.env.macmini contains MANA_AI_PUBLIC_KEY_PEM=-----BEGIN PUBLIC KEY----- ...
which bash parses as 'PUBLIC: command not found' under set -e.
Plus drop set -e so a single failed container doesn't abort the rest.
Failures already collected in FAILED_DBS for the summary report.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aktuell deckte das Skript nur mana-infra-postgres ab. Greenfield-Apps
(Cards seit 2026-05-08, Nutriphi seit 2026-05-08, Zitare seit 2026-05-08,
Manaspur seit 2026-05-13) haben eigene Container und waren komplett
NICHT gebackupt.
Plus: launchd-Job lief seit 2026-02-12 nicht — Pfad zeigte auf
mana-monorepo/, das nicht mehr existiert. plist-Update folgt separat.
Test-Run: bash backup-databases.sh sollte jetzt 6 Container × N DBs
dumpen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auth portal is now live: API calls (Better Auth endpoints) still hit
mana-auth (:3001) directly; all UI routes (login, register, reset,
verify-email) are served by the new mana-auth-web SvelteKit app on
host port 3042.
Also updates the duplicate-hostname validator to allow path-based split
routing rules for the same hostname.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase-3-Rename des ehemaligen Multi-App-Monorepos zum eigenständigen
Produkt-Repo. Verein heißt mana e.V., Plattform-Domain bleibt mana.how,
apps/mana/ bleibt unverändert — nur der Repo-Container kriegt den
neuen Namen "managarten" (Garten der mana-Apps).
Geändert:
- package.json#name + #description
- README.md (Titel + erster Absatz)
- TROUBLESHOOTING.md
- alle Mac-Mini-Skripte (Pfade ~/projects/mana-monorepo → ~/projects/managarten)
- COMPOSE_PROJECT_NAME-default in scripts/mac-mini/status.sh
- .github/workflows/cd-macmini.yml + mirror-to-forgejo.yml
- apps/docs (astro.config.mjs + content)
- .claude/settings.local.json (Bash-Permission-Pfade)
- alle docs/*.md Pfad-Referenzen
- launchd plists, .env.macmini.example, infrastructure/
Forgejo-Repo + GitHub-Repo bereits via API umbenannt. Lokales
Verzeichnis-Rename + Mac-Mini-Cutover folgen separat.
Part of the 8-Doppel-Cutover (2026-05-08, plan
~/.claude/plans/floating-swinging-flurry.md):
- docker-compose.{macmini,dev,test}.yml: build context for
mana-{auth,credits,media,llm,notify} switched to ../mana/services/...
so the Mac Mini stack pulls platform services from the platform repo
(sibling clone), not from services/ in this monorepo.
- .npmrc + apps/api/{Dockerfile,package.json}: @mana/media-client now
resolved from Verdaccio (npm.mana.how, ^0.1.0) instead of as a
workspace COPY from services/mana-media/packages/client. Build-arg
NPM_TOKEN flows through .npmrc for pnpm install auth. Required
before services/mana-media/ can be deleted.
- .github/workflows/{ci,cd-macmini,daily-tests}.yml: removed the
detect-/build-/test-jobs that targeted services/mana-{auth,credits,
notify,media}/. Those services build out of the platform repo now —
CI for them belongs in mana/-repo (open). cd-macmini's
workflow_dispatch can still rebuild any of them on demand;
auto-detect on path-change is gone for these five.
- scripts/{mac-mini/push-schemas.sh,run-integration-tests.sh}:
rewritten to look in ../mana/ for the platform services.
- package.json dev:{auth,credits,notify,media}: paths point at
../mana/services/... so local dev still works post-cutover.
What this commit does NOT do: delete services/mana-{auth,credits,...}
from this repo. That waits for Phase 7 once the Mac Mini stack has
booted cleanly from the new build paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the migration documented in docs/PORT_SCHEMA.md:29-30
("moved from 3065 on 2026-05-06 because the platform mana-media
reserves 3065"). services/mana-events/src/config.ts already
defaults to 3115 — generate-env.mjs was the last file still
emitting the old value, so anyone running pnpm setup:env would
get a .env that pinned the service back to 3065 and collided
with mana-media on the platform.
cards-server: switch from `bun run --watch` to `bun run --hot`.
--watch restarts the whole process on file change, racing the
old + new Bun.serve calls for the port (the EADDRINUSE you see
right after `listening on :3072`). --hot does in-process HMR via
the globalThis[hmrSymbol] pattern; the port stays bound across
reloads. apps/api already uses --hot for the same reason.
scripts/setup-databases.sh:
- create_db_if_not_exists "mana_notify" + "mana_credits" so a
fresh-machine `pnpm setup:db` no longer leaves these two DBs
off (mana-notify was crashing on boot with SASL fallback,
mana-credits was less obvious because its drizzle config
defaults to mana_platform — but the runtime config can point
at mana_credits, so safer to have the DB exist).
- Fix the cards branch: was pointing at the non-existent
@mana/cards-database package; now points at @mana/cards-server
where the actual schema lives.
Verified: drop+re-create flow + cardecky:full boot + touch-trigger
hot-reload all clean.
Phase A — Cards joins the unified theme system:
- Drop placeholder --color-cards-* palette; app.css imports
@mana/shared-tailwind/themes.css + sources.css.
- Remove hardcoded class="dark" from app.html; body uses
bg-background text-foreground.
- New $lib/stores/theme.ts: createThemeStore({ appId: 'cards' }).
ThemeToggle from @mana/shared-theme-ui in the header next to
the streak chip.
- Sweep all neutral / red / emerald / amber / indigo utilities in
apps/cards/apps/web/src to semantic tokens (560 substitutions
across 19 files): bg-neutral-900 → bg-card, text-neutral-400 →
text-muted-foreground, bg-red-500 → bg-error, etc. Domain
literals kept (FSRS grade colors red/orange/green/blue, GitHub-
violet PR-merged badge, marketplace-amber Buy button, admin-
inbox category palette).
- Cards added to validate-theme-utilities scope so future drift
fails CI.
Phase C — per-app accent token:
- New --color-app-accent in shared-tailwind/themes.css. Theme-
agnostic (registered in validate-theme-parity's THEME_AGNOSTIC
regex), so it stays the same across light/dark/lume/etc. Defaults
to Mana indigo at :root.
- Cards layout writes 258 90% 66% (= #8b5cf6 violet, from
MANA_APPS.cards.color) onto documentElement at boot via
applyCardsAccent(). All Cards CTAs (Lernen, Abonnieren, Senden,
links inside cloze cards) flow through bg-app-accent /
text-app-accent now.
Net effect: Cards gets light/dark + 4 palette variants + a11y
toggles for free, and any future app can drop in by setting its
own --color-app-accent without touching shared-tailwind.
Schließt die platform/product-split-Lücke: HEAD's apps/api/src/index.ts
referenziert seit dem Forms-M10d-Commit personasInternalRoutes /
personasAdminRoutes — die Implementierung lag aber noch nicht im Repo.
Build war strukturell broken bis hierhin.
Was wandert von mana-auth nach apps/api:
apps/api/src/modules/personas/
├── schema.ts — pgSchema('personas') mit personas /
│ persona_actions / persona_feedback;
│ userId ist plain text (Cross-DB-FK auf
│ mana-auth's auth.users geht nach Split nicht).
├── internal-routes.ts — service-key gated GET /due, POST /:id/actions
│ und POST /:id/feedback. Append-only +
│ idempotent über deterministische row-ids
│ (tickId-i-tool / tickId-module).
└── admin-routes.ts — admin-JWT gated CRUD; ruft mana-auth via
/api/v1/admin/users + /api/v1/auth/register
+ /api/v1/internal/users/:id/persona-stamp
für den User-Lifecycle.
Persona-runner-Client zeigt jetzt auf apps/api:
- config.ts: neues apiUrl-Feld (default http://localhost:3060,
Env MANA_API_URL); authUrl bleibt für /api/v1/auth/login + spaces.
- clients/mana-auth-internal.ts: drei Calls treffen jetzt
/api/v1/personas/internal/* statt mana-auth's
/api/v1/internal/personas/* — Datei-Name bleibt um Call-Site-Diff
klein zu halten.
- index.ts: ManaAuthInternalClient bekommt config.apiUrl statt authUrl.
Seed/Cleanup-Skripte:
- --api= als bevorzugter Flag, --auth= als Legacy-Alias (cached
Shell-History würde sonst hart brechen).
- default http://localhost:3060, Env MANA_API_URL.
- Endpoint-Pfade umgeschrieben:
POST /api/v1/admin/personas → /api/v1/personas/admin
DELETE /api/v1/admin/personas/:id → /api/v1/personas/admin/:id
drizzle.config.ts: schema-Array + schemaFilter um 'personas' erweitert.
DB-push ist Pflicht-Schritt vor erstem Boot, sonst 42P01 auf /due.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up sweep after acb737e25 — the context module's UI + Dexie
tables + AI route were already removed, but a handful of registry-style
refs in the monorepo's plumbing still pointed at the dead module:
- packages/shared-utils/src/analytics.ts: drop the `context: createModuleTracker('context')`
entry from the `track` map and delete the unused `ContextEvents`
helper (no consumers — every analytics call site that used it lived
in the deleted module).
- packages/shared-utils/src/analytics.ts (cont.): the deletion above
removes the only typed reference to track.context, so the property
cleanly disappears from the inferred type.
- package.json: drop 6 dead npm scripts (`context:dev`, `dev:context:web`,
`dev:context:app`, `dev:context:full`, `dev:context:local`, `setup:db:context`)
— all referenced `@context/*` workspace packages that were removed
with the module. `pnpm context:dev` would silently succeed-with-zero-targets
before; now it correctly errors as unknown script.
- scripts/generate-env.mjs: drop the two `apps/context/apps/{server,web}/.env`
generator entries pointing at non-existent app directories.
- scripts/validate-monorepo.mjs: drop `'@context/'` from the internal
workspace prefix list — fences a class of dependency that no longer
exists.
- .env.development: fix a stale comment pointing at the renamed
/api/v1/context/import-url endpoint (now /api/v1/kontext/import-url
per acb737e25).
- apps/context/: delete the leftover directory (CLAUDE.md describing
vanished paths + a package.json with a `dev:mobile` script filtering
the @context/mobile package that was deleted with all per-product
mobile apps on 2026-04-20).
What remains and is intentional: historical plan docs / devlogs /
audit reports / generated complexity-map.html / Dexie v57 drop
migration / pnpm-lock.yaml (regenerates on next `pnpm install`).
Unrelated `'context'` strings (MemoryCategory enum, Kontext-Agent
template id, encryption-vaults DB column, Astro landing /context
content collection) stay — different concepts that happen to share
the word.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The context module's UI + Dexie tables + i18n bundle were already
removed in d3e2e73ca. This follow-up cleans up everything else that
still referenced it:
- API: rename POST /api/v1/context/import-url → /api/v1/kontext/import-url
(the kontext singleton was the only consumer); drop the unused
/ai/generate + /ai/estimate endpoints; rename the credit-op label
AI_CONTEXT_IMPORT_URL → KONTEXT_IMPORT_URL; drop AI_CONTEXT_GENERATION
from packages/credits.
- Web: drop registerApp + File icon import from app-registry/apps.ts;
drop contextModuleConfig from data/module-registry.ts (+ snapshot test);
drop useRecentDocuments + useSpaces from cross-app-queries.ts; drop
ContextDocsWidget from widget-registry + dashboard.svelte.ts +
types/dashboard{,.test}.ts; drop dashboard.widgets.context from all 5
dashboard locales; drop context entries from hooks.server allowlist,
splitscreen registry, observatory mockData, spiral collect, crypto
registry + plaintext-allowlist.
- Dexie: remove documents/contextSpaces/documentTags from v1, v31, v53
stores blocks; add v57 dropping the three tables on local dev DBs
that already ran an earlier schema.
- Shared-branding: drop 'context' from AppId union, APP_BRANDING,
MANA_APPS, APP_ICONS (+ contextSvg), ContextLogo.svelte (+ logos
barrel re-export).
- Spiral-DB: drop context: 10 from MANA_APP_INDEX (slot now free).
- i18n hardcoded-string baseline: drop 5 context routes/files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Arcade lives as its own pnpm workspace at ~/Documents/Code/arcade
now, with no @mana/* coupling. This drops every reference and the
games/ directory from the monorepo.
Removes:
- games/ directory (89 files: web + server + 22 HTML games + screenshots)
- @arcade/web, @arcade/server pnpm workspace entries (games/* globs)
- arcade scripts in root package.json (4 scripts)
- arcade.mana.how from mana-auth trusted origins + CORS_ORIGINS
- arcade entries in mana-apps registry, app-icons, URL overrides
- arcade.mana.how from cloudflared tunnel + prometheus blackbox probes
- arcade-web service block in docker-compose.macmini.yml
- generate-env.mjs entries for arcade server + web
- BRANDING_ONLY 'arcade' entry in registry consistency spec
- dead arcade translation keys in GuestWelcomeModal (DE+EN)
- arcade mention in CLAUDE.md, authentication guideline, MODULE_REGISTRY
Verified:
- services/mana-auth/src/auth/sso-config.spec.ts: 8/8 pass
- pnpm install regenerates lockfile cleanly (-536 lines)
- no remaining 'arcade' refs outside historical snapshot docs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the cross-cutting drift that the app-registry sanity-test was
silently catching but BRANDING_ONLY exceptions papered over.
App-registry wiring:
- Register augur, broadcasts, invoices, timeline as workbench cards.
- Resolve agents↔ai-agents naming drift: workbench id is now `agents`
(matches MANA_APPS + the /agents route URL); folder stays `ai-agents`
for grouping with other ai-* modules.
Broadcast→broadcasts unification:
- module.config appId, MANA_APPS id, APP_ICONS key, all route appIds,
and the redundant APP_URL_OVERRIDES entry — all aligned with the
earlier folder rename so nothing diverges anymore.
Top-level routes for workbench-only modules:
- /goals, /myday, /kontext, /rituals, /automations, /activity — thin
RoutePage wrappers around the existing module ListViews.
- /timeline becomes a real module (ListView extracted from the route),
route shrinks to a 12-line wrapper.
Food unarchive:
- packages/shared-branding/src/mana-apps.ts: remove `archived: true`
from food entry. The module is fully wired (registered, synced,
routed, with AI tools); the flag was outdated.
i18n cleanup:
- Rename ai-agents → agents key in all 5 apps locales.
- Drop dead "observatory" key from all 5 nav locales (route folder was
removed in 7bca16dfa).
New CI guard — scripts/validate-tier-patches.mjs:
- Scans for `LOCAL TIER PATCH — revert before release` markers.
- Default: informational list (does not fail).
- Strict mode (MANA_TIER_PATCH_STRICT=1) for release/RC pipeline.
- Wired into validate:all.
Spec update:
- registry.spec.ts WORKBENCH_ONLY/BRANDING_ONLY: documented Settings
family + AI Studio surfaces + intentionally-internal modules so the
drift guard fires only on real drift.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Erste Demo-Persona auf Prod live: chor-taegerwilen@mana.how.
Inhalt:
- Recherche-Brief mit Quellen, IDs, Modul-Mapping, Pitch-Hooks
- data.ts: 54 Mitglieder (S/A/T/B vollständig), Vorstand, Chorleiter,
Termine April–Juni 2026, 5 Konzerte 2026, Konzert-Archiv 2015–2025,
kontextDoc Markdown
- seed.ts: idempotentes Bun-Skript, schreibt direkt in
mana_sync.sync_changes via SSH-Tunnel (5433). Setzt RLS-Context,
räumt prior demo-seed Rows auf, schreibt 118 Records über
kontext / contacts / calendar+timeblocks / events / library /
notes / website / ai-missions.
Pitch-Hook: der Verein war bereits ClubDesk-Kunde — Mana-Replacement
ist die direkte Migrations-Story.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pelias was retired from the Mac mini on 2026-04-28; photon-self
(self-hosted Photon on mana-gpu) has been the live primary since then.
This removes the now-dead Pelias adapter, config, tests, and the
services/mana-geocoding/pelias/ stack — the entire compose file, the
geojsonify_place_details.js patch, the setup.sh import script.
Provider chain is now `photon-self → photon → nominatim`. The chain
keeps its `privacy: 'local' | 'public'` split, sensitive-query
blocking, coord quantization, and aggressive caching unchanged.
Three direct calls to nominatim.openstreetmap.org that bypassed
mana-geocoding now route through the wrapper:
- citycorners/add-city + citycorners/cities/[slug]/add use the shared
searchAddress() client (browser → same-origin proxy → mana-geocoding
→ photon-self).
- memoro mobile drops its OSM reverse-geocoding fallback entirely;
Expo's on-device reverse-geocoding stays as the sole path. Routing
through the wrapper would require a memoro-server proxy endpoint —
a follow-up if Expo's quality proves insufficient.
Other behavioral changes:
- CACHE_PUBLIC_TTL_MS dropped from 7d → 1h. The long TTL was a
privacy-amplification trick from the Pelias era; with photon-self
serving the bulk of traffic, a transient cross-LAN blip was pinning
cached fallback answers for days. 1h gives quick recovery.
- /health/pelias renamed to /health/photon-self; prometheus blackbox
config + status-page generator updated.
- mana-geocoding container no longer needs `extra_hosts:
host.docker.internal:host-gateway` (was only there for the
Pelias-on-host-network era).
113 tests passing. CLAUDE.md rewritten to reflect the post-Pelias
architecture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Colima starts its Linux VM with no swap configured. Without swap the
kernel responds to memory pressure by invoking the OOM-killer instead
of paging out cold pages — meaning a transient peak (mana-web Vite
build with 8 GiB heap landing on top of the running container set)
takes down a container instead of just stalling for a few seconds.
The 2026-04-28 Mac Mini RAM audit found:
- VM allocated: 12 GiB (1 GiB kernel overhead → 11 GiB user)
- Container RSS: ~4 GiB pinned
- Available headroom: ~7.6 GiB
- mana-web Vite peak: ~8 GiB
That's 400 MiB over the limit during builds, which is why we previously
needed the build-memory-headroom.sh wrapper to pause monitoring (frees
~700 MiB temporarily). Swap is the safer second backstop — Linux only
swaps under actual pressure (used=0 right after creation, confirmed
free -h), and the kernel can fall back to paging cold container memory
to give a build the burst it needs without killing anything.
The new step in migrate-to-colima.sh:
- creates /swap (2 GiB, root-only)
- mkswap + swapon
- persists in /etc/fstab so the VM remounts it on every restart
- idempotent — re-runs are no-ops
Already provisioned on the live VM via:
ssh mana-server 'colima ssh -- "sudo fallocate -l 2G /swap && \
sudo chmod 600 /swap && sudo mkswap /swap && sudo swapon /swap"'
Verified: free -h shows Swap: 2.0Gi total / 0B used. Currently dormant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous version of the cleanup trap only deleted SQL files left
by drizzle-kit's probe-generate, but not the matching `_snapshot.json`
(56 KB per service) or the journal entry. Each deploy then leaked one
snapshot file into the runner's working tree.
Surfaced after my own local smoke-test: ran the script against
mana-auth, found a 56 KB \`drizzle/meta/0000_snapshot.json\` left
behind that I had to clean up manually.
The trap now:
- Computes the full set of files added under \`drizzle/\` during this
run (not just SQL) and removes every one of them.
- Strips the probe's journal entry via jq.
- If the \`drizzle/\` dir didn't exist before the run, removes it
entirely. Otherwise sweeps empty meta/ subdirs the run created.
Smoke-tested locally: working tree is clean after each run regardless
of whether drizzle/ existed beforehand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-up fixes after the first migration-step deploy revealed
gaps:
1. \`pnpm dlx drizzle-kit\` doesn't work — the drizzle.config.ts file
itself does \`import { defineConfig } from 'drizzle-kit'\`, and
Node's resolver only finds that import via local node_modules,
not pnpm's dlx cache. Reverted to plain \`pnpm exec drizzle-kit\`
and require the workspace to be installed.
2. CD now runs \`pnpm install --filter ./services/<svc>... --frozen-
lockfile --ignore-scripts\` once at the start of the migration
step for every Drizzle service in the deploy. Path-based filter
(not name-based) because our service package names follow no
uniform convention (\`@mana/auth\` vs \`@mana/credits-service\` vs
\`@mana/events\`). pnpm's lockfile cache makes second-and-later
runs near-instant.
3. Dropped the \`--silent\` flag from \`pnpm exec drizzle-kit --version\`
— it isn't a recognised pnpm-exec flag and causes a 254 exit code,
making the script's "is drizzle-kit available?" probe always fail.
Smoke-tested locally — script now runs cleanly against mana-auth's
schema, reports "no changes detected", cleans up the probe SQL file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Mac Mini runner doesn't run \`pnpm install\` (every service builds
inside Docker), so per-service node_modules/.bin/drizzle-kit isn't
present. The first deploy with the new migration step printed
\`ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command "drizzle-kit" not found\`
and silently treated every service as "no schema changes — clean".
Pick the invocation mode at runtime: \`pnpm exec drizzle-kit\` if a local
binary exists, otherwise \`pnpm dlx drizzle-kit\`. dlx caches the package
in the global pnpm store after the first fetch, so subsequent calls
are fast. drizzle-kit reads its config from cwd, so it still picks up
each service's drizzle.config.ts correctly.
Smoke-tested locally against services/mana-auth — script reports
"no schema changes — clean" instead of failing silently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mana-geocoding wrapper now returns `notice: 'fallback_used' |
'sensitive_local_unavailable'` alongside results so the UI can show
the user *why* a query had unusual behavior. This commit wires that
all the way through the Places module's address-autocomplete inputs.
Geocoding client (lib/geocoding/index.ts):
- Add `GeocodingNotice` and `SearchOutcome` types
- Add `searchAddressDetailed` and `reverseGeocodeDetailed` — same
semantics as the existing functions but return the wrapper's
provider/notice metadata. Existing `searchAddress`/`reverseGeocode`
stay backward-compatible (they call the detailed variants under
the hood and discard the metadata).
- Extend GeocodingResult with optional `provider` field.
Places ListView (the only current consumer that exposes typed
addresses to users):
- Both autocomplete inputs (tracking-edit + main address-search)
now use searchAddressDetailed and surface notices inline.
- 'sensitive_local_unavailable' renders an amber explainer block in
the dropdown — title + body — so the user knows why their medical
query returned 0 hits without leaking the search to a public API.
- 'fallback_used' renders a small "≈ ungefähr" footer badge so users
understand the result came from public OSM (less precise but
still valid).
- The dropdown opens when EITHER results exist OR a notice is
present — sensitive blocked queries with empty results still
surface their explainer.
i18n: new `places.geocoding_notice.*` sub-namespace in all 5 locales
(de/en/es/fr/it) — 4 strings each. All validators green.
Other consumers (places DetailView, events, photos, contacts) keep
the existing searchAddress/reverseGeocode calls — they don't need
the privacy notices today and would just add noise. They can adopt
the detailed variant if/when the use case warrants it.
Two CD-pipeline ergonomics fixes that surfaced during the 2026-04-28
schema-drift sweep.
(C) Auto-apply additive Drizzle migrations
========================================
8 services use Drizzle (mana-auth/-credits/-events/-research/-mail/
-subscriptions/-user/-analytics) but the CD pipeline never ran their
`db:push` script, so 4 schema additions stayed undeployed for days
(auth.users.kind, credits.{sync_subscriptions,reservations},
event_discovery.*) until live PostgresErrors surfaced them.
New `scripts/mac-mini/safe-db-push.sh`:
- Uses `drizzle-kit generate` to write a probe SQL file (does NOT
apply yet).
- Greps the generated SQL for destructive patterns (DROP TABLE/
COLUMN/TYPE/SCHEMA/INDEX, ALTER COLUMN ... TYPE, RENAME).
- Refuses to auto-apply if any are found — operator must review and
run `pnpm db:push --force` manually after pg_dump.
- Otherwise applies via `drizzle-kit push --force` and cleans up the
probe artifacts.
CD step "Apply schema migrations" runs between build and container
restart, sourcing each changed service's DATABASE_URL from compose
config (with @postgres → @localhost rewrite for the host runner).
Failure aborts deploy before the new container starts — the old
container keeps running with the old schema, which matches.
(D) Build-time RAM headroom
========================================
mana-web's Vite build needs 8 GiB of Node heap; Colima's VM is sized
at 12 GiB; ~3.5 GiB of other containers run during deploy. The 2026-
04-28 mana-web deploy OOM'd at the Vite step ("cannot allocate
memory") and only succeeded on retry once concurrent traffic settled.
New `scripts/mac-mini/build-memory-headroom.sh`:
- `start`: stops every container matching `^mana-mon-` (the
observability stack — VictoriaMetrics, Loki, Glitchtip, cAdvisor,
umami, blackbox, exporters). Frees ~700 MiB.
- `stop`: restores them from the snapshot list captured at start.
- `wrap <cmd>`: pause + run + always-resume via trap.
CD wraps the build loop with start/stop, but only when mana-web is in
the change set — other services build well below 4 GiB and don't
need the headroom. The monitoring stack resumes before the migration
step so cAdvisor + exporters are back online for the deploy-metrics
collection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- comic/components/CharacterPicker: route through comic.picker.* with
HTML interpolation for the no-face/empty-garment alerts
- comic/views/DetailCharacterView: route through comic.character_detail.*
+ dynamic comic.styles.<id>; drops unused STYLE_LABELS import
- quiz/PlayView: route through quiz.play_view.* (back/empty/result/play
all consolidated)
Baseline 869 → 851 (-18).
- Back button (← Symbole), Gespeichert hint, Zusammenführen…/Löschen actions
- Merge panel: label with {name} interpolation, "– Symbol wählen –" placeholder, OK/Abbrechen
- Empty: "Symbol nicht gefunden."
- Editable header: name placeholder, "Traum"/"Träume" via count_singular/plural
- Color picker: aria with {color} interpolation
- 4 section labels (Meine Bedeutung / Stimmungs-Verteilung / Häufig zusammen mit / Träume mit diesem Symbol) + meaning placeholder
- Mood label routed via $_('dreams.moods.' + mood) with valid-mood guard; "Unbekannt" fallback via symbol_detail.mood_unknown
- Co-occurring chip title with {name} interpolation
- Confirms: delete + merge with {name}/{source}/{target} interpolation
- Dream-ref title fallback via dreams.list_view.untitled
- MOOD_LABELS import dropped (constant kept in types.ts for non-Svelte callers)
Baselines: hardcoded 1074 → 1066 (8 cleared); missing-keys baseline +0 (dreams.moods.* dynamic key already baselined).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>