mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-24 03:16:43 +00:00
504d348917c2fb8ed2c139bbbc352e07fccae19a
21 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7cda0d7896 |
feat: rebrand to Perpetual Software + new tagline (IDEA-832) (#273)
Migrates from xarmian/pad to PerpetualSoftware/pad across the entire
repo and updates the product subtitle to "Collaborate with your AI
agents".
Go module rename
- go.mod: github.com/xarmian/pad → github.com/PerpetualSoftware/pad
- All Go imports updated across cmd/pad, internal/{cli,server,store,
models,collections,items,events,metrics,webhooks} (~130 files)
- Test fixtures with the literal repo slug ("xarmian/pad" in JSON
shapes, SSH/HTTPS git URL strings, workspace_context fixtures)
also updated, including the secondary repo entry
(xarmian/pad-web → PerpetualSoftware/pad-web — pad-web was also
moved to the org per branch context)
Docs / config
- README badges, install instructions, brew tap, Docker image, source
build path, sponsor link (sponsor link kept as personal @xarmian)
- Subtitle: "Project management for developers and AI agents." →
"Collaborate with your AI agents." (README, manifests, web layout
meta, .goreleaser homebrew description)
- CONTRIBUTING.md, SECURITY.md, skills/INSTALL.md
- .goreleaser.yaml: homebrew_casks owner, GHCR image, release github
owner, cosign cert-identity regex, comments
- .github/workflows/release.yml: tap/release comments
- deploy/k8s/deployment.yaml: container image
- docs/deployment.md: clone URL
- web/static/{site.webmanifest,manifest.json}: description
- web/src/routes/+layout.svelte: meta description + og:description
Brew tap path is PerpetualSoftware/tap/pad (CamelCase, matches
GitHub user case). GHCR image is ghcr.io/perpetualsoftware/pad
(lowercased per GHCR's URL normalization). CODEOWNERS @xarmian and
FUNDING.yml github: xarmian intentionally retained — those are the
personal maintainer / sponsor account, separate from the org repo.
Verification: go build ./..., go test ./... (all pkgs pass), web
build, and make install all clean (TASK-844, TASK-845).
|
||
|
|
157ca4e88f |
chore: bump Go toolchain to 1.26 (TASK-763) (#247)
* chore: bump Go toolchain to 1.26 (TASK-763) Bump Go from 1.25 to 1.26 across all toolchain pins: - go.mod — go 1.25.0 → go 1.26.0 - Dockerfile — golang:1.25-alpine → golang:1.26-alpine - .github/workflows/ci.yml — three setup-go steps (Go, Go-Postgres, E2E jobs) - .github/workflows/release.yml — release pipeline No `toolchain` directive: the repo is pre-launch with no external contributors yet, so we set the floor where we want it (hard requirement). Verified locally before commit: - golangci-lint v2.11.4 builds and runs under Go 1.26.2 (pinned in CI) - golang:1.26-alpine and 1.26.2-alpine images present on Docker Hub - go build ./... clean - go vet ./... clean - go test ./... all pass Parent: PLAN-644 (OSS Repo Hygiene and Launch Polish). * chore: gofmt -w under Go 1.26 (TASK-763) Apply Go 1.26's gofmt to the codebase. ~41 files reformatted, all struct-tag whitespace realignment — no semantic changes. Verified: - gofmt -l ./cmd ./internal returns empty after - go build ./... still clean - go test ./... still passes (run before commit) Bundling the gofmt diff with the toolchain bump in the same PR because the formatting drift is a direct consequence of moving from 1.25 to 1.26; splitting them creates a mandatory two-PR ordering for no value. Parent: PLAN-644. * docs: bump documented Go floor to 1.26 (TASK-763) Match go.mod's hard 1.26.0 requirement in the source-build instructions. Caught by Codex review round 1 on PR #247. - README.md:158 — "Go 1.25+" → "Go 1.26+" - CONTRIBUTING.md:9 — "Go 1.25+" → "Go 1.26+" |
||
|
|
e0f3583333 |
feat(cli): categorized template picker + interactive select (TASK-616) (#148)
* feat(cli): categorized template picker + interactive select (TASK-616)
Turns the CLI template picker from a flat alphabetical dump into a
category-aware flow that reflects the Software / People / Research /
Content / Operations / Personal taxonomy established by PLAN-609.
Library
-------
- collections.GroupTemplatesByCategory returns visible templates
bucketed into CategoryOrder with a trailing slot for any
custom-category templates — one canonical grouping that both CLI
and the upcoming web picker (TASK-617) can consume.
- collections.CategoryLabel turns category slugs into display labels
("software" → "Software") with passthrough for unknown values.
- collections.CategoryOrder exposes the canonical display order.
CLI
---
- New cmd/pad/templates_picker.go defines:
- printGroupedTemplates: writes the grouped listing with icons,
aligned columns, and a dim "(default)" marker on startup.
- pickTemplateInteractive: prompts when the user hasn't passed
--template and is on a TTY. Accepts a number OR a template name;
enter selects the default (startup); invalid input re-prompts.
- canPromptForTemplate: TTY detection so scripts never block.
- pad workspace init --list-templates now uses the grouped printer.
- pad workspace init / pad init error messages for unknown templates
show the grouped list instead of a flat dump.
- pad init now triggers pickTemplateInteractive when no --template
flag is set AND stdin/stdout are TTYs. Non-TTY invocations fall
back to the "startup" default unchanged.
- --template flag help no longer hardcodes "startup, scrum, product"
since the list now grows with non-software templates.
Tests
-----
- Library: TestGroupTemplatesByCategory (canonical order, no hidden,
every visible template assigned), TestCategoryLabel.
- CLI: TestPickTemplateInteractiveDefault / ByName / ByNumber /
RetriesOnInvalid, TestPrintGroupedTemplatesIncludesEveryVisibleTemplate
(smoke: every visible template renders, demo hidden, category
headers present).
Parent: PLAN-609.
* fix(cli): propagate non-EOF prompt read errors in template picker
Per Codex review on PR #148. pickTemplateInteractive previously
mapped any ReadString error to silently selecting the default
template. A detached PTY returning EIO or a similar read failure
would quietly create a workspace with the startup template even
though the user never made a valid choice. Restrict the silent
fallback to io.EOF (which is benign for pipes, tests, closed
stdin) and bubble up any other error so the command aborts.
* test(cli): cover non-EOF read error path in template picker
Adds TestPickTemplateInteractiveSurfacesNonEOFReadErrors to verify
the behavior change from the previous commit — a non-EOF read
failure propagates up instead of silently selecting the default
template.
|
||
|
|
fed365d914 |
feat(templates): ship interviewing template (TASK-615) (#147)
* feat(templates): ship interviewing template (TASK-615) Candidate-side companion to the hiring (company-side) template. Same People category, near-zero collection overlap — a real proof that one category can hold two templates with barely-related schemas. Collections ----------- - Applications (APP) — roles being tracked, stages from researching → applied → screen → interviewing → offer → accepted / rejected / withdrawn - Interviews (INT) — individual rounds, child of an Application, with round type, format, date, prep_status (including completed) - Companies (CO) — standalone research notes on companies, referenced from Applications via wiki-link so notes are persistent even across multiple Applications at the same company - Contacts (CON) — referrals, recruiters, interviewers — tracked independently for followup hygiene - Docs, Conventions, Playbooks Trigger vocabularies -------------------- - InterviewingConventionTriggers: always, on-application-submitted, on-interview-scheduled, on-interview-completed, on-stage-change, on-offer-received, on-rejection, weekly-review - InterviewingPlaybookTriggers: on-application-submitted, on-interview-scheduled, on-interview-completed, on-stage-change, weekly-review, manual - Scopes: all, research, applications, interviews, followups Starter pack ------------ - Conventions (3): 48h prep notes (should/on-interview-scheduled), end-of-application retros (should/on-stage-change), 24h thank-yous (should/on-interview-completed) - Playbooks (3): Log an Interview, Weekly Job Search Review, Interviewing Workspace Onboarding - Seed items (2): one example Application, one example Company Tests ----- - TestInterviewingTemplate — collections present, interviewing triggers present and distinct from both software AND hiring triggers - TestSeedCollectionsFromTemplateInterviewing — end-to-end: seven collections created, starter pack populated, prefixes correct Parent: PLAN-609. * fix(templates): mark interviewing Companies.closed as terminal Per Codex review on PR #147. The Companies status field had a 'closed' option but no TerminalOptions declared, so done-state resolution fell back to the global default set which doesn't include 'closed' — closed companies were being counted as active in dashboard and progress views. Adding TerminalOptions fixes lifecycle metrics without changing user-facing options. * fix(templates): Log-an-Interview playbook uses only interviewing collections Per Codex review iteration 2 on PR #147. The playbook step referenced 'Create a follow-up Task' but the interviewing template doesn't ship a tasks collection. Rewrote the step to use the collections that actually exist — log the thank-you as a comment on the Interview and update the matching Contact's last_contact. Keeps the starter playbook consistent with the schema it ships alongside. |
||
|
|
b891ba84a4 |
feat(templates): ship hiring template (TASK-614) (#146)
* feat(templates): ship hiring template (TASK-614) First non-software template under PLAN-609. Proves the machinery built by TASK-610 through TASK-613 end-to-end: category grouping, per-template trigger vocabularies, template-owned starter packs, domain-specific seed items. Collections ----------- - Requisitions (REQ) — open roles, with status/team/level/location - Candidates (CAND) — applicants, parent-linked to a Requisition - Interview Loops (LOOP) — interview rounds, parent-linked to a Candidate - Feedback (FB) — per-interviewer debriefs, parent-linked to a Loop - Docs — rubrics, process notes - Conventions + Playbooks — using hiring trigger vocabulary Trigger vocabularies -------------------- - HiringConventionTriggers: always, on-candidate-advance, on-loop-scheduled, on-feedback-submitted, on-offer-extended, on-close-requisition - HiringPlaybookTriggers: on-candidate-advance, on-interview-scheduled, on-feedback-submitted, on-close-requisition, manual - HiringConventionScopes / HiringPlaybookScopes: all, sourcing, screening, interviewing, offers Starter pack ------------ - Conventions (3): PII handling (must/always), requisition linking (should/always), 24h debriefs (should/on-feedback-submitted) - Playbooks (2): "Advance a Candidate" (on-candidate-advance), "Hiring Workspace Onboarding" (manual) - Seed items: one example Requisition, one example Candidate (both labeled as seeded so users can delete or overwrite) Tests ----- - TestHiringTemplate — collections present, trigger vocabulary uses hiring values and does not leak software triggers (on-commit etc.) - TestSeedCollectionsFromTemplateHiring — end-to-end: seeding creates all seven collections plus populates the starter pack Parent: PLAN-609. * fix(web): display hiring triggers on conventions + playbooks pages Per Codex review P1 on PR #146. The conventions page hardcoded a software-only TRIGGERS list in its grouping loop, silently hiding any convention whose trigger wasn't in that list — so a hiring workspace's seeded conventions (on-candidate-advance etc.) never appeared in the primary management UI. Same issue on the playbooks page's filter dropdown. - conventions page: grouping now iterates the union of the hardcoded TRIGGERS (in original order) plus any triggers discovered in the data (sorted alphabetically). Unknown triggers fall back to a generic bell icon + the raw trigger string via a triggerMeta helper. byTrigger is now SvelteMap<string, Item[]>; the narrow Trigger type still gates the create form. - playbooks page: the filter dropdowns for trigger and scope now expose the union of the hardcoded list and any distinct values found on loaded playbooks. Create form still uses the narrow list. The broader "derive options from collection schema so the CREATE forms also follow the workspace's trigger vocabulary" is tracked as IDEA-619 for a follow-up PR. * fix(templates): ship explicit prefixes for hiring collections Per Codex P2 on PR #146. The seeded candidate's content referenced --parent REQ-1, but the default DerivePrefix turns "Requisitions" into "REQUI" (strips trailing S, caps at 5), so the example wouldn't resolve. Similar problems for Candidates (CANDI) and Feedback (FEEDB). - Add optional Prefix string field to DefaultCollection so templates can override the derived prefix. Empty (the default) preserves today's auto-derivation for every existing template. - Thread DefaultCollection.Prefix through SeedCollectionsFromTemplate to the CollectionCreate call — the CreateCollection API already supported a Prefix field. - Hiring template sets explicit prefixes: Requisitions → REQ, Candidates → CAND, Interview Loops → LOOP, Feedback → FB. The seeded onboarding text's --parent REQ-1 reference now resolves. - Test asserts the expected prefixes land on the created collections. * fix: add 'offers' to hiring playbook scopes + tolerate custom scopes in web UI Per Codex review iteration 3 on PR #146. - HiringPlaybookScopes now includes 'offers', matching HiringConventionScopes. The hiring pipeline has a distinct offer stage and playbook workflows tied to offer management were previously uncovered in the schema. - web/conventions page: the scope filter dropdown now exposes the union of SURFACES (original order) plus any scopes discovered on loaded conventions, via a new allSurfaces $derived. Same pattern as the earlier triggers fix. Non-software scopes (sourcing, screening, interviewing, offers) now show up for hiring workspaces instead of being hidden by the narrow hardcoded list. The conventions create form still uses the hardcoded SURFACES — that broader "derive from collection schema" fix is tracked as IDEA-619. * fix(templates): hiring Feedback BoardGroupBy uses submitted, not recommendation Per Codex review iteration 4 on PR #146. Feedback items have two select fields: recommendation (strong-hire/hire/mixed/no-hire/ strong-no, no terminal values) and submitted (pending/submitted, terminal=submitted). The done-state pipeline prefers settings.board_group_by when it's a select field, so grouping on recommendation made terminal detection fall back to checking recommendation against default done-statuses — never matching, leaving submitted feedback perpetually 'active' in active-count views. Group on submitted so completion actually registers. * fix(templates): Advance-a-Candidate playbook uses valid Feedback fields Per Codex review iteration 5 on PR #146. The seeded playbook told agents to create Feedback items with recommendation=pending, but recommendation's allowed values are only the concrete verdicts (strong-hire, hire, mixed, no-hire, strong-no) — 'pending' would be rejected at field validation. Updated the step to use submitted=pending (which IS in the allowed options) and call out that recommendation should stay blank until the interviewer actually records a verdict. |
||
|
|
115b33849e |
feat(templates): software starter pack + idempotent seeding (TASK-612) (#144)
* feat(templates): software starter pack + idempotent seeding (TASK-612) Ship the software templates (startup, scrum, product) with a curated starter pack of conventions + playbooks so new workspaces feel "batteries included" rather than empty shells. The pack is a safe, small subset drawn from the existing convention/playbook library — the library itself remains the full catalog for interactive onboarding. Starter pack contents --------------------- Conventions (4): - Conventional commit format (on-commit, should) - Never push directly to main (on-commit, must) - Run tests before completing tasks (on-task-complete, must) - Review your own changes before PR (on-pr-create, should) Playbooks (2): - Implementation Workflow (on-implement) - Code Review Process (on-review) The pack is materialized by looking up library items by title and converting them to SeedConvention / SeedPlaybook via json.Marshal of the expected field shape. When the library's wording changes, the template's seed content changes automatically. Store-side changes ------------------ SeedCollectionsFromTemplate is now idempotent with respect to seed items: items are only created in collections that were freshly created during the current call (tracked via a freshlyCreated set). That's the invariant that lets the server's startup auto-upgrade safely re-run on every boot without duplicating items across every workspace in the DB. Empty template name preserves the old behavior (default collections, no starter pack) — this keeps backward compatibility for callers that don't pass a template, including the server-startup auto-upgrade path and all existing server tests. Explicit "startup" / "scrum" / "product" now gets the starter pack. Tests ----- - TestSoftwareStarterPacksPopulated — guards against library-title drift - TestSoftwareTemplatesShipStarterPacks — each software template ships a pack - TestSeedCollectionsFromTemplateSeedsStarterPack — end-to-end seeding works - TestSeedCollectionsFromTemplateIdempotentWithSeedItems — re-seed doesn't duplicate Parent: PLAN-609. * fix(cli): default pad init to startup template when --template is omitted Per Codex review on PR #144. Without this, `pad workspace init` without `--template` no longer seeded the starter pack, even though startup is documented as the default. The fix lives in ensureWorkspace (shared by both init.go and the workspace creation command in main.go) — empty flag is rewritten to "startup" there. Tests and other direct API callers that want an empty workspace still pass Template="" through. * fix(cloud): auto-create workspace passes startup template for starter pack Per Codex review iteration 2 on PR #144. The auto-create cloud-signup flow calls SeedCollectionsFromTemplate with an empty template, which after this PR's semantics meant new cloud workspaces got no starter conventions/playbooks. Pass "startup" explicitly to match the CLI init behavior. * fix(store): propagate collection lookup errors during seeding Per Codex review iteration 3 on PR #144. seedItem previously treated any error from GetCollectionBySlug as a silent no-op, which hid real DB lookup failures — a transient error during workspace creation would make seeding appear successful while conventions/playbooks were in fact missing. Now we distinguish the two cases: - err != nil → propagate so callers can detect partial init - coll == nil → benign (template references a slug not in its collections list; template-author bug, no-op) * fix(store): idempotent seeding by item title (partial-init recovery) Per Codex review iteration 4 on PR #144. The previous design gated item seeding on collections being freshly-created-in-this-call, which trapped partially-initialized workspaces: if a DB error fired between collection creation and item seeding, a retry would see the collections already existed and skip every remaining seed item. Switch to title-based idempotency. Before inserting a seed item we list the target collection's existing items (once per collection, via a small cache) and skip any whose title already exists. That makes seeding: - Idempotent: re-running a template doesn't duplicate items - Recoverable: retrying fills in missing items after partial init - Retry-safe: the auto-upgrade path can re-run safely on every boot New test TestSeedCollectionsFromTemplateRecoversPartialInit exercises the recovery path explicitly. |
||
|
|
d1c7ede735 |
feat(templates): parameterize conventions & playbooks schemas (TASK-611) (#143)
The Conventions and Playbooks collections currently hardcode their trigger and scope select options to a software-centric vocabulary (on-commit, on-pr-create, backend, frontend, ...). That makes it impossible for a non-software template to ship domain-specific triggers like on-candidate-advance or on-interview-scheduled, which is a hard blocker for PLAN-609. - Change conventionsCollection() and playbooksCollection() to accept trigger + scope option lists from the caller. - Export SoftwareConventionTriggers, SoftwareConventionScopes, SoftwarePlaybookTriggers, SoftwarePlaybookScopes as the canonical software-domain defaults. Non-software templates pass their own lists. - Defensively copy the caller's slices inside the helpers so a template package author cannot accidentally mutate a shared option list. - Update scrum + product templates to pass the software defaults. - Refactor Defaults() in defaults.go to use the same parameterized helpers — eliminates ~85 lines of duplicated schema definition that had drifted out of sync with the templates.go copy. - Add tests covering: caller option propagation, defensive copying, and software-template invariants. Parent: PLAN-609. |
||
|
|
73a6e1f3a9 |
feat(templates): categorize WorkspaceTemplate + hide demo (TASK-610) (#142)
Refactor the WorkspaceTemplate struct to carry the metadata and domain- specific seed packs needed for the upcoming non-software templates. - Add Category, Icon, Hidden, Conventions, Playbooks fields to the WorkspaceTemplate struct. Existing fields (Name, Description, Collections, SeedItems) unchanged. - Define SeedConvention and SeedPlaybook types so templates can carry domain-specific rules and workflows (populated in a follow-up task). - Introduce category constants (software, people, research, content, operations, personal). - Assign Category=software and Icon to startup (🚀), scrum (🏃), product (📦). Mark demo (🎬) as Hidden so it no longer appears in the picker while remaining buildable by explicit --template demo. - Split ListTemplates() into a filtered picker view and a new ListAllTemplates() for internal tooling. - Expose category and icon on the /workspaces/templates API response so the web picker can group by category in a follow-up task. - Add package tests for hidden-filtering and picker metadata invariants (the package previously had no tests). Parent: PLAN-609. |
||
|
|
973887d5dd |
fix: comprehensive collection visibility enforcement
Close all identified bypass paths in the collection visibility system: HIGH: - Add requireItemVisible check to all 15+ item-by-slug handlers (get, update, delete, restore, move, children, progress, activity, versions, timeline, comments, links) - Filter incremental sync (GET /changes) by visible collections with proper error handling for deleted item lookups - Fix search to fail closed on visibility errors instead of removing the collection filter; apply per-workspace filtering in multi-workspace search path - Empty CollectionIDs (non-nil but len 0) now returns zero results in ListItems and Search instead of skipping the filter - Filter returned item links by linked item visibility; require target item visibility before creating links - Block moving items into hidden collections - Add visibility checks to comment-by-ID routes (delete, reply, add/remove reaction) MEDIUM: - SSE events for replies and reactions now include collection slug so visibility filtering can scope them; fail closed on visibility error - Parent/plan resolution in create/update checks resolved parent is in a visible collection - Progress endpoints compute from visible children only when user has restricted access - Role board reorder checks item visibility before allowing sort changes - Parent enrichment accepts optional visibility filter to hide parents from hidden collections - Add IsSystem: true to Conventions and Playbooks in defaults.go LOW: - Child listing handles visibility lookup errors instead of failing open - GetDeletedItemsWithCollection returns proper errors instead of swallowing them - SetMemberCollectionAccess wrapped in transaction with workspace validation for collection IDs |
||
|
|
d74431fbb3 |
feat: collection-level visibility + system collections
Add per-member collection visibility controls and mark conventions/
playbooks as system collections (TASK-413 + TASK-415).
Data model:
- workspace_members: new collection_access column ('all' or 'specific')
- New member_collection_access table (workspace_id, user_id, collection_id)
- collections: new is_system column, set for conventions and playbooks
Store methods:
- VisibleCollectionIDs(workspaceID, userID) — returns nil for "all"
access, or specific IDs (including system collections) for "specific"
- SetMemberCollectionAccess/GetMemberCollectionAccess for CRUD
- All collection queries include is_system in SELECT/scan
- Export/import handles is_system field
Default collection definitions:
- conventionsCollection and playbooksCollection set IsSystem=true
- New workspaces get system flag on seed
D7: default collection_access is "all" — absence of restrictions
means full access. System collections always visible to members.
|
||
|
|
bde15d45ca |
Rename Phases to Plans, clean up deprecated aliases (#71)
* Rename "Phases" to "Plans" and clean up deprecated phase aliases
Renames the default "Phases" collection to "Plans" across the full stack:
- DB migration renames existing collections in-place (name, slug, prefix PLAN, icon 🗺️)
- Removes all deprecated Phase* backward-compat aliases from models and store
- Removes --phase CLI flag (use --parent instead)
- Updates convention triggers: on-phase-start/complete → on-plan-start/complete
- Updates dashboard API: active_phases → active_plans, /phases-progress → /plans-progress
- Updates all frontend components, types, and documentation
Closes IDEA-124
* Fix CSRF cookie not being cleared on logout
The SessionAuth middleware was re-issuing a CSRF cookie before the
logout handler could clear it, resulting in two Set-Cookie headers.
Skip CSRF re-issue for /api/v1/auth/ paths since auth endpoints
manage their own CSRF cookies (login sets, logout clears).
* Fix migration issues found in Codex review
- P1: Move doc_type UPDATE from migration 024 into 025, which recreates
the table with the new CHECK constraint first (SQLite enforces CHECK
on UPDATE, so the old constraint would reject 'plan')
- P1: Add PostgreSQL migration 005 for the collection rename (phases →
plans) — previously only existed on the SQLite path
- P2: Recreate FTS triggers, indexes, and rebuild FTS after the table
swap in migration 025 (DROP TABLE drops associated objects in SQLite)
* Fix parent filter field name and sync .agents skill copy
Codex review round 2 findings:
- P1: Parent filter compared against `parent_id` (wrong) instead of
`parent_link_id` — plan filtering in collection view was broken
- P1: .agents/skills/pad/SKILL.md still had old --phase flags and
"Phases" references — synced from the updated .claude copy
- P2: Accept legacy 'phase' filter key for backward compat with
existing saved views that serialized the old key name
* Fix PG migration JSONB casting and add slug collision guards
Codex PR review bot findings:
- P1: PostgreSQL REPLACE/LIKE don't work on JSONB columns — cast
schema::text and fields::text before string ops, then back to ::jsonb
- P1: If a workspace already has a custom 'plans' collection, the
rename hits UNIQUE(workspace_id, slug) — added NOT EXISTS guard
to both SQLite and PostgreSQL migrations
|
||
|
|
367116b3a0 |
Unify relation fields and item links into single dependency system (#66)
* feat: unify relation fields and item links into single dependency system
Phase membership (Task→Phase) was previously stored as a UUID in the
item's fields JSON, separate from the item_links table used for
blocks/related/implements relationships. This unifies both into the
item_links table so all item relationships use one system.
Backend:
- Add 'phase' link type to item_links constants
- Migration 021: migrate existing phase field values to item_links,
strip phase from fields JSON, remove phase field from tasks schema
- Rewrite GetPhaseProgress, GetAllPhasesProgress, GetTasksForPhase
to JOIN on item_links instead of json_extract(fields, '$.phase')
- Add SetPhaseLink, ClearPhaseLink, GetPhaseForItem, GetTaskPhaseMap
store helpers with single-phase constraint enforcement
- Create/update handlers intercept 'phase' in fields and route through
links system; enrich item responses with phase_id/ref/title
- Dashboard orphan detection uses batch GetTaskPhaseMap lookup
- Add PhaseID filter to ItemListParams for link-based list filtering
Frontend:
- Remove relation field type from FieldEditor (no longer needed)
- Add link CRUD UI to item detail page: "Add relationship" inline form
with link type picker + item search, delete buttons on existing links
- Phase links appear in Relationships section as "In phase"/"Phase"
- ItemCard reads phase from item.phase_title instead of fields.phase
- FilterBar phase filter uses item.phase_id for client-side filtering
- Add api.links.delete to frontend API client
- Fix duplicate {#each} key on dashboard attention list
Implements IDEA-106.
* fix: remove relationLabels prop from BoardView, ListView, TableView
ItemCard no longer accepts relationLabels (phase info now comes from
item.phase_title), so remove the prop from all parent view components
that were passing it through. Also remove unused .cell-relation CSS.
* fix: address PR review — atomic SetPhaseLink, migration safety, error handling
1. Migration 021: remove deleted_at filters so archived tasks and tasks
pointing to archived phases also get their phase links migrated.
2. SetPhaseLink: wrap delete+insert in a transaction so a failed insert
doesn't leave the item with no phase link (previously non-atomic).
3. Create/update handlers: return proper HTTP errors when phase link
operations fail instead of logging warnings and returning 200 OK.
|
||
|
|
e21da3c6a4 |
fix: schema-driven terminal statuses replace hardcoded lists (BUG-17) (#65)
Add `terminal_options` to collection field schemas so each collection declares which statuses are terminal/finalized. Replaces 10+ inconsistent hardcoded status lists across backend, CLI, and frontend. - Add TerminalOptions to FieldDef and centralized helpers in models/terminal.go - Populate terminal_options on all default and template collections - Replace hardcoded isDoneStatus/isTerminalItemStatus with schema-aware lookups - Fix phase progress to count all terminal statuses (not just "done") - Add terminal status toggle UI in collection field editor (Settings → Fields) - Redesign collection field editor for cleaner layout and alignment - Move Platform settings tab before Danger Zone tab |
||
|
|
9fedbe4ad6 |
feat: skill role awareness + role-specific conventions (#59)
* feat: skill role awareness + role-specific conventions (#PHASE-10) Make the /pad skill role-aware so agents know what role they're acting as and load conventions scoped to that role. Role context lives in the conversation — no server state, no files, no new CLI commands. Skill changes: - Ask for role on first invocation when roles exist - Parse "as <role>" inline: /pad as implementer, /pad what's next as reviewer - Auto-filter work queue by active (user, role) pair - Role-aware greeting: "Working as 🔨 Implementer. Your queue: ..." - Load role-specific + global conventions before performing work - Support mid-session role switching - Updated CLI reference: --role/--assign flags, pad role commands, --comment best practice Convention schema: - Migration 018: add optional `role` field to Conventions collection - Conventions with a role value apply only to that role - Conventions without a role apply to all (backward compatible) - Updated defaults.go with role field * fix: use json_insert for conventions role field migration Replace fragile REPLACE() on exact JSON string literal with SQLite's json_insert(schema, '$.fields[#]', ...) which appends the role field regardless of field order or custom fields in the schema. Adds a NOT EXISTS guard via json_each() to skip if role already present. Also adds role field to shared conventions template for non-default workspace templates, and regression tests for schema seeding. Addresses Codex review comment on PR #59. |
||
|
|
be576d9e24 |
feat: agent roles — role-based (user, role) assignment for items (#58)
* feat: agent roles — role-based (user, role) assignment for items (#PHASE-9) Introduce agent roles as a first-class concept for human-agent work assignment. Roles describe capability specializations (Planner, Implementer, Reviewer, etc.) and items can be assigned to a (user, role) pair, enabling natural handoff workflows between different AI tools. Migration: - New `agent_roles` table (workspace-scoped, slug-unique) - `assigned_user_id` + `agent_role_id` columns on `items` with FKs - Removed legacy `assignee` text field from Tasks schema Backend: - AgentRole model + full CRUD store/API - All item queries updated with LEFT JOINs to resolve assignment - Item list filtering by assigned_user_id and agent_role_id - Role transitions tracked in activity feed metadata CLI: - `pad role list/create/delete` commands - `--role` and `--assign` flags on item create/update/list - Assignment displayed in `pad item show` output Web: - TypeScript types + API client for agent roles - Role badge on item cards in list/board views - Assignment display on item detail page * fix: enforce workspace-scoped assignments and fail fast on unresolved --assign filter Addresses code review feedback from PR #58: P1: Add validateAssignmentScope() to the store layer, called by both CreateItem and UpdateItem. Verifies that assigned_user_id belongs to the workspace (via IsWorkspaceMember) and agent_role_id exists in the workspace (via GetAgentRole) before writing. Prevents cross-workspace assignment leaks. P2: The CLI `pad item list --assign <name>` now errors instead of silently returning unfiltered results when the member lookup fails or no workspace member matches the provided name. |
||
|
|
35f3dd1da4 |
feat(conventions): add structured metadata for TASK-133 (#51)
* feat(conventions): add structured metadata for TASK-133 * fix(web): add workspace update type for CI |
||
|
|
f5649b912e | refactor(cli): group first-release commands for TASK-127 (#45) | ||
|
|
889c6d3a56 |
Enhance demo template with playbook, extra convention, and multi-agent refs
The demo workspace now showcases conventions AND playbooks (both key differentiators), references multi-agent support in the architecture doc, and has a better description. This is what people see when they run pad init --template demo to try Pad for the first time. |
||
|
|
a6d08fdd7e |
Add Quick Actions — contextual prompt buttons that copy agent commands to clipboard
New QuickAction type in collection settings lets users define prompt templates
with variables ({ref}, {title}, {status}, etc.) that resolve at click time.
Lightning bolt menu appears on item detail and collection pages. Includes
default actions for Tasks, Ideas, Phases, and Docs collections. Fully
customizable via new Quick Actions tab in EditCollectionModal.
|
||
|
|
b3909b13ee |
Add 'implemented' status option to Ideas collection schema
Adds migration for existing workspaces and updates the default definition for new workspaces. |
||
|
|
81579847c6 |
Initial release
Pad — project management for developers and AI agents. Single Go binary with embedded SvelteKit web UI, SQLite storage, CLI, and Claude Code /pad skill integration. https://getpad.dev |