Compare commits

...

63 commits

Author SHA1 Message Date
jlightner
26a5d0e0ef fix: crypto.randomUUID fallback for HTTP contexts
Chat page and ChatWidget used crypto.randomUUID() for conversation IDs,
which is only available in secure contexts (HTTPS). On HTTP, this throws
'crypto.randomUUID is not a function'. Added generateUUID() utility with
Math.random-based fallback.
2026-04-05 06:05:17 +00:00
jlightner
96491ac70a fix: LLM config, task time limits, base_url, fallback model, debug Redis caching
- Add BASE_URL setting to config.py, replace hardcoded localhost:8096 in notifications
- Fix LLM_FALLBACK_MODEL default from fyn-llm-agent-chat to qwen2.5:7b
- Fix docker-compose LLM_FALLBACK_MODEL to use env var with correct default
- Add BASE_URL env var to API and worker in docker-compose.yml
- Add soft_time_limit/time_limit to all pipeline stage tasks (prevent stuck workers)
- Cache Redis connection in _is_debug_mode() instead of creating per-call
- Remove duplicate test files in backend/tests/notifications/
2026-04-05 06:01:54 +00:00
jlightner
41b0046361 chore: auto-commit after complete-milestone
GSD-Unit: M025
2026-04-04 15:42:06 +00:00
jlightner
64c0a47816 feat: Created 198-line Newcomer-Guide.md with 8 sections covering full…
- "Newcomer-Guide.md"

GSD-Task: S11/T02
2026-04-04 15:35:39 +00:00
jlightner
e77f8e0fea docs: Updated 10 Forgejo wiki pages with M025 feature documentation — n…
- "Home.md"
- "API-Surface.md"
- "Data-Model.md"
- "Frontend.md"
- "Configuration.md"
- "Chat-Engine.md"
- "Deployment.md"
- "Decisions.md"

GSD-Task: S11/T01
2026-04-04 15:32:48 +00:00
jlightner
4b810c04ea feat: Browser-validated R015, R037, R038, R039, R041 against live ub01:…
- ".gsd/REQUIREMENTS.md"

GSD-Task: S10/T02
2026-04-04 15:22:04 +00:00
jlightner
612cae3491 feat: Added ReadingHeader sticky bar that slides in when scrolling past…
- "frontend/src/components/ReadingHeader.tsx"
- "frontend/src/pages/TechniquePage.tsx"
- "frontend/src/App.css"

GSD-Task: S10/T01
2026-04-04 15:14:05 +00:00
jlightner
4854dad086 feat: Ran manual chat evaluation against live endpoint, documented qual…
- ".gsd/milestones/M025/slices/S09/S09-QUALITY-REPORT.md"
- "backend/pipeline/quality/results/chat_eval_baseline.json"

GSD-Task: S09/T03
2026-04-04 14:50:44 +00:00
jlightner
3cbb614654 test: Rewrote _SYSTEM_PROMPT_TEMPLATE with citation density rules, resp…
- "backend/chat_service.py"

GSD-Task: S09/T02
2026-04-04 14:45:09 +00:00
jlightner
846db2aad5 test: Created chat-specific LLM-as-judge scorer (5 dimensions), SSE-par…
- "backend/pipeline/quality/chat_scorer.py"
- "backend/pipeline/quality/chat_eval.py"
- "backend/pipeline/quality/fixtures/chat_test_suite.yaml"
- "backend/pipeline/quality/__main__.py"

GSD-Task: S09/T01
2026-04-04 14:43:52 +00:00
jlightner
160adc24bf test: Created standalone async load test script that fires concurrent c…
- "scripts/load_test_chat.py"

GSD-Task: S08/T02
2026-04-04 14:33:29 +00:00
jlightner
899ab742a8 test: Added automatic primary→fallback LLM endpoint switching in ChatSe…
- "backend/chat_service.py"
- "backend/tests/test_chat.py"
- "docker-compose.yml"

GSD-Task: S08/T01
2026-04-04 14:31:28 +00:00
jlightner
c42d21a29f feat: Added Export My Data download button to CreatorDashboard with loa…
- "frontend/src/pages/CreatorDashboard.tsx"
- "frontend/src/pages/CreatorDashboard.module.css"
- "frontend/src/api/creator-dashboard.ts"

GSD-Task: S07/T02
2026-04-04 14:19:30 +00:00
jlightner
8b2876906c chore: Added GET /creator/export endpoint that returns a ZIP archive co…
- "backend/routers/creator_dashboard.py"
- "backend/tests/test_export.py"

GSD-Task: S07/T01
2026-04-04 14:16:56 +00:00
jlightner
cfc7e95d28 feat: Wrote NetworkX vs Neo4j benchmark report with production measurem…
- "docs/graph-backend-evaluation.md"

GSD-Task: S06/T01
2026-04-04 14:05:55 +00:00
jlightner
6f3a0cc3d2 feat: Built CreatorTransparency page with four collapsible sections, AP…
- "frontend/src/api/creator-transparency.ts"
- "frontend/src/pages/CreatorTransparency.tsx"
- "frontend/src/pages/CreatorTransparency.module.css"
- "frontend/src/App.tsx"
- "frontend/src/pages/CreatorDashboard.tsx"

GSD-Task: S05/T02
2026-04-04 13:58:33 +00:00
jlightner
b32fc5134b feat: Added GET /creator/transparency endpoint returning technique page…
- "backend/schemas.py"
- "backend/routers/creator_dashboard.py"

GSD-Task: S05/T01
2026-04-04 13:55:13 +00:00
jlightner
be1919e223 feat: Added GET /admin/usage endpoint with today/week/month token aggre…
- "backend/routers/admin.py"
- "frontend/src/api/admin-usage.ts"
- "frontend/src/pages/AdminUsage.tsx"
- "frontend/src/pages/AdminUsage.module.css"
- "frontend/src/App.tsx"
- "frontend/src/components/AdminDropdown.tsx"
- "frontend/src/api/index.ts"

GSD-Task: S04/T02
2026-04-04 13:44:44 +00:00
jlightner
a5d3af55ca feat: Built Redis sliding-window rate limiter, ChatUsageLog model with…
- "backend/rate_limiter.py"
- "backend/models.py"
- "backend/routers/chat.py"
- "backend/chat_service.py"
- "backend/config.py"
- "alembic/versions/031_add_chat_usage_log.py"

GSD-Task: S04/T01
2026-04-04 13:36:29 +00:00
jlightner
d243344ce8 feat: Built 3-step onboarding wizard (Welcome → Consent → Tour), wired…
- "frontend/src/pages/CreatorOnboarding.tsx"
- "frontend/src/pages/CreatorOnboarding.module.css"
- "frontend/src/api/auth.ts"
- "frontend/src/context/AuthContext.tsx"
- "frontend/src/pages/Login.tsx"
- "frontend/src/App.tsx"

GSD-Task: S03/T02
2026-04-04 13:16:58 +00:00
jlightner
51e3e75cf8 feat: Added onboarding_completed flag to User model, UserResponse schem…
- "backend/models.py"
- "backend/schemas.py"
- "backend/routers/auth.py"
- "alembic/versions/030_add_onboarding_completed.py"

GSD-Task: S03/T01
2026-04-04 13:13:05 +00:00
jlightner
4221bae3eb feat: Added ≤768px and ≤400px responsive breakpoints for admin pipeline…
- "frontend/src/App.css"

GSD-Task: S02/T03
2026-04-04 13:04:42 +00:00
jlightner
60b281b9d8 feat: Added ≤400px responsive breakpoints to ConsentDashboard, CreatorS…
- "frontend/src/pages/ConsentDashboard.module.css"
- "frontend/src/pages/CreatorSettings.module.css"
- "frontend/src/pages/Login.module.css"
- "frontend/src/pages/Register.module.css"

GSD-Task: S02/T02
2026-04-04 12:55:34 +00:00
jlightner
b893abead1 feat: Audited all 9 public-facing pages at 375px and 768px viewports —…
- "frontend/src/App.css"

GSD-Task: S02/T01
2026-04-04 12:48:08 +00:00
jlightner
cb3a6c919c test: Added GET/PUT notification preferences endpoints, signed-token un…
- "backend/routers/notifications.py"
- "backend/main.py"
- "backend/tests/notifications/test_notifications.py"
- "frontend/src/api/notifications.ts"
- "frontend/src/pages/CreatorSettings.tsx"

GSD-Task: S01/T03
2026-04-04 12:27:18 +00:00
jlightner
5e4b173917 feat: Built send_digest_emails Celery task with per-user content queryi…
- "backend/tasks/__init__.py"
- "backend/tasks/notifications.py"
- "backend/worker.py"
- "docker-compose.yml"

GSD-Task: S01/T02
2026-04-04 12:15:43 +00:00
jlightner
34a45d1c8e chore: Added SMTP config, User notification_preferences JSONB, EmailDig…
- "backend/config.py"
- "backend/models.py"
- "backend/schemas.py"
- "backend/services/email.py"
- "alembic/versions/029_add_email_digest.py"

GSD-Task: S01/T01
2026-04-04 12:11:13 +00:00
jlightner
0f9e76babd chore: auto-commit after complete-milestone
GSD-Unit: M024
2026-04-04 12:02:25 +00:00
jlightner
a4a6187de2 docs: Updated 7 Forgejo wiki pages with M024 feature documentation cove…
- "Home.md"
- "Data-Model.md"
- "API-Surface.md"
- "Frontend.md"
- "Pipeline.md"
- "Player.md"
- "Chat-Engine.md"

GSD-Task: S06/T01
2026-04-04 11:56:06 +00:00
jlightner
0eecee4271 feat: Extract shared citation parser and formatTime utilities, add time…
- "frontend/src/utils/chatCitations.tsx"
- "frontend/src/utils/formatTime.ts"
- "frontend/src/api/chat.ts"
- "frontend/src/pages/ChatPage.tsx"
- "frontend/src/pages/ChatPage.module.css"
- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/components/ChatWidget.module.css"

GSD-Task: S05/T02
2026-04-04 11:46:00 +00:00
jlightner
f706afe8f6 feat: Added source_video_id, start_time, end_time, and video_filename t…
- "backend/search_service.py"
- "backend/chat_service.py"

GSD-Task: S05/T01
2026-04-04 11:40:59 +00:00
jlightner
a60f4074dc chore: Add GET/PUT shorts-template admin endpoints, collapsible templat…
- "backend/routers/creators.py"
- "backend/schemas.py"
- "frontend/src/api/templates.ts"
- "frontend/src/pages/HighlightQueue.tsx"
- "frontend/src/pages/HighlightQueue.module.css"
- "backend/routers/shorts.py"
- "backend/pipeline/stages.py"
- "frontend/src/api/shorts.ts"

GSD-Task: S04/T03
2026-04-04 11:25:29 +00:00
jlightner
fa493e2640 feat: Built ffmpeg-based card renderer with concat demuxer pipeline and…
- "backend/pipeline/card_renderer.py"
- "backend/pipeline/shorts_generator.py"
- "backend/pipeline/stages.py"
- "backend/models.py"
- "alembic/versions/028_add_shorts_template.py"
- "backend/pipeline/test_card_renderer.py"

GSD-Task: S04/T02
2026-04-04 11:17:38 +00:00
jlightner
125983588d feat: Created ASS subtitle generator with karaoke word-by-word highligh…
- "backend/pipeline/caption_generator.py"
- "backend/pipeline/shorts_generator.py"
- "backend/pipeline/stages.py"
- "backend/models.py"
- "alembic/versions/027_add_captions_enabled.py"
- "backend/pipeline/test_caption_generator.py"

GSD-Task: S04/T01
2026-04-04 11:12:19 +00:00
jlightner
18e9a4dce1 feat: Wired /embed/:videoId route outside AppShell for chrome-free rend…
- "frontend/src/App.tsx"
- "frontend/src/pages/WatchPage.tsx"
- "frontend/src/App.css"

GSD-Task: S03/T02
2026-04-04 10:59:14 +00:00
jlightner
8444fbdb12 feat: Extracted shared copyToClipboard utility and created EmbedPlayer…
- "frontend/src/utils/clipboard.ts"
- "frontend/src/pages/EmbedPlayer.tsx"
- "frontend/src/pages/EmbedPlayer.module.css"
- "frontend/src/pages/ShortPlayer.tsx"

GSD-Task: S03/T01
2026-04-04 10:55:21 +00:00
jlightner
9208b134b6 feat: Added collapsible inline video player to TechniquePage with chapt…
- "frontend/src/pages/TechniquePage.tsx"
- "frontend/src/App.css"

GSD-Task: S02/T02
2026-04-04 10:48:12 +00:00
jlightner
3c99084eb2 feat: Replaced thin 3px line markers with 12px color-coded circle pins,…
- "frontend/src/components/ChapterMarkers.tsx"
- "frontend/src/components/PlayerControls.tsx"
- "frontend/src/App.css"

GSD-Task: S02/T01
2026-04-04 10:44:45 +00:00
jlightner
160b1a8445 feat: Added public ShortPlayer page at /shorts/:token with video playba…
- "frontend/src/pages/ShortPlayer.tsx"
- "frontend/src/pages/ShortPlayer.module.css"
- "frontend/src/api/shorts.ts"
- "frontend/src/App.tsx"
- "frontend/src/pages/HighlightQueue.tsx"
- "frontend/src/pages/HighlightQueue.module.css"

GSD-Task: S01/T02
2026-04-04 10:35:56 +00:00
jlightner
5f4b960dc1 feat: Added share_token column with migration 026, wired token generati…
- "backend/models.py"
- "alembic/versions/026_add_share_token.py"
- "backend/pipeline/stages.py"
- "backend/routers/shorts.py"
- "backend/routers/shorts_public.py"
- "backend/main.py"

GSD-Task: S01/T01
2026-04-04 10:33:00 +00:00
jlightner
2c5d084c49 chore: auto-commit after complete-milestone
GSD-Unit: M023
2026-04-04 10:23:20 +00:00
jlightner
3a5fdad6b3 feat: Updated 8 Forgejo wiki pages with M023 features: post editor, Min…
- "Home.md (wiki)"
- "Data-Model.md (wiki)"
- "API-Surface.md (wiki)"
- "Frontend.md (wiki)"
- "Chat-Engine.md (wiki)"
- "Decisions.md (wiki)"
- "Configuration.md (wiki)"
- "_Sidebar.md (wiki)"

GSD-Task: S05/T01
2026-04-04 10:17:32 +00:00
jlightner
4db33399e9 feat: Added getTierLabel() helper, gradient track fill via --slider-fil…
- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/components/ChatWidget.module.css"

GSD-Task: S04/T02
2026-04-04 10:06:46 +00:00
jlightner
1062e003bf feat: Replaced 3-tier step function with 5-tier continuous interpolatio…
- "backend/chat_service.py"
- "backend/tests/test_chat.py"

GSD-Task: S04/T01
2026-04-04 10:04:47 +00:00
jlightner
84d8dc4455 feat: Added shorts router with generate/list/download endpoints, fronte…
- "backend/routers/shorts.py"
- "backend/main.py"
- "frontend/src/api/shorts.ts"
- "frontend/src/pages/HighlightQueue.tsx"
- "frontend/src/pages/HighlightQueue.module.css"

GSD-Task: S03/T03
2026-04-04 09:52:01 +00:00
jlightner
0007528e77 feat: Added shorts_generator.py with 3 format presets and stage_generat…
- "backend/pipeline/shorts_generator.py"
- "backend/pipeline/stages.py"

GSD-Task: S03/T02
2026-04-04 09:47:40 +00:00
jlightner
dfc5aa2ae7 chore: Added GeneratedShort model with FormatPreset/ShortStatus enums,…
- "backend/models.py"
- "backend/config.py"
- "docker/Dockerfile.api"
- "docker-compose.yml"
- "alembic/versions/025_add_generated_shorts.py"

GSD-Task: S03/T01
2026-04-04 09:43:36 +00:00
jlightner
3bb0266a19 feat: Added personality weight slider (0.0–1.0) to ChatWidget header an…
- "frontend/src/api/chat.ts"
- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/components/ChatWidget.module.css"

GSD-Task: S02/T02
2026-04-04 09:30:56 +00:00
jlightner
d1efdbb3fa feat: Added personality_weight (0.0–1.0) to chat API; modulates system…
- "backend/routers/chat.py"
- "backend/chat_service.py"
- "backend/tests/test_chat.py"

GSD-Task: S02/T01
2026-04-04 09:28:35 +00:00
jlightner
9431aa2095 feat: Added PostsFeed component to creator profile pages with Tiptap HT…
- "frontend/src/components/PostsFeed.tsx"
- "frontend/src/components/PostsFeed.module.css"
- "frontend/src/pages/PostsList.tsx"
- "frontend/src/pages/PostsList.module.css"
- "frontend/src/pages/CreatorDetail.tsx"
- "frontend/src/App.tsx"
- "frontend/src/pages/CreatorDashboard.tsx"

GSD-Task: S01/T04
2026-04-04 09:17:30 +00:00
jlightner
9139d5a93a feat: Built Tiptap rich text post editor with file attachments, multipa…
- "frontend/src/pages/PostEditor.tsx"
- "frontend/src/pages/PostEditor.module.css"
- "frontend/src/api/posts.ts"
- "frontend/src/api/client.ts"
- "frontend/src/App.tsx"
- "frontend/src/pages/CreatorDashboard.tsx"

GSD-Task: S01/T03
2026-04-04 09:13:48 +00:00
jlightner
cc60852ac9 feat: Built post CRUD and file upload/download API routers with auth, o…
- "backend/routers/posts.py"
- "backend/routers/files.py"
- "backend/minio_client.py"
- "backend/auth.py"
- "backend/main.py"

GSD-Task: S01/T02
2026-04-04 09:07:35 +00:00
jlightner
f0f36a3f76 feat: Added MinIO Docker service, Post/PostAttachment models with migra…
- "docker-compose.yml"
- "backend/config.py"
- "backend/minio_client.py"
- "backend/models.py"
- "backend/schemas.py"
- "backend/requirements.txt"
- "docker/nginx.conf"
- "alembic/versions/024_add_posts_and_attachments.py"

GSD-Task: S01/T01
2026-04-04 09:02:40 +00:00
jlightner
758bf7ecea chore: auto-commit after complete-milestone
GSD-Unit: M022
2026-04-04 08:51:47 +00:00
jlightner
66f0f51219 feat: Forgejo wiki updated with 9 files (1 new, 8 modified) covering M0…
- "Personality-Profiles.md"
- "Home.md"
- "Highlights.md"
- "Chat-Engine.md"
- "Data-Model.md"
- "API-Surface.md"
- "Frontend.md"
- "Decisions.md"

GSD-Task: S07/T01
2026-04-04 08:46:09 +00:00
jlightner
eab362d897 feat: Added collapsible PersonalityProfile component to CreatorDetail p…
- "frontend/src/components/PersonalityProfile.tsx"
- "frontend/src/api/creators.ts"
- "frontend/src/pages/CreatorDetail.tsx"
- "frontend/src/App.css"

GSD-Task: S06/T03
2026-04-04 08:31:37 +00:00
jlightner
2d9076ae92 feat: Added personality extraction pipeline: prompt template, 3-tier tr…
- "prompts/personality_extraction.txt"
- "backend/pipeline/stages.py"
- "backend/schemas.py"
- "backend/routers/admin.py"

GSD-Task: S06/T02
2026-04-04 08:28:18 +00:00
jlightner
10cd175333 feat: Added personality_profile JSONB column to Creator model with migr…
- "backend/models.py"
- "backend/schemas.py"
- "backend/routers/creators.py"
- "alembic/versions/023_add_personality_profile.py"

GSD-Task: S06/T01
2026-04-04 08:24:44 +00:00
jlightner
fb6a4cc58a feat: Wired word-timing extraction into stage_highlight_detection — 62…
- "backend/pipeline/stages.py"
- ".gsd/KNOWLEDGE.md"

GSD-Task: S05/T02
2026-04-04 08:11:32 +00:00
jlightner
27c5f4866b test: Added 3 audio proxy scoring functions, extract_word_timings utili…
- "backend/pipeline/highlight_scorer.py"
- "backend/pipeline/highlight_schemas.py"
- "backend/pipeline/test_highlight_scorer.py"

GSD-Task: S05/T01
2026-04-04 08:05:22 +00:00
jlightner
34acf468c6 feat: Updated streamChat() API, ChatWidget, and ChatPage to thread conv…
- "frontend/src/api/chat.ts"
- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/pages/ChatPage.tsx"
- "frontend/src/pages/ChatPage.module.css"

GSD-Task: S04/T02
2026-04-04 07:53:50 +00:00
jlightner
d13d6c3aa1 test: Added multi-turn conversation memory with Redis-backed history (1…
- "backend/chat_service.py"
- "backend/routers/chat.py"
- "backend/tests/test_chat.py"

GSD-Task: S04/T01
2026-04-04 07:50:30 +00:00
jlightner
3226709382 feat: Built floating ChatWidget with streaming responses, citation link…
- "frontend/src/components/ChatWidget.tsx"
- "frontend/src/components/ChatWidget.module.css"
- "frontend/src/pages/CreatorDetail.tsx"

GSD-Task: S03/T01
2026-04-04 07:41:59 +00:00
747 changed files with 103316 additions and 1002 deletions

View file

@ -46,3 +46,9 @@
| D038 | | infrastructure | Primary git remote for chrysopedia | git.xpltd.co (Forgejo) instead of github.com | Consolidating on self-hosted Forgejo instance at git.xpltd.co. Wiki is already there. Single source of truth. | Yes | human |
| D039 | | architecture | LightRAG vs Qdrant search execution strategy | Sequential with fallback — LightRAG first, Qdrant only on LightRAG failure/empty, not parallel | Running both in parallel would double latency overhead. LightRAG is the primary engine; Qdrant is a safety net. Sequential approach reduces load and simplifies result merging. | Yes | agent |
| D040 | M021/S02 | architecture | Creator-scoped retrieval cascade strategy | Sequential 4-tier cascade (creator → domain → global → none) with ll_keywords scoping and post-filtering | Sequential cascade is simpler than parallel-with-priority and avoids wasted LightRAG calls when early tiers succeed. ll_keywords hints LightRAG's retrieval without hard constraints. Post-filtering on tier 1 ensures strict creator scoping while 3x oversampling compensates for filtering losses. Domain tier uses ≥2 page threshold to avoid noise from sparse creators. | Yes | agent |
| D041 | M022/S05 | architecture | Highlight scorer weight distribution for 10-dimension model | Original 7 dimensions reduced proportionally, new 3 audio proxy dimensions (speech_rate_variance, pause_density, speaking_pace) allocated 0.22 total weight. Audio dims default to 0.5 (neutral) when word_timings unavailable for backward compatibility. | Audio proxy signals derived from word-level timing data provide meaningful highlight quality indicators without requiring raw audio analysis (librosa). Neutral fallback ensures existing scoring paths are unaffected. | Yes | agent |
| D042 | M023/S01 | architecture | Rich text editor for creator posts | Tiptap (headless, React) with StarterKit + Link + Placeholder extensions. Store Tiptap JSON as canonical format in JSONB column, render client-side via @tiptap/html. | Headless architecture fits dark theme customization. Large ecosystem, well-maintained. JSON storage is lossless and enables future server-side rendering. No HTML sanitization needed since canonical format is structured JSON. | Yes | agent |
| D043 | M023/S02 | architecture | Personality weight → system prompt modulation strategy | 3-tier intensity (<0.4 subtle reference, 0.4-0.8 adopt voice, 0.8 fully embody) with temperature scaling 0.30.5 linear on weight | Stepped intensity prevents jarring persona at low weights while allowing full creator voice at high values. Temperature stays in 0.3-0.5 range to keep responses factually grounded even at maximum personality wider ranges risk hallucination in a knowledge-base context. | Yes | agent |
| D044 | M023/S04 | architecture | Personality weight → system prompt modulation strategy (revision) | 5-tier continuous interpolation replacing 3-tier step function. Progressive field inclusion: weight < 0.2 = no personality block; 0.2+ adds basic tone; 0.4+ adds descriptors/explanation approach; 0.6+ adds signature phrases (count scaled with weight); 0.8+ adds full vocabulary/style markers; 0.9+ adds summary paragraph. Temperature scaling unchanged (0.3 + weight * 0.2). | 3-tier step function had jarring transitions at 0.4 and 0.8 boundaries. Continuous interpolation with progressive field inclusion gives finer control encyclopedic responses stay clean at low weights while high weights pull in the full personality profile gradually. The 0.0-0.19 dead zone ensures purely encyclopedic mode remains truly encyclopedic with zero personality artifacts. | Yes | agent |
| D045 | M025/S01 | library | Signed unsubscribe token library for email digests | PyJWT instead of itsdangerous | PyJWT was already a dependency (used for auth tokens). Avoids adding itsdangerous as a new package. JWT exp claim provides built-in expiry handling for the 30-day token validity. | Yes | agent |
| D046 | M025/S10 | feature | Whether to accept current sticky title bar as meeting R041 or implement the specified ReadingHeader | Implement proper ReadingHeader component matching R041 spec | The current sticky title bar is always visible and shows no section tracking — it doesn't match R041's spec (thin bar, appears on scroll-past, shows current section name, slide transition). The infrastructure already exists (activeId, titleBarRef, IntersectionObserver), so the implementation is ~80 lines TSX + CSS. Since M025 is about formal validation, accepting a partial implementation undermines the validation exercise. | Yes | agent |

View file

@ -332,3 +332,75 @@
**Context:** When a route depends on services that require a live database, create a standalone ASGI test client that mocks the DB session at the dependency level rather than using the shared conftest.py client. This avoids PostgreSQL dependency for tests that only need to verify request/response shape and SSE event ordering. The pattern: create a fresh FastAPI app in the test, override the DB dependency, mount the router, and use httpx.AsyncClient with ASGITransport.
**Where:** `backend/tests/test_chat.py` — chat_client fixture
## SourceVideo.transcript_path stores absolute paths
**Context:** The `transcript_path` column on `SourceVideo` stores the full absolute path (e.g., `/data/transcripts/Creator/filename.mp4.json`), not a relative path from the mount point. Code that loads transcript files should use the path directly, not join it with a base directory prefix.
**Fix:** Use `source_video.transcript_path` directly as the file path. Do not `os.path.join("/data/transcripts", transcript_path)` — that produces a double prefix.
## highlight_candidates constraint name mismatch
**Context:** The code in `stages.py` referenced `uq_highlight_candidate_moment` for the ON CONFLICT constraint, but the actual PostgreSQL constraint is named `highlight_candidates_key_moment_id_key`. This was created by Alembic's auto-naming convention rather than an explicit `UniqueConstraint(name=...)` in the model.
**Fix:** Use the actual constraint name `highlight_candidates_key_moment_id_key`. When writing ON CONFLICT upserts, always verify the actual constraint name in the database with `inspect(engine).get_unique_constraints(table_name)` rather than guessing from the model definition.
## Parallel slice execution creates cosmetic integration gaps
**Context:** M022/S01 built the HighlightQueue UI rendering 7 score dimensions. S05 (later in the milestone) expanded the scorer to 10 dimensions. The 3 new dimensions are stored in the DB but the frontend doesn't render them yet.
**Rule:** When slices that produce data (S05 scorer) and slices that consume it (S01 UI) run in the same milestone, the consumer may be built against the pre-expansion schema. Accept this as a cosmetic gap unless it breaks functionality. Track as a follow-up for the next milestone.
## conversation_id threading pattern for SSE-based chat
**Context:** Multi-turn chat requires a stable conversation identifier flowing through: POST body → backend service → Redis key → SSE done event → frontend state. The frontend generates the ID on first message (`crypto.randomUUID()`), then reads it back from the done event for subsequent messages.
**Pattern:** conversation_id is optional on the API (auto-generated if missing for backward compat). Redis key: `chrysopedia:chat:{conversation_id}` with TTL refresh on each interaction. This lets stateless API servers serve any conversation — Redis is the single source of truth for history.
## 3-tier transcript sampling for personality extraction
**Context:** Extracting a creator personality profile from transcripts. Small creators have <5 videos, large creators have 20+. Sampling strategy must adapt to corpus size while ensuring topic diversity.
**Pattern:** 3 tiers: small (≤5 videos: use all), medium (6-15: sample ~8), large (>15: sample ~10). For medium/large, use Redis classification data to group transcripts by topic and sample proportionally, ensuring the profile captures the creator's full range rather than overrepresenting one topic area.
## Tiptap v3 useEditor requires immediatelyRender: false for React 18
**Context:** Tiptap v3's `useEditor` hook by default attempts synchronous rendering on mount, which conflicts with React 18's concurrent features (StrictMode double-mounting, suspense boundaries). This causes hydration mismatches and "flushSync was called from inside a lifecycle method" warnings.
**Fix:** Pass `immediatelyRender: false` in the useEditor config: `useEditor({ immediatelyRender: false, extensions: [...], content: ... })`. The editor still renders on the first paint — this just defers it to be React 18 compatible. No visual difference.
## get_optional_user pattern for public-but-auth-aware endpoints
**Context:** Some API endpoints need to be publicly accessible but provide different behavior for authenticated users (e.g., showing draft posts to the owner). Using the standard `get_current_user` dependency rejects unauthenticated requests with 401.
**Fix:** Create `get_optional_user` using `OAuth2PasswordBearer(auto_error=False)`. When `auto_error=False`, missing/invalid tokens return `None` instead of raising 401. The endpoint receives `Optional[User]` and branches on whether the user is present and matches the resource owner.
## Pass CSS module styles as Record<string, string> to shared utilities
**Context:** When extracting a shared React utility (e.g., `parseChatCitations`) that renders JSX with CSS module class names, the utility needs the calling component's styles object. TypeScript's `CSSModuleClasses` type is structurally incompatible across module boundaries — passing `styles` typed as the specific module's interface causes type errors.
**Fix:** Type the styles parameter as `Record<string, string>`. CSS modules already satisfy this shape at runtime. This allows any component to pass its own CSS module styles to the shared utility without type gymnastics.
## ffmpeg anullsrc for silent audio in concat workflows
**Context:** When concatenating video segments using ffmpeg's concat demuxer, all segments must have matching stream types. Intro/outro card segments generated with lavfi (color + drawtext) have no audio track, while the main clip has audio. The concat fails with stream mismatch errors.
**Fix:** Add a silent audio track to card segments using `-f lavfi -i anullsrc=r=44100:cl=stereo` and map both video and audio streams. Use `-shortest` to ensure the silent audio matches the card duration. This makes all segments codec-compatible for `-c copy` concat.
## ASS karaoke subtitles from word-level timings
**Context:** Whisper provides word-level timestamps in transcripts. Burning subtitles into short video clips requires converting these to a subtitle format that supports word-by-word highlighting.
**Fix:** Generate ASS (Advanced SubStation Alpha) files with `\k` karaoke tags. Each word gets its own Dialogue line with `{\k<centiseconds>}` duration tag. Clip-relative timing is achieved by subtracting `clip_start` from each word's timestamp. The ffmpeg `ass=` video filter renders the subtitles during encoding. This avoids custom player UI for subtitle rendering entirely.
## Fail-open rate limiting for availability-critical endpoints
**Context:** Redis-based rate limiters can fail when Redis is down or slow. For a chat endpoint that's the product's core feature, blocking all users because the rate limiter is unavailable is worse than allowing a few extra requests through.
**Fix:** Wrap all Redis operations (ZADD, ZREMRANGEBYSCORE, ZCARD) in try/except. On any Redis error, log WARNING with context and return "allowed" — the request proceeds unthrottled. This is a deliberate availability-over-strictness tradeoff. Only appropriate for features where temporary rate-limit bypass is acceptable.
## Sentinel div for IntersectionObserver on sticky/fixed elements
**Context:** IntersectionObserver doesn't detect when a sticky or fixed element scrolls past a threshold, because the element never actually leaves the viewport — it stays stuck in place.
**Fix:** Place an invisible sentinel `<div>` at the position where the event should fire (e.g., just below the article title). Observe the sentinel instead of the sticky element. When the sentinel leaves the viewport, the sticky element should appear. Use a callback ref on the sentinel so the observer reconnects if the target element renders conditionally.

View file

@ -4,7 +4,7 @@
## Current State
Twenty-one milestones complete. M021 delivered the intelligence layer: LightRAG is now the primary search engine (Qdrant fallback), creator-scoped retrieval cascade narrows results by creator→domain→global context, a streaming chat engine answers questions with citation deep-links to technique pages, highlight detection v1 scores key moments as shorts candidates, the media player supports audio mode with waveform visualization and chapter markers, and a chapter review UI lets creators manage auto-detected chapters. Impersonation write mode and admin audit log are live. Forgejo wiki at 19 pages. The system is deployed and running on ub01 at `http://ub01:8096`. Forgejo knowledgebase wiki live at `https://git.xpltd.co/xpltdco/chrysopedia/wiki/`.
Twenty-five milestones complete. The platform is production-hardened and demo-ready. Rate limiting (Redis sliding-window, 3 tiers), LLM fallback (primary→Ollama), email digest notifications (Celery Beat), GDPR-style data export, creator onboarding wizard, AI transparency page, mobile responsiveness (all pages pass 375px/768px), chat quality toolkit, and formal requirement validation (37/41 validated, 4 out-of-scope). Forgejo wiki at 21 pages with Newcomer Guide. The system is deployed and running on ub01 at `http://ub01:8096`. Forgejo knowledgebase wiki live at `https://git.xpltd.co/xpltdco/chrysopedia/wiki/`.
### What's Built
@ -56,6 +56,17 @@ Twenty-one milestones complete. M021 delivered the intelligence layer: LightRAG
- **Pipeline admin UI fixes** — Collapse toggle styling, mobile card layout, stage chevrons, filter right-alignment, creator dropdown visibility.
- **Creator profile page** — Hero section (96px avatar, bio, genre pills), social link icons (9 platforms), stats dashboard (technique/video/moment counts), featured technique card with gradient border, enriched technique grid (summary, tags, moment count), inline admin editing (bio + social links), and 480px mobile responsive overrides.
- **Post editor + file sharing** — Tiptap v3 rich text editor with formatting toolbar, drag-and-drop file attachments stored in MinIO (internal-only, presigned URL downloads). Post CRUD API with creator ownership, PostsFeed on creator profiles, PostsList management page.
- **Shorts generation pipeline** — ffmpeg-based clip extraction from approved highlights in 3 format presets (vertical 1080×1920, square 1080×1080, horizontal 1920×1080). GeneratedShort model, Celery task with per-preset independent error handling, MinIO storage, HighlightQueue UI with generate/status/download.
- **5-tier personality interpolation** — Personality slider (0.01.0) threads through API to ChatService. Progressive field inclusion: <0.2 pure encyclopedic, 0.2+ basic tone, 0.4+ descriptors, 0.6+ signature phrases (count scaled), 0.8+ vocabulary markers, 0.9+ summary paragraph. Temperature 0.30.5 linear.
- **Shorts publishing flow** — Public shareable URLs via token-based access (`/shorts/:token`). ShortPlayer page renders video with creator metadata. Share link and embed code copy buttons on HighlightQueue. Public API endpoint resolves share_token with no auth.
- **Auto-captioning** — Whisper-generated ASS karaoke subtitles with per-word `\k` tags burned into shorts via ffmpeg ass filter. Non-blocking: caption failure doesn't block short generation. 17 unit tests.
- **Shorts template system** — Creator-configurable intro/outro cards via JSONB template config on Creator model. ffmpeg lavfi card rendering with concat demuxer pipeline. Admin API and collapsible config panel in HighlightQueue. Silent audio tracks for codec-compatible concat. 28 unit tests.
- **Embeddable player** — Chrome-free player at `/embed/:videoId` rendered outside AppShell. Audio-aware iframe snippet generation (120px audio, 405px video). Copy Embed Code button on WatchPage.
- **Key moment timeline pins** — 12px color-coded circle pins on player seek bar (technique=cyan, settings=amber, reasoning=purple, workflow=green) with active-state highlighting and touch-friendly hit areas. Collapsible inline player on TechniquePage with chapter pins and bibliography seek wiring.
- **Citation timestamp links** — Chat source cards show timestamp badges linking to `/watch/:id?t=N`. Video metadata (source_video_id, start_time, end_time, filename) propagated through search → chat → SSE → frontend. Shared parseChatCitations and formatTime utilities.
- **Creator authentication** — Invite-code registration, JWT login (HS256, 24h expiry), protected routes. User and InviteCode models with Alembic migration 016. FastAPI auth router (register, login, me, update-profile). React AuthContext with localStorage JWT persistence.
- **Creator dashboard shell** — Protected /creator/* routes with sidebar nav (Dashboard, Settings). Profile edit and password change forms. Code-split with React.lazy.
- **Consent infrastructure** — Per-video consent toggles (allow_embed, allow_search, allow_kb, allow_download, allow_remix) with versioned audit trail. VideoConsent and ConsentAuditLog models with Alembic migration 017. 5 API endpoints with ownership verification and admin bypass.
@ -75,6 +86,12 @@ Twenty-one milestones complete. M021 delivered the intelligence layer: LightRAG
- **Audio mode + chapter markers** — WatchPage conditionally renders AudioWaveform (wavesurfer.js) or VideoPlayer. ChapterMarkers overlay tick buttons on seek bar. useMediaSync widened for audio/video polymorphism. Backend stream and chapters endpoints.
- **Chapter review UI** — Creator-facing ChapterReview page at /creator/chapters/:videoId with waveform regions (draggable/resizable), status cycling (draft→approved→hidden), rename, reorder. 4 chapter management API endpoints.
- **Impersonation write mode** — write_mode support on impersonation tokens with ConfirmModal confirmation. ImpersonationBanner shows during sessions. AdminAuditLog page at /admin/audit-log with paginated session history.
- **Highlight review queue** — Creator-scoped HighlightQueue page at /creator/highlights with filter tabs (All/Shorts/Approved/Rejected), score breakdown bars, approve/discard actions, inline trim panel. 4 backend endpoints with ownership verification.
- **Follow system** — CreatorFollow model with idempotent follow/unfollow (INSERT ON CONFLICT DO NOTHING), follower_count on creator detail, follow button on CreatorDetail hero. CreatorTiers page with Free/Pro/Premium cards and Coming Soon modals.
- **Chat widget** — Floating ChatWidget on creator profile pages: fixed-position bubble → slide-up panel, streaming SSE scoped to creator, typing indicator, citation links, suggested questions from technique data.
- **Multi-turn conversation memory** — Redis-backed conversation history with conversation_id threading (API → ChatService → SSE done event → frontend). 10 turn pair cap, 1h TTL. Both ChatWidget and ChatPage support multi-turn.
- **Highlight detection v2** — 10-dimension scoring with 3 audio proxy dimensions (speech_rate_variance, pause_density, speaking_pace) from word-level transcript timing. Neutral fallback (0.5) when word timings unavailable. 62 tests.
- **Personality profile extraction** — Creator.personality_profile JSONB column, 3-tier transcript sampling with topic-diverse selection, LLM extraction with Pydantic validation, admin trigger endpoint, collapsible frontend component.
### Stack
@ -108,3 +125,7 @@ Twenty-one milestones complete. M021 delivered the intelligence layer: LightRAG
| M019 | Foundations — Auth, Consent & LightRAG | ✅ Complete |
| M020 | Core Experiences — Player, Impersonation & Knowledge Routing | ✅ Complete |
| M021 | Intelligence Online — Chat, Chapters & Search Cutover | ✅ Complete |
| M022 | Creator Tools & Personality | ✅ Complete |
| M023 | MVP Integration — Demo Build | ✅ Complete |
| M024 | Polish, Shorts Pipeline & Citations | ✅ Complete |
| M025 | Hardening & Launch Prep | ✅ Complete |

View file

@ -85,9 +85,9 @@
**Primary Owner:** M001/S05
## R015 — 30-Second Retrieval Target
**Status:** active
**Status:** validated
**Description:** A producer mid-session can find a specific technique in under 30 seconds from Alt+Tab to reading the key insight.
**Validation:** Timed test: Alt+Tab → search → read technique → under 30 seconds.
**Validation:** Timed browser test: homepage → search "snare" → click first result → technique page content visible in ~9 seconds (well under 30s target). Verified on live deployment at ub01:8096.
**Primary Owner:** M001/S05
## R016 — Card Hover Animations & Staggered Entrance
@ -220,22 +220,22 @@
**Primary Owner:** M015/S05
## R037 — Landing Page Visual Consistency
**Status:** active
**Status:** validated
**Description:** Homepage has consistent max-width tracks, spacing, border-radius, and no CSS bugs (duplicate .btn rule, border-image killing border-radius). Stats scorecard has animated count-up. Section headings use unified treatment.
**Validation:** Visual comparison at 1280px and 375px shows consistent alignment, spacing, and card radius. No jagged center column. Featured card has rounded corners.
**Validation:** Visual comparison at 1280px: 3-column card alignment, stats scorecard (95 ARTICLES, 26 CREATORS) with count-up animation, consistent spacing. At 375px: single-column, no overflow or breakage. Verified on ub01:8096 via browser screenshots.
**Primary Owner:** M016/S01
**Supporting Slices:** M016/S06
## R038 — Pipeline Admin UI Fixes
**Status:** active
**Status:** validated
**Description:** Pipeline admin page: most-recent run collapse toggle works without flicker, mobile job cards don't show vertical text, stage direction chevrons visible, status filter uses button group (not text input), creator dropdown populates without 422 error.
**Validation:** Collapse toggle on most-recent run works. 375px shows truncated creator names. Chevrons between stages. Filter buttons right-aligned. Creator dropdown populated.
**Validation:** Pipeline admin at ub01:8096/admin/pipeline: collapse toggle on Recent Activity works (click toggles). Text filter shows multiple creators (KOAN Sound, COPYCATT, Current Value, DJ Shortee, DONKONG, Chee). Mobile 375px: no vertical text on cards, single-column layout.
**Primary Owner:** M016/S02
## R039 — Brand Minimum (Favicon, OG Tags, Logo)
**Status:** active
**Status:** validated
**Description:** Site has a favicon, OG meta tags for social sharing previews, and an inline SVG logo next to "Chrysopedia" in the header.
**Validation:** Browser tab shows favicon. Sharing URL produces preview card with title/description/image. Logo visible in header.
**Validation:** DOM: link[rel='icon'] href="/favicon.svg", meta[property='og:title'] content="Chrysopedia", meta[property='og:image'] content="/og-image.png". Logo SVG visible in header. HTTP 200 for /favicon.svg and /og-image.png on ub01:8096.
**Primary Owner:** M016/S03
## R040 — Table of Contents Modernization
@ -245,7 +245,8 @@
**Primary Owner:** M016/S04
## R041 — Sticky Reading Header
**Status:** active
**Status:** validated
**Description:** Thin sticky bar appears when user scrolls past the article title on technique pages. Shows article title (truncated) + current section name. Slides in/out with CSS transition. Works on mobile.
**Validation:** Scroll past title on a long technique page — reading header appears with correct section tracking. Works at 375px.
**Validation:** Navigated to v2 technique page (drop-first-production-copycatt). Scrolled past title: .reading-header--visible class present, showing truncated title. At 375px mobile: reading header renders at full width. CSS transition slide-in/out confirmed.
**Primary Owner:** M016/S05
**Notes:** ReadingHeader only renders on v2 body_sections_format pages (84 of 95 pages). The 11 v1 pages are legacy format from early pipeline runs.

View file

@ -1,36 +1,36 @@
# GSD State
**Active Milestone:** M022: Creator Tools & Personality
**Active Slice:** S02: [A] Follow System + Tier UI (Demo Placeholders)
**Phase:** evaluating-gates
**Active Milestone:** M025: Hardening & Launch Prep
**Active Slice:** None
**Phase:** complete
**Requirements Status:** 0 active · 0 validated · 0 deferred · 0 out of scope
## Milestone Registry
- ✅ **M001:** Chrysopedia Foundation Infrastructure, Pipeline Core, and Skeleton UI
- ✅ **M002:** M002: Chrysopedia Deployment — GitHub, ub01 Docker Stack, and Production Wiring
- ✅ **M003:** M003: Domain + DNS + Per-Stage LLM Model Routing
- ✅ **M004:** M004: UI Polish, Bug Fixes, Technique Page Redesign, and Article Versioning
- ✅ **M005:** M005: Pipeline Dashboard, Technique Page Redesign, Key Moment Cards
- ✅ **M006:** M006: Admin Nav, Pipeline Log Views, Commit SHA, Tag Polish, Topics Redesign, Footer
- ✅ **M007:** M007: Pipeline Transparency, Auto-Ingest, Admin UX Polish, and Mobile Fixes
- ✅ **M008:** M008: Credibility Debt Cleanup — Broken Links, Test Data, Jargon, Empty Metrics
- ✅ **M001:** Chrysopedia Foundation - Infrastructure, Pipeline Core, and Skeleton UI
- ✅ **M002:** M002:
- ✅ **M003:** M003:
- ✅ **M004:** M004:
- ✅ **M005:** M005:
- ✅ **M006:** M006:
- ✅ **M007:** M007:
- ✅ **M008:** M008:
- ✅ **M009:** Homepage & First Impression
- ✅ **M010:** Discovery, Navigation & Visual Identity
- ✅ **M011:** M011: Interaction Polish, Navigation & Accessibility
- ✅ **M012:** M012: Multi-Field Composite Search & Sort Controls
- ✅ **M013:** M013: Prompt Quality Toolkit — LLM Fitness, Scoring, and Automated Optimization
- ✅ **M014:** M014: Multi-Source Technique Pages — Nested Sections, Composition, Citations, and Section Search
- ✅ **M015:** M015: Social Proof, Freshness Signals & Admin UX
- ✅ **M016:** M016: Visual Identity & Reading Experience
- ✅ **M017:** M017: Creator Profile Page — Hero, Stats, Featured Technique & Admin Editing
- ✅ **M018:** M018: Phase 2 Research & Documentation — Site Audit and Forgejo Wiki Bootstrap
- ✅ **M011:** M011:
- ✅ **M012:** M012:
- ✅ **M013:** M013:
- ✅ **M014:** M014:
- ✅ **M015:** M015:
- ✅ **M016:** M016:
- ✅ **M017:** M017:
- ✅ **M018:** M018:
- ✅ **M019:** Foundations — Auth, Consent & LightRAG
- ✅ **M020:** Core Experiences — Player, Impersonation & Knowledge Routing
- ✅ **M021:** Intelligence Online — Chat, Chapters & Search Cutover
- 🔄 **M022:** Creator Tools & Personality
- **M023:** MVP Integration — Demo Build
- **M024:** Polish, Shorts Pipeline & Citations
- **M025:** Hardening & Launch Prep
- **M022:** Creator Tools & Personality
- **M023:** MVP Integration — Demo Build
- **M024:** Polish, Shorts Pipeline & Citations
- **M025:** Hardening & Launch Prep
## Recent Decisions
- None recorded
@ -39,4 +39,4 @@
- None
## Next Action
Evaluate 3 quality gate(s) for S02 before execution.
All milestones complete.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more