Scaffold the unified visibility/privacy layer introduced by docs/plans/
visibility-system.md. No module adopts it yet — this is the foundation
PR (M1). Module rollout lands in follow-ups starting with Library (M2).
What ships:
- @mana/shared-privacy package
- VisibilityLevel enum ('private' | 'space' | 'unlisted' | 'public')
- VisibilityLevelSchema + UnlistedTokenSchema (zod)
- defaultVisibilityFor(spaceType): personal → private, else → space
- predicates: canEmbedOnWebsite, isReachableByLink,
isVisibleToSpaceMember, canAiAccessCrossUser (always false in P1)
- generateUnlistedToken() — 32-char base64url, CSPRNG, ~192 bits
- VISIBILITY_METADATA: German labels + descriptions + phosphor icon
names so non-UI surfaces (audit logs, CLI) label levels consistently
- <VisibilityPicker> svelte component: compact lock/globe trigger with
4-option menu, full descriptions, optional compact + disabledLevels
- VisibilityChangedPayload type for the domain-event catalog (consumer
registers it when the first module adopts the system)
- .claude/guidelines/visibility.md — step-by-step for module authors
(schema migrations + store wiring + picker placement + embed resolver +
legacy isPublic migration), with a pre-PR checklist
- Plan-doc "Offene Fragen" section rewritten as "Designentscheidungen"
with the seven resolutions the user approved
- CLAUDE.md: shared-privacy listed in the packages table; visibility.md
listed in the guidelines table
- 15 unit tests covering predicates (one-and-only-one 'public' for
embed; phase-1 AI always-deny), defaults (personal vs multi-member,
null fallback), token uniqueness + schema round-trip
Key constraints honored:
- `visibility` stays plaintext (NOT added to the encryption registry)
so RLS predicates and publish resolvers can read it without the user's
master key
- Publish flow remains "decrypt client-side, inline plaintext into
snapshot" — the pattern picture.board already uses in embeds.ts
- Deny-by-default everywhere (personal default = private; unknown space
type defaults to private; cross-user AI always false)
Not in this PR (per plan):
- No schema migrations in any module (M2–M6)
- No RLS predicate updates (arrives with M2)
- No /settings/privacy overview (M7)
- No unlisted share routes (M8)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "every Drizzle table uses pgSchema" rule was documented in
.claude/guidelines/database.md (added yesterday as part of Concern 5)
but enforced only by convention. A new service could slip a raw
\`pgTable()\` past review and collide in the default \`public\` schema
of \`mana_platform\`, and nothing would surface the mistake until a
production migration failed.
- \`scripts/validate-pg-schema-isolation.mjs\` scans every tracked
TypeScript file under services/, apps/api/, packages/ for call sites
of \`pgTable(\` (not imports — imports can still be useful for types).
Strips comments before matching so doc-examples like "use \`pgTable()\`"
don't trigger false positives.
- Wired as \`pnpm run validate:pg-schema\` and a new CI step in the
validate job (right after the turbo-recursion check). 721 files
scan clean today.
- Removed an unused \`pgTable\` import in mana-subscriptions that would
have been the only import of the symbol remaining after this change.
- Updated .claude/guidelines/database.md — the old verification blurb
said "no automated lint rule yet", now points at the enforcer.
Drift verified: injecting a synthetic \`pgTable('bad', {})\` into
subscriptions.ts failed with a clear file:line violation pointing at
the database guideline.
Closes the "no automated lint rule" gap noted in the database guideline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pgSchema was enforced in practice across every service (auth, credits,
usr, events, mail, research, subscriptions, analytics) but nowhere
documented as a rule. New services had to reverse-engineer the pattern
from existing code, and the example in the guideline itself still used
raw pgTable() — actively steering readers in the wrong direction.
- New "Schema Isolation" section: the rule, the why, the naming table
(service → schema), the mana_sync exception, a grep-based verification
command.
- Updated the "Table Definition Pattern" example to use pgSchema so
readers copy the right thing.
The root CLAUDE.md already links here from the "Database (PostgreSQL)"
section — no change needed there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI previously ran `pnpm run test || true` — test failures were silently
swallowed with no artifact, so we had no visibility into what was actually
passing across 1,296 test files.
- New `test:coverage` turbo pipeline task + root script; packages that opt
in by declaring their own `test:coverage` get picked up automatically.
- Wired up three high-value Vitest targets: apps/mana/apps/web (main
frontend, ~590 tests), shared-ui (Svelte component library), and
shared-storage (S3 client). Each emits lcov.info + coverage-summary.json
+ browsable HTML.
- apps/mana/apps/web `"test"` was running in watch mode (just `vitest`),
which hangs under turbo orchestration — changed to `vitest run` and
added `test:watch` for the interactive case.
- CI uploads coverage artifacts (14-day retention) regardless of whether
tests passed. `continue-on-error: true` replaces `|| true` so a failed
suite shows up as a warning annotation on the PR rather than being
invisible. Flip to a hard gate once main is green for a full week.
- Testing guideline documents the pattern + the template vitest config
+ the planned 80% threshold.
- ESLint flat-config `vitest.config.ts` ignore only matched at the root;
widened to `**/vitest.config.{ts,js,mjs}` so nested configs don't trip
the project-service parser.
Coverage baseline produced locally:
shared-storage: 91.37% lines (6 files, 123 tests)
shared-ui: 2.87% lines (mostly Svelte components, untested)
apps/mana/web: 9/59 test files fail — pre-existing, not regression
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The flow was only documented in code comments scattered across the
catalog, executor, and runner. This guide collects the three-file
contract (catalog / executor / init.ts), the auto-vs-propose policy
matrix, and the drift-guard semantics into one place so future
sessions adding a new module's tools have a single entry point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The JWT already carried a `tier` claim but nothing on the server read it
— AuthGate enforcement was client-only, so a valid JWT could hit paid
LLM/research endpoints regardless of the user's access tier.
- shared-hono authMiddleware now extracts `tier` into `c.userTier`,
defaulting unknown/missing claims to `public` (never silently grants
higher access).
- New `requireTier(minTier)` middleware + `hasTier`/`getTierLevel`
helpers. Tier hierarchy (guest < public < beta < alpha < founder) is
mirrored locally to avoid pulling the Svelte-facing shared-branding
package into Bun services.
- Applied `requireTier('beta')` as defense-in-depth on resource-heavy
apps/api modules (chat, context, food, guides, news-research, picture,
plants, research, traces, who) and the MCP endpoint. Pure CRUD modules
stay auth-only — access there is gated by ownership, not tier.
- DEV_BYPASS_AUTH now injects `userTier` (defaults to founder, override
via DEV_USER_TIER).
- Authentication guideline documents the pattern + test suite covers
hierarchy, passes-at-minimum, and rejection paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three naming conventions had drifted through the monorepo (--muted, --theme-*,
--color-*). Only the last is defined in the Mana theme; the others silently
fell back to nothing and stopped tracking theme variants. Today's cleanup
migrated ~100 files, but nothing stopped the drift from creeping back.
- scripts/audit-theme-tokens.mjs scans ~3k source files and fails if any
references a bare shadcn token or a --theme-* prefix, with an allowlist
for known-literal module brand colors (news-research, agent templates)
- wire into pnpm script and lint-staged (runs once per commit touching
*.{svelte,css}, ignores per-file args)
- design-ux.md guideline: fix stale --color-destructive entry (Mana uses
--color-error), add explicit "never bare tokens" warning with examples
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The credit system was overengineered for the local-first architecture:
- Productivity micro-credits (task/event/contact creation at 0.02 credits) made no sense
since these operations happen locally in IndexedDB with zero server cost and were never enforced
- Guild pool system (6 DB tables, spending limits, membership checks) had no active users
- Gift system had 5 types (simple/personalized/split/first_come/riddle) when 2 suffice
Now credits are only charged for operations that actually cost money: AI API calls and
premium features (sync, exports). This makes the value proposition clear to users.
Changes:
- Remove 8 productivity operations + CreditCategory.PRODUCTIVITY from @mana/credits
- Delete guild pool service, routes, schema (3 files); remove guild refs from 8 backend files
- Simplify gifts to simple + personalized only; remove bcrypt/riddle/portions logic
- Update all frontend pages (credits dashboard, gift create/redeem, public gift page)
- Update shared-hono consumeCredits() to remove creditSource parameter
- Update mana-credits CLAUDE.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace @arcade/backend (NestJS) with @arcade/server (Hono/Bun).
Same two endpoints, no auth required (public game generator):
- POST /api/games/generate — AI game generation (Gemini, Claude, GPT)
- POST /api/games/submit — Community game submission via GitHub PR
- GET /health — Health check
This removes the last remaining NestJS backend from the monorepo.
NestJS is now completely gone — all servers use Hono + Bun.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the icon standard: always use Phosphor components from
@manacore/shared-icons, never inline SVG paths. Lists exceptions
(spinners, brand logos, charts, decorative SVGs).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Combines LightWrite (beat/lyrics editor) and Mukke (iOS music player) into
a single web-based music workspace app. Archives the old Mukke mobile app.
- Rename: @lightwrite/* → @mukke/*, all branding, configs, Dockerfiles
- New DB schemas: songs, playlists, playlist_songs + songId FK on projects
- New backend modules: SongModule, PlaylistModule, LibraryModule
- New web: app shell with sidebar, library (songs/albums/artists/genres),
web player (queue/shuffle/repeat/MediaSession), playlists, search,
upload, dashboard, album/artist/genre detail pages
- Auth: add forgot-password + reset-password pages, extend auth store
- Tests: 40 backend unit tests (song, playlist, library services)
- Config: env generation, MinIO bucket, docker-compose prod, cloudflare
- Docs: update CLAUDE.md, auth guidelines with SvelteKit checklist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add design-ux.md with UI patterns (inline editing, mobile-first,
animations). Add LOCAL_LLM_MODELS.md and LOCAL_STT_MODELS.md
documenting available AI models for the Mac Mini server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mark all 7 web apps with backends as using the correct
dynamic URL pattern after recent fixes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Better Auth generates non-UUID user IDs (e.g., otUe1YrfENPdHnrF3g1vSBfpkQfambCZ).
Changed all user_id columns from uuid to text type to prevent
"invalid input syntax for type uuid" errors.
Also documented this requirement in:
- .claude/guidelines/authentication.md (new User ID Format section)
- .claude/guidelines/database.md (User ID Column Type section)
- apps/todo/CLAUDE.md (Database Schema section)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive guide in .claude/guidelines/sveltekit-web.md about
build-time vs runtime environment variables for Docker deployments
- Document the correct hooks.server.ts + window injection pattern
- Add Problem 9 to TROUBLESHOOTING.md for hardcoded localhost URLs
- List which apps are fixed vs need fixing
- Add checklist for fixing new apps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Run prettier --write to fix formatting inconsistencies in 80 files
across calendar, contacts, picture, presi, storage, zitare apps
and shared packages/documentation.