Run same workloads as TypeScript in parallel, compare results.
Shadow never writes authoritative state — results logged for comparison.
Candidates: heartbeat, friction, digest, proposal triage, content-sync.
Comparison metrics: latency, error rate, output quality, recovery time.
Shadow comparison becomes the graduation exam for ADR-0114.
Oban + OTP + Phoenix is the recommended stack if migrating.
Jido adds agent abstractions but young/dependency risk.
Oban is the mature Inngest equivalent (6+ yrs, funded, dashboards).
- Email read now caches threads to ~/.cache/joelclaw/email/threads/
- Attachment download + local file caching
- --refresh flag to bypass cache, --cache-ttl-minutes for TTL control
- Stale fallback when Front API is unreachable
- ADR-0112 proposes system-wide caching: hot (Redis), warm (file), cold (NAS)
- Covers Front, Todoist, Granola, Typesense, PDS with per-service TTLs
- Webhook-driven invalidation + CLI --refresh on all cached commands
- New StatusLed component: colored dot only, no text/pill background
- Sizes sm/md/lg (6/8/10px) with pulse animation for healthy
- Subtle glow effect matching status color
- Left-aligned: [LED] name across nodes, pods, daemons
- StatusBadge kept for backward compat
- Fix formatDateStatic import → toDateString (function was removed by prior commit)
- Remove proxy.ts (conflicts with middleware.ts in Next.js 16)
- Clean up untracked (owner) route group and leftover files from gateway agent
LazyReviewGate and ConvexReviewGate were untracked but imported by
[slug]/page.tsx and adrs/[slug]/page.tsx. Lazy-loads Convex client
via useEffect to avoid Date.now() during static prerender.
- Add connection() before MDX rendering in /[slug] and /adrs/[slug]
- Wrap AdrListWithFilters in Suspense on /adrs index
- next-mdx-remote uses Date.now() internally, requires dynamic opt-in
auth.isOwner: wrap getAuthUser in try/catch — returns false instead
of throwing when no session token exists.
ReviewGate: treat undefined (loading) same as false — render children
without wrapper to prevent layout shift.
ReviewWrapper: remove -mx-2/px-2/py-0.5 from paragraph hover styles
that caused vertical CLS when wrapper mounted after auth resolved.
Nav: sliding active indicator bar (CSS transition, no framer-motion).
Measures active link position, slides on route change. 200ms
cubic-bezier(0.22, 1, 0.36, 1).
Mobile nav: exit animation mirrors entrance. Links slide out right
with reverse stagger (30ms), overlay fades out 150ms.
Fix transition-all violations (emilkowal props-transform-opacity):
- signin button: explicit property list
- review FAB: explicit + scale(0.97) per spec
- review submit: explicit + motion-reduce
All animations respect prefers-reduced-motion.
useAutoResize hook: measures scrollHeight, transitions height at
150ms with cubic-bezier(0.22, 1, 0.36, 1). Expands as you type,
collapses when content is deleted. Applied to both compose and
edit textareas. Max height 240px then scrolls.
- Plain click: single select (toggle)
- Cmd/Ctrl+click: add/remove from selection
- Shift+click: range select (DOM order)
- Cmd+Shift: range added to existing selection
- Multi-select shows '3 paragraphs' badge in comment header
- Comment prefixed with selection context for agent
- Hint bar shows keyboard shortcuts
Replaces Vaul drawer with portal-based inline editor that renders
directly below the clicked paragraph. Paragraph gets ring highlight
and scrolls into view. Click same paragraph to toggle closed. Esc to dismiss.