# 2026-02-27 — Daily Notes

## Key Events

### Security
- **XMRig miner reinfection** — Binary re-dropped after Ben's cleanup. Found @reboot crontab + .bashrc line 122 injection. All cleaned. Entry point still unknown.
- **CVE-2025-29927** — Next.js middleware auth bypass. All 22+ products patched to 16.1.6. PRs pending merge for 6 repos.
- **Caddy scanner_block fix** — .env/.git paths were returning 200 at non-root paths. Fixed regex + blocked 3 attacker IPs. No actual credential exposure (was Next.js HTML shell).

### Builds & Features
- **Skill-Bridge v1.0** (Ed) — Express middleware on port 3099 routes Symphony button actions through brand-aware pipeline. PM2 managed. Phase 2 pending.
- **Symphony Create Tab** (Frances) — Marketing image generation with pegs, platform mockups, persistent gallery. Full redesign after feedback.
- **Brandcast concurrency fix** — Semaphore (max 3) + async to prevent 36-process crash from 12 simultaneous frame gens.
- **Remotion pipeline wired end-to-end** (loewe) — validate → Veo QA → render → remotion-qa → gate. QA hard gate enforced (HTTP 422 on FAIL).
- **VectorPM marketing reel** (loewe) — Generated 3 versions, found QA bypass gap, fixed it, regenerated with clean clips. Added 7-track royalty-free music library.
- **ReadStreak micro-interactions** (Nick G) — 10 Reanimated animations. Pushed to SymphonyAgents fork.
- **ReadStreak book cover fixes** (Nick G) — Swapped Google Books URLs to Open Library for stable covers.
- **Alignly Content Hub Style D** (Kenneth) — Dark green pain-point meme cards + 6 posts.
- **Alignly GloryReborn single-tenant** (Med) — Middleware routing, invite-only signup, empty state handling.
- **WanderKit Concept K** (Tom) — TC cards, music sourcing, Leonardo scene prep.
- **Tiangge motherboard sheet** (Jhai) — 6-tab Excel template.

### Corrections Captured
- Frances: Always ask image size before generating
- Kenneth: Content updates → content hub site, not main site
- Loewe: QA gate must be enforced, subagents must check blocked/qaWarning fields
- Nick G: Data accuracy — verify Firestore data before reporting user ownership

## TIL (Today I Learned)
- Google Books image URLs without `imgtk` auth token can silently return grey placeholder images
- Reanimated requires babel.config.js — without it animations silently fail (no error)
- Remotion's volume coefficient at 0.12 is inaudible (-27dB) without TTS narration — use ffmpeg for no-narration videos
- Scanner block regexes must match at any path depth, not anchored to root
- The server's XMRig attacker has an active dropper that re-infects after cleanup — need root-level investigation

## WanderKit PWA Offline (#103c) — Session Summary (2026-02-27 afternoon/evening)

### What was shipped
- **#103c Layer 1-2** (pre-existing, already on PROD 00050): start_url=/dashboard, last-url persistence, /dashboard SW fallback, proactive caching
- **#103c Layer 3**: Block trip creation while offline (full-screen guard), sync interstitial on reconnect, fire-and-forget offline writes in ActivityModal + EditTripModal
- **UI fix**: Long activity names truncate with ellipsis in modal header (min-w-0 on DialogHeader in CSS grid context)
- **SW wk-v8**: pre-caches /wanderkit-logo.png (was missing — Logo component uses this, NOT /icon-192.png); image handler checks caches before returning mountain placeholder; handles /_next/image?url= by extracting and checking original URL
- **Trip card cover**: onError handler on <img> falls back to gradient placeholder when Firebase Storage image fails offline

### Persistent offline save bug — root cause archaeology
The "infinite spinner on Save" in ActivityModal had MULTIPLE layers:
1. `navigator.onLine` unreliable on Android — replaced with fire-and-forget (no await)
2. `fireWithTimeout` (Promise.race with 2s timer) — still hung on some activities
3. Root cause: Vaul Drawer fires `onOpenChange(false)` when controlled `open` prop changes. The ActivityModal's `isDirty` interceptor wrapper runs at that moment. React hadn't yet committed `setIsDirty(false)` (batched). So `isDirty` appeared stale-true → interceptor blocked the close → modal stuck with spinner
4. Fix attempt: `&& !isSubmitting` in interceptor — partially worked but `handleCloseAttempt` (X button) also has isDirty check without isSubmitting guard
5. Final fix: `forceClose` state + useEffect — sets `forceClose=true` AFTER committing `isDirty=false` + `isSubmitting=false`, then the useEffect calls `onOpenChange(false)` post-commit, guaranteeing no stale closure

### PROD revision history today
- 00050: pre-existing (start_url, caching) — already there
- 00051: #103c offline guard + sync interstitial + bug fixes
- 00052: fire-and-forget writes + SW logo fix (wk-v8)
- 00053: EditTripModal fire-and-forget (Tom was editing TRIP not activity)
- 00054: close modal outside try-catch (isDirty race attempt)
- 00055: `!isSubmitting` in isDirty interceptor (partial fix)
- 00056: forceClose useEffect (in progress, building now)

### Key learnings
- `navigator.onLine` is unreliable on Android PWA during offline transitions
- Firestore `persistentLocalCache` writes should NEVER be awaited — fire-and-forget only
- Vaul Drawer calls `onOpenChange` when controlled `open` prop changes (Radix does too) — this fires during React's commit phase before state updates are visible in closures
- `useEffect` is the safe way to call `onOpenChange(false)` after a save — runs post-commit
- Logo component uses `/wanderkit-logo.png` NOT `/icon-192.png` — must be pre-cached separately
- SW `/_next/image?url=` requests need special handling — extract `url` param, check original in cache
- CSS grid cells default to `min-width: auto` — add `min-w-0` to allow text truncation
- `overflow-hidden` on a flex-col container can kill content visibility in Vaul Drawer context
- Tom tests on Android Nothing Phone 3a Pro with installed PROD PWA
- For offline SW testing: must go online, open app, open activity modal (to cache lazy chunk), THEN go offline

### EditTripModal
- Had bare `await updateDoc/addDoc/deleteDoc` with no offline handling — replaced all with fire-and-forget

### WanderKit PROD
- Service: `wanderkit-prod`, project: `symph-wanderkit`, region: `us-central1`
- Deploy: `gcloud builds submit --config cloudbuild.yaml --substitutions=SHORT_SHA=$(git rev-parse --short HEAD) --project=symph-wanderkit`
- Cloud Build uses global region (NOT us-central1 regional builds for this project)
- `cloudbuild.yaml` passes all NEXT_PUBLIC_FIREBASE_* build args — must use this, not `gcloud run deploy --source`
- Dev: `/home/aria/projects/wanderkit/` (symlinked from /home/aria/products/wanderkit/apps/main)
[2026-02-27 20:53:59] Daily changelog posted to #aria (aria-changelog missing perms)

## Fact Extraction Run (fact-extraction-hb72, 20:57 GMT+8)
- Extracted: 13 new technical facts (WanderKit #103c PWA offline session), 3 new people entries (Frances, Jhai, Nick G correction)
- Sources: memory/2026-02-27.md (full day notes)
- Notes: decisions.md and most of technical.md were already up to date from prior extraction runs; no conflicts found
