diff --git a/.planning/phases/32-setup-sharing-system/32-CONTEXT.md b/.planning/phases/32-setup-sharing-system/32-CONTEXT.md new file mode 100644 index 0000000..bad233e --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-CONTEXT.md @@ -0,0 +1,120 @@ +# Phase 32: Setup Sharing System - Context + +**Gathered:** 2026-04-13 +**Status:** Ready for planning + + +## Phase Boundary + +Setup owners can toggle visibility between private, link-shared, and public. Share links use secret tokens with configurable expiration and revocation. Schema includes full future-proofing for person-specific shares, write permissions, and collaborative editing — but only read-only link sharing is enforced in this phase. + + + + +## Implementation Decisions + +### Visibility Model +- **D-01:** Three visibility levels: `private` (owner only), `link` (accessible via share token, not discoverable), `public` (discoverable on feed/profiles) +- **D-02:** Replace `isPublic: boolean` column on `setups` table with `visibility: text` column (`private`/`link`/`public`). Column on table for query speed — discovery feed queries `WHERE visibility = 'public'` +- **D-03:** Setting visibility to `private` deactivates (does not delete) all share links. Switching back to `link` reactivates them +- **D-04:** Share links use secret tokens — the setup's numeric ID alone is not sufficient for link-shared access + +### Share Links +- **D-05:** Multiple share links can coexist per setup, each independent with its own token, expiration, and revocation status +- **D-06:** Share URLs: `/s/{token}` (short URL for sharing) AND `/setups/:id?share={token}` (both work, short URL is primary for sharing) +- **D-07:** Default link expiration: 14 days. Options when creating: 7 days, 14 days, 30 days, infinite +- **D-08:** Each link can be individually revoked without affecting other links +- **D-09:** Only read-only link shares are functional in this phase. Write permission exists in schema but is not enforced + +### Schema +- **D-10:** Full `shares` table created now: id, setupId, token, permission (read/write), expiresAt (nullable = infinite), userId (nullable — null = link share, set = person-specific share), createdAt, revokedAt (nullable) +- **D-11:** Person-specific shares (userId column) exist in schema but are not used in this phase +- **D-12:** Write permission column exists but write-access is not enforced — no mutation permission checks + +### Share UX +- **D-13:** Share modal (Google Docs style) is the single UI for managing visibility AND share links +- **D-14:** Share icon button replaces the current globe public/private toggle on setup detail page. Icon reflects current visibility state via color/icon variation +- **D-15:** Modal contains: visibility picker (private/link/public), create share link with expiration picker, active share links list with copy/revoke actions +- **D-16:** Desktop and mobile use the same share icon button opening the same modal + +### Public Setup Presentation +- **D-17:** Link-shared setup viewer UX: Claude's discretion — will pick based on existing setup detail page patterns (subtle shared context vs. identical to public view) +- **D-18:** No changes to discovery feed or profile page visuals in this phase +- **D-19:** Discovery feed query updated from `isPublic = true` to `visibility = 'public'` — same behavior, new column + +### Claude's Discretion +- Viewer experience for link-shared setups (shared banner/badge vs. clean view) — pick what fits the existing design patterns + +### Folded Todos +None — no relevant todos matched this phase's scope. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +No external specs — requirements fully captured in decisions above. + +### Existing Implementation +- `src/db/schema.ts` — Current `setups` table with `isPublic` boolean (line 118-127) +- `src/server/services/setup.service.ts` — Setup CRUD with `isPublic` handling +- `src/server/services/discovery.service.ts` — Discovery feed query using `isPublic = true` +- `src/server/services/profile.service.ts` — `getPublicSetupWithItems()` for public viewing +- `src/client/routes/setups/$setupId.tsx` — Setup detail page with current globe toggle (lines 177-203) +- `src/client/hooks/useSetups.ts` — `usePublicSetup` hook (line 67) +- `src/shared/schemas.ts` — Zod schemas with `isPublic` field (lines 88, 93) + + + + +## Existing Code Insights + +### Reusable Assets +- Setup detail page (`setups/$setupId.tsx`): Has desktop + mobile button patterns for the share button replacement +- `useSetups` hooks: Mutation patterns for `updateSetup` — extend for visibility changes +- `LucideIcon` component: Icons like `share-2`, `link`, `globe`, `lock` available for visibility states +- Modal patterns: Used elsewhere in the app (thread creation, item add) — reuse for share modal + +### Established Patterns +- Service layer with DI (`db` as first param) for testability +- Zod validation on route handlers via `@hono/zod-validator` +- React Query mutations with cache invalidation +- Detail pages (not panels) for complex interactions + +### Integration Points +- `src/db/schema.ts`: New `shares` table + modify `setups` table (isPublic → visibility) +- `src/server/routes/setups.ts`: New share link CRUD endpoints +- `src/server/routes/`: New `/s/:token` route for short share URLs +- `src/server/services/setup.service.ts`: Update queries from isPublic to visibility +- `src/server/services/discovery.service.ts`: Update feed query +- `src/server/services/profile.service.ts`: Update public setup query +- `src/client/routes/setups/$setupId.tsx`: Replace globe toggle with share button + modal +- `src/shared/schemas.ts`: New share schemas, update setup schemas + + + + +## Specific Ideas + +- Share modal inspired by Google Docs share dialog — visibility picker at top, share links list below +- When visibility is set to `private`, share links become inactive but aren't deleted — switching back to `link` reactivates them +- Multiple shares coexist: e.g., one permanent read link + one 14-day read link simultaneously +- Future: person-specific shares should influence discovery algorithm (deferred) + + + + +## Deferred Ideas + +- **Person-specific shares influencing discovery feed algorithm** — when direct shares exist between users, factor that into feed ranking. Belongs in a future personalization/social phase. +- **Write-access share enforcement** — collaborative editing requires conflict resolution, real-time sync, and mutation permission checks. Belongs in a dedicated collaborative editing phase. +- **Person-specific share UI** — inviting specific users by username/email to a setup. Needs user search/lookup. Future phase. + + + +--- + +*Phase: 32-setup-sharing-system* +*Context gathered: 2026-04-13* diff --git a/.planning/phases/32-setup-sharing-system/32-DISCUSSION-LOG.md b/.planning/phases/32-setup-sharing-system/32-DISCUSSION-LOG.md new file mode 100644 index 0000000..bfb4f34 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-DISCUSSION-LOG.md @@ -0,0 +1,141 @@ +# Phase 32: Setup Sharing System - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-04-13 +**Phase:** 32-Setup Sharing System +**Areas discussed:** Visibility model, Share UX & controls, Schema future-proofing, Public setup presentation + +--- + +## Visibility Model + +| Option | Description | Selected | +|--------|-------------|----------| +| Unlisted link (no token) | Setup ID in URL is the 'key'. Simple but IDs are guessable. | | +| Secret token link | URL contains random token. More secure, requires generation/storage. | ✓ | +| Two levels only (private/public) | Keep current boolean. Skip link-sharing. | Rejected by user upfront | + +**User's choice:** Secret token link +**Notes:** User explicitly stated "ditching the link share ain't it" — three levels are required. + +### Follow-up: Share URL format + +| Option | Description | Selected | +|--------|-------------|----------| +| /setups/42?share=token | Query param on existing route | | +| /s/token (short URL) | Dedicated short route, cleaner for sharing | ✓ (both) | + +**User's choice:** Both should work, but `/s/token` is primary for sharing because it's shorter. + +### Follow-up: Token revocation + +| Option | Description | Selected | +|--------|-------------|----------| +| Regenerate button (single token) | One token per setup, regenerate invalidates old | | +| Full shares list with management | Multiple shares per setup, each with permission/expiration/revocation | ✓ | + +**User's choice:** Full shares management. Multiple coexisting shares with different permissions (read/write), expirations (default 14 days, settable, or infinite), individually revocable. Vision includes person-specific shares with write access. + +### Follow-up: Scope check + +| Option | Description | Selected | +|--------|-------------|----------| +| Read shares now, write schema only | Implement read link shares. Schema includes write/person columns unused. | ✓ | +| Full system now | Implement everything including write shares and person-specific shares. | | +| Minimal + schema | Single share link only. Full schema but minimal UI. | | + +**User's choice:** Read shares now, write permission schema only. + +--- + +## Share UX & Controls + +### Visibility control UI + +| Option | Description | Selected | +|--------|-------------|----------| +| Dropdown selector | Replace globe with dropdown for visibility levels | | +| Visibility section in panel | Dedicated section below setup content | | +| Modal dialog | Share button opens Google Docs-style modal | ✓ | + +**User's choice:** Modal dialog + +### Share button appearance + +| Option | Description | Selected | +|--------|-------------|----------| +| Share icon button | Replace globe toggle with share icon showing visibility state | ✓ | +| Keep globe + add share | Two buttons, two functions | | +| Text button with state | Labeled button showing current state | | + +**User's choice:** Share icon button + +### Default expiration + +| Option | Description | Selected | +|--------|-------------|----------| +| 14 days default | Safe default, options for 7d/30d/infinite | ✓ | +| No expiration default | Permanent by default, optional expiration | | +| You decide | Claude picks | | + +**User's choice:** 14 days default + +--- + +## Schema Future-Proofing + +### Shares table design + +| Option | Description | Selected | +|--------|-------------|----------| +| Full shares table now | Complete table with permission, userId, expiresAt, revokedAt | ✓ | +| Link shares only, extend later | Simpler table, add columns in future migrations | | +| You decide | Claude picks based on tradeoffs | | + +**User's choice:** Full shares table now + +### Visibility storage + +| Option | Description | Selected | +|--------|-------------|----------| +| Column on setups table | Replace isPublic with visibility text column | ✓ | +| Derived from shares | No column, derive from shares table via JOINs | | + +**User's choice:** Column on setups table — best for query speed, but must prevent conflicts with shares. +**Notes:** User emphasized "it must be done right to prevent conflicts with the shares" + +--- + +## Public Setup Presentation + +### Link-shared viewer experience + +| Option | Description | Selected | +|--------|-------------|----------| +| Same as public view | Identical to public setup view | | +| Shared view with context | Subtle banner showing share status and expiration | | +| You decide | Claude picks based on existing patterns | ✓ | + +**User's choice:** Claude's discretion + +### Discovery feed changes + +| Option | Description | Selected | +|--------|-------------|----------| +| No changes needed | Just update query from isPublic to visibility | ✓ | +| Add share count indicator | Show social proof on setup cards | | + +**User's choice:** No changes for Phase 32. +**Notes:** Person-specific shares influencing feed algorithm is deferred to future. + +## Claude's Discretion + +- Viewer experience for link-shared setups (shared banner vs. clean view) + +## Deferred Ideas + +- Person-specific shares influencing discovery feed algorithm +- Write-access share enforcement (collaborative editing) +- Person-specific share UI (invite by username/email)