[Chunk 1.7.0]: Multi-Stream Synthesis & Dynamic Dashboard - #70
Conversation
- Decoupled monolithic 11-dimension analysis into 3 concurrent parallel streams. - Added database-level segment chunk persistence and auto-stitching in /persist API. - Refactored frontend SynthesisStreamAdapter and Zustand store to dynamically reconstruct and render partial progress. - Resolved TS2353 compilation error by adding monetizationVerdict and setMonetizationVerdict to useSynthesisNucleus store. - Gate status: type-check ✅ | lint ✅ | build ✅
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Reviewer's GuideImplements segmented multi-stream synthesis for YouTube analyses by splitting the 11-dimension UCIS analysis into three parallel worker streams, persisting each partial JSON chunk in a new analysis_chunks table, stitching them into a full UCIS v2.0 payload in the /analyses/persist API, and updating the frontend stream adapter and state store to progressively reconstruct markdown and dashboard state, including monetization verdicts. Sequence diagram for segmented multi-stream synthesis and chunk stitchingsequenceDiagram
actor User
participant useSSEStream
participant Worker as analyze_llm_stream
participant PromptBuilder
participant PersistAPI as analyses_persist_POST
participant Adapter as SupabasePersistenceAdapter
User->>useSSEStream: startAnalysis()
useSSEStream->>useSSEStream: launch 3 runStream(chunkIndex)
par Chunk_1
useSSEStream->>Worker: POST /analyze-llm-stream (chunkIndex=1)
Worker->>PromptBuilder: build(context with chunkIndex=1)
Worker-->>useSSEStream: JSON delta stream (dimensions 1-4)
Worker->>PersistAPI: POST /api/analyses/persist (chunkIndex=1, payload)
PersistAPI->>Adapter: persistAnalysisChunk(analysisId,1,...)
PersistAPI->>Adapter: findAnalysisChunks(analysisId)
and Chunk_2
useSSEStream->>Worker: POST /analyze-llm-stream (chunkIndex=2)
Worker->>PromptBuilder: build(context with chunkIndex=2)
Worker-->>useSSEStream: JSON delta stream (dimensions 5-8)
Worker->>PersistAPI: POST /api/analyses/persist (chunkIndex=2, payload)
PersistAPI->>Adapter: persistAnalysisChunk(analysisId,2,...)
PersistAPI->>Adapter: findAnalysisChunks(analysisId)
and Chunk_3
useSSEStream->>Worker: POST /analyze-llm-stream (chunkIndex=3)
Worker->>PromptBuilder: build(context with chunkIndex=3)
Worker-->>useSSEStream: JSON delta stream (dimensions 9-11)
Worker->>PersistAPI: POST /api/analyses/persist (chunkIndex=3, payload)
PersistAPI->>Adapter: persistAnalysisChunk(analysisId,3,...)
PersistAPI->>Adapter: findAnalysisChunks(analysisId)
end
PersistAPI->>PersistAPI: [if 3 completed chunks]
PersistAPI->>Adapter: updateAnalysisResult(stitchedPayload, stitchedMarkdown)
PersistAPI->>Adapter: persistKnowledgeGraph(stitchedPayload.knowledgeGraph)
PersistAPI-->>Worker: { ok: true, status: 'chunk_saved' }
useSSEStream->>useSSEStream: completeAnalysis() when 3 streams done
Entity relationship diagram for analyses and analysis_chunkserDiagram
analyses {
uuid id PK
uuid user_id
}
analysis_chunks {
uuid id PK
uuid analysis_id FK
int chunk_index
int[] dimensions_covered
jsonb payload
varchar status
}
analyses ||--o{ analysis_chunks : has_many
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Caution Review failedPull request was closed or merged during review WalkthroughThis PR implements a multi-stream chunked analysis system where a single analysis request is split into three concurrent SSE streams, each processing a specific dimension range. Partial results are persisted to a new ChangesMulti-Stream Chunked Analysis Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Updates to Preview Branch (feature/chunk-1.7.0-multi-stream-synthesis) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
There was a problem hiding this comment.
Hey - I've found 4 issues, and left some high level feedback:
- The chunked persistence path hard-codes three chunks and specific indices (1, 2, 3) in both the API stitching logic and the client stream orchestration; consider centralizing the chunk configuration (count and index-to-dimension mapping) so future changes don’t require touching multiple call sites and risk drift.
- In the concurrent SSE flow, when one chunk fails you set error state but allow the other streams to continue; you may want to call
currentController.abort()(or equivalent) on first fatal error to avoid unnecessary work and simplify downstream state handling.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The chunked persistence path hard-codes three chunks and specific indices (1, 2, 3) in both the API stitching logic and the client stream orchestration; consider centralizing the chunk configuration (count and index-to-dimension mapping) so future changes don’t require touching multiple call sites and risk drift.
- In the concurrent SSE flow, when one chunk fails you set error state but allow the other streams to continue; you may want to call `currentController.abort()` (or equivalent) on first fatal error to avoid unnecessary work and simplify downstream state handling.
## Individual Comments
### Comment 1
<location path="web/app/api/analyses/persist/route.ts" line_range="66-75" />
<code_context>
+ let validPayload: any;
</code_context>
<issue_to_address>
**suggestion:** Avoid `any` for `validPayload` and keep the UCIS payload typed, even in chunked mode.
Changing `validPayload` from `UCISPayloadV2 | undefined` to `any` drops your compile‑time guarantees on the payload shape. You can still support the chunked case without `any` by widening the type instead, e.g.
```ts
let validPayload:
| UCISPayloadV2
| { schemaVersion: '2.0'; dimensions: unknown[] }
| undefined;
```
Then narrow based on `isChunk` so downstream calls like `updateAnalysisResult` remain type‑safe.
Suggested implementation:
```typescript
// Validate payload schema before persisting.
let validPayload:
| UCISPayloadV2
| { schemaVersion: '2.0'; dimensions: unknown[] }
| undefined;
if (payload !== undefined && payload !== null) {
const isChunk = chunkIndex !== undefined;
const parseResult = isChunk
? z
.object({
schemaVersion: z.literal('2.0'),
dimensions: z.array(z.unknown()),
})
.safeParse(payload)
: UCISPayloadV2Schema.safeParse(payload);
```
1. Ensure that when `parseResult.success` is true, you assign `validPayload = parseResult.data;` so it gets a correctly inferred type from the union.
2. In any downstream code where `validPayload` is used, narrow by `isChunk` (or by checking `validPayload.schemaVersion === '2.0'` and the presence of `dimensions`) so that calls like `updateAnalysisResult` see a properly typed `UCISPayloadV2` in the non-chunked case.
</issue_to_address>
### Comment 2
<location path="web/app/api/analyses/persist/route.ts" line_range="106-107" />
<code_context>
const isInterrupted = status === 'interrupted';
+ // === CHUNKED PERSISTENCE PATH ===
+ if (chunkIndex !== undefined) {
+ const dimensionsCovered = Array.isArray((payload as any)?.dimensions)
+ ? (payload as any).dimensions.map((d: any) => d.number)
+ : [];
</code_context>
<issue_to_address>
**issue (bug_risk):** Stricter validation of `chunkIndex` and chunk count would reduce stitching edge cases.
Right now any non-undefined `chunkIndex` is accepted, and stitching completes when `completedChunks.length === 3`, without ensuring those chunks are exactly indices 1–3 and each appears only once. This allows malformed or retried requests to produce duplicate or missing segments. Please validate `typeof chunkIndex === 'number' && [1, 2, 3].includes(chunkIndex)` on input, and at stitch time only proceed if `chunkMap` contains keys `{1, 2, 3}` with no duplicates.
</issue_to_address>
### Comment 3
<location path="web/app/api/analyses/persist/route.ts" line_range="135-141" />
<code_context>
+ const p2 = chunkMap.get(2) || {};
+ const p3 = chunkMap.get(3) || {};
+
+ const stitchedDimensions = [
+ ...(p1.dimensions || []),
+ ...(p2.dimensions || []),
+ ...(p3.dimensions || [])
+ ].sort((a, b: any) => a.number - b.number);
+
+ const stitchedPayload: UCISPayloadV2 = {
+ schemaVersion: '2.0',
+ persona: p1.persona || {
</code_context>
<issue_to_address>
**issue:** Guard against unexpected or non-numeric `dimension.number` values when stitching.
`stitchedDimensions` is assembled from `p1/2/3.dimensions` and sorted by `a.number - b.number`. Because the chunk schema uses `z.any()`, a dimension may lack a numeric `number`, leading to `NaN` comparisons and undefined ordering. Consider filtering or coercing before sort, e.g. `filter(d => d && typeof d.number === 'number')`, and optionally logging or rejecting invalid entries.
</issue_to_address>
### Comment 4
<location path="docs/history/HANDOVER_REPORT_2026_06_13_1200.md" line_range="55" />
<code_context>
+## 3. Handover Advice
+* **Active Branch Status**: Currently on the `main` branch. The working tree is clean (apart from some untracked assets).
+* **Continuation Recommendation**: **No further action is required at this time.** The workspace compiles, builds, and runs cleanly.
+* **Next Steps for incoming agent**: Check the output of the active ledger task `Conducting connectivity and performance benchmark for Analysis/Chat/Reasoning LLM cascades` to see if there are any latency optimization recommendations for `cascade.ts`.
</code_context>
<issue_to_address>
**suggestion (typo):** Add "the" before "incoming agent" for smoother grammar.
Please update the heading text to "**Next Steps for the incoming agent**" for more natural wording.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| let validPayload: any; | ||
| if (payload !== undefined && payload !== null) { | ||
| const parseResult = UCISPayloadV2Schema.safeParse(payload); | ||
| const isChunk = chunkIndex !== undefined; | ||
| const parseResult = isChunk | ||
| ? z.object({ schemaVersion: z.literal('2.0'), dimensions: z.array(z.any()) }).safeParse(payload) | ||
| : UCISPayloadV2Schema.safeParse(payload); | ||
|
|
||
| if (!parseResult.success) { | ||
| console.warn('[analyses/persist] Invalid payload schema', { analysisId, videoId, errors: parseResult.error.flatten() }); | ||
| console.warn('[analyses/persist] Invalid payload schema', { | ||
| analysisId, |
There was a problem hiding this comment.
suggestion: Avoid any for validPayload and keep the UCIS payload typed, even in chunked mode.
Changing validPayload from UCISPayloadV2 | undefined to any drops your compile‑time guarantees on the payload shape. You can still support the chunked case without any by widening the type instead, e.g.
let validPayload:
| UCISPayloadV2
| { schemaVersion: '2.0'; dimensions: unknown[] }
| undefined;Then narrow based on isChunk so downstream calls like updateAnalysisResult remain type‑safe.
Suggested implementation:
// Validate payload schema before persisting.
let validPayload:
| UCISPayloadV2
| { schemaVersion: '2.0'; dimensions: unknown[] }
| undefined;
if (payload !== undefined && payload !== null) {
const isChunk = chunkIndex !== undefined;
const parseResult = isChunk
? z
.object({
schemaVersion: z.literal('2.0'),
dimensions: z.array(z.unknown()),
})
.safeParse(payload)
: UCISPayloadV2Schema.safeParse(payload);- Ensure that when
parseResult.successis true, you assignvalidPayload = parseResult.data;so it gets a correctly inferred type from the union. - In any downstream code where
validPayloadis used, narrow byisChunk(or by checkingvalidPayload.schemaVersion === '2.0'and the presence ofdimensions) so that calls likeupdateAnalysisResultsee a properly typedUCISPayloadV2in the non-chunked case.
| if (chunkIndex !== undefined) { | ||
| const dimensionsCovered = Array.isArray((payload as any)?.dimensions) |
There was a problem hiding this comment.
issue (bug_risk): Stricter validation of chunkIndex and chunk count would reduce stitching edge cases.
Right now any non-undefined chunkIndex is accepted, and stitching completes when completedChunks.length === 3, without ensuring those chunks are exactly indices 1–3 and each appears only once. This allows malformed or retried requests to produce duplicate or missing segments. Please validate typeof chunkIndex === 'number' && [1, 2, 3].includes(chunkIndex) on input, and at stitch time only proceed if chunkMap contains keys {1, 2, 3} with no duplicates.
| const stitchedDimensions = [ | ||
| ...(p1.dimensions || []), | ||
| ...(p2.dimensions || []), | ||
| ...(p3.dimensions || []) | ||
| ].sort((a, b: any) => a.number - b.number); | ||
|
|
||
| const stitchedPayload: UCISPayloadV2 = { |
There was a problem hiding this comment.
issue: Guard against unexpected or non-numeric dimension.number values when stitching.
stitchedDimensions is assembled from p1/2/3.dimensions and sorted by a.number - b.number. Because the chunk schema uses z.any(), a dimension may lack a numeric number, leading to NaN comparisons and undefined ordering. Consider filtering or coercing before sort, e.g. filter(d => d && typeof d.number === 'number'), and optionally logging or rejecting invalid entries.
| ## 3. Handover Advice | ||
| * **Active Branch Status**: Currently on the `main` branch. The working tree is clean (apart from some untracked assets). | ||
| * **Continuation Recommendation**: **No further action is required at this time.** The workspace compiles, builds, and runs cleanly. | ||
| * **Next Steps for incoming agent**: Check the output of the active ledger task `Conducting connectivity and performance benchmark for Analysis/Chat/Reasoning LLM cascades` to see if there are any latency optimization recommendations for `cascade.ts`. |
There was a problem hiding this comment.
suggestion (typo): Add "the" before "incoming agent" for smoother grammar.
Please update the heading text to "Next Steps for the incoming agent" for more natural wording.
|
✅ Pipeline Status: All checks passed. Ready to merge. |
…, 16 moderate, 1 low) Queried the Dependabot API directly (gh api repos/.../dependabot/alerts) for exact vulnerable-version ranges and first_patched_version per alert, rather than guessing at bump targets from severity labels alone. - next: 16.2.6 -> 16.2.11 (web/package.json). Single patch-level bump resolves all 16 alerts (#73-90) at once -- SSRF in Server Actions/rewrites, Middleware/Turbopack bypass, DoS in Server Actions and Image Optimization SVGs, cache confusion on invalid-UTF8 request bodies, unauthenticated Server Function endpoint disclosure, unbounded Server Action payload in Edge runtime. Same minor line (16.2.x), no breaking changes -- verified via full build + 861-test suite, not assumed safe from the version number alone. - tar: ^7.5.16 -> ^7.5.19 (pnpm-workspace.yaml override). Resolves 4 cascading CVEs at once (#62 moderate through #64 critical -- decompression/parse DoS via unlimited input, negative-entry-size infinite loop, PAX numeric path type confusion, NUL-byte DoS). - js-yaml: ^4.1.2 -> ^4.3.0 (override). #61: merge-key chains forcing quadratic CPU consumption. - fast-uri: new override ^3.1.4 (was resolving to 3.1.2, no prior override entry). #69, #72: host confusion via failed IDN canonicalization and via a literal backslash authority delimiter. - sharp: new override ^0.35.0 (transitive, no direct dependency anywhere in the workspace). #71: inherited libvips CVEs (2026-33327/33328/35590/35591). - hono: new override ^4.12.27 (root workspace only -- video-pipelines own lockfile already resolved 4.12.31, already patched, no action needed there). #66-68: XSS via cx() escaping bypass, jsx context leaking across requests, API Gateway v1 adapter dropping repeated header values. - dompurify: ^3.4.11 -> ^3.4.12 (override, was one patch version short). #70: CUSTOM_ELEMENT_HANDLING bypasses afterSanitizeElements. - @hono/node-server: ^1.13.7 -> ^2.0.5 (video-pipeline/package.json). #91-92: Windows path traversal in serve-static via encoded backslash (%5C) -- these two were NET NEW from tonights video-pipeline addition, not pre-existing debt. A major version bump (1.x->2.x) for a service that only calls serve({fetch, port}) minimally -- verified beyond typecheck: full Docker build (npm audit inside the build reported "found 0 vulnerabilities"), then ran the container and confirmed /health and the auth-gate 401 both still work correctly post-bump. Duplicate-key fix along the way: pnpm-workspace.yaml already had tar and js-yaml override entries further down the file that my first pass missed, causing two separate "duplicated mapping key" YAML errors on `pnpm install` -- caught immediately (install refuses to run on invalid YAML, not a silent failure) and consolidated onto the pre-existing entries instead of leaving two competing pins for the same package. Verified E2E, not just typecheck, given the scope of what changed: - web: type-check clean, lint clean (0/0), production build clean, full vitest suite 861 passed / 16 pre-existing skips / 0 failed. - worker: typecheck clean, esbuild clean (2.1mb, no size regression). - video-pipeline: typecheck clean, Docker build clean (0 vulnerabilities per npm audit inside the image), container run-and-curl verified /health (all 3 binaries report healthy) and the 401 auth gate, matching pre-bump behavior exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
) - Decoupled monolithic 11-dimension analysis into 3 concurrent parallel streams. - Added database-level segment chunk persistence and auto-stitching in /persist API. - Refactored frontend SynthesisStreamAdapter and Zustand store to dynamically reconstruct and render partial progress. - Resolved TS2353 compilation error by adding monetizationVerdict and setMonetizationVerdict to useSynthesisNucleus store. - Gate status: type-check ✅ | lint ✅ | build ✅ Co-authored-by: Kelly Bakri <noreply@github.com>
…, 16 moderate, 1 low) Queried the Dependabot API directly (gh api repos/.../dependabot/alerts) for exact vulnerable-version ranges and first_patched_version per alert, rather than guessing at bump targets from severity labels alone. - next: 16.2.6 -> 16.2.11 (web/package.json). Single patch-level bump resolves all 16 alerts (#73-90) at once -- SSRF in Server Actions/rewrites, Middleware/Turbopack bypass, DoS in Server Actions and Image Optimization SVGs, cache confusion on invalid-UTF8 request bodies, unauthenticated Server Function endpoint disclosure, unbounded Server Action payload in Edge runtime. Same minor line (16.2.x), no breaking changes -- verified via full build + 861-test suite, not assumed safe from the version number alone. - tar: ^7.5.16 -> ^7.5.19 (pnpm-workspace.yaml override). Resolves 4 cascading CVEs at once (#62 moderate through #64 critical -- decompression/parse DoS via unlimited input, negative-entry-size infinite loop, PAX numeric path type confusion, NUL-byte DoS). - js-yaml: ^4.1.2 -> ^4.3.0 (override). #61: merge-key chains forcing quadratic CPU consumption. - fast-uri: new override ^3.1.4 (was resolving to 3.1.2, no prior override entry). #69, #72: host confusion via failed IDN canonicalization and via a literal backslash authority delimiter. - sharp: new override ^0.35.0 (transitive, no direct dependency anywhere in the workspace). #71: inherited libvips CVEs (2026-33327/33328/35590/35591). - hono: new override ^4.12.27 (root workspace only -- video-pipelines own lockfile already resolved 4.12.31, already patched, no action needed there). #66-68: XSS via cx() escaping bypass, jsx context leaking across requests, API Gateway v1 adapter dropping repeated header values. - dompurify: ^3.4.11 -> ^3.4.12 (override, was one patch version short). #70: CUSTOM_ELEMENT_HANDLING bypasses afterSanitizeElements. - @hono/node-server: ^1.13.7 -> ^2.0.5 (video-pipeline/package.json). #91-92: Windows path traversal in serve-static via encoded backslash (%5C) -- these two were NET NEW from tonights video-pipeline addition, not pre-existing debt. A major version bump (1.x->2.x) for a service that only calls serve({fetch, port}) minimally -- verified beyond typecheck: full Docker build (npm audit inside the build reported "found 0 vulnerabilities"), then ran the container and confirmed /health and the auth-gate 401 both still work correctly post-bump. Duplicate-key fix along the way: pnpm-workspace.yaml already had tar and js-yaml override entries further down the file that my first pass missed, causing two separate "duplicated mapping key" YAML errors on `pnpm install` -- caught immediately (install refuses to run on invalid YAML, not a silent failure) and consolidated onto the pre-existing entries instead of leaving two competing pins for the same package. Verified E2E, not just typecheck, given the scope of what changed: - web: type-check clean, lint clean (0/0), production build clean, full vitest suite 861 passed / 16 pre-existing skips / 0 failed. - worker: typecheck clean, esbuild clean (2.1mb, no size regression). - video-pipeline: typecheck clean, Docker build clean (0 vulnerabilities per npm audit inside the image), container run-and-curl verified /health (all 3 binaries report healthy) and the 401 auth gate, matching pre-bump behavior exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Segmented Multi-Stream Synthesis & Dynamic Dashboard (Chunk 1.7.0)
Summary
This PR implements the Segmented Multi-Stream Synthesis pattern (ADR 005) to address LLM token ceiling limits and Serverless execution limits. It decouples the monolithic 11-dimension YouTube transcript analysis into 3 parallel concurrent requests, introduces database-level segment chunk persistence via
public.analysis_chunks, performs auto-stitching in the/persistAPI upon completion of all chunks, and refactors the frontend stream adapter and Zustand store to dynamically reconstruct and render partial progress.Changes
Database & Migrations
supabase/migrations/20260613133000_add_analysis_chunks_table.sqlcreating theanalysis_chunkstable with unique constraint on(analysis_id, chunk_index).Domain, Ports, and Adapters (DDD / DI Alignment)
PersistencePortandSupabasePersistenceAdapterto supportpersistAnalysisChunkandfindAnalysisChunks.web/lib/utils/markdown-reconstructor.tsfor clean markdown layout rebuilding from structured JSON payload on both client and server.Backend Routing & Worker
web/app/api/analyses/persist/route.tsto support segmented chunk persistence, schema-based JSON validation, entity/relationship parsing, and auto-stitching.worker/src/worker.ts,PromptBuilder.ts,ReasoningEnginePort.ts) to receive and process explicit chunk segments.Client-Side Integration & State
web/hooks/useSSEStream.tsto initiate three concurrent streams in parallel.web/lib/adapters/synthesis-stream-adapter.tsto progressively collect, heal, and reconstruct structured JSON segments.monetizationVerdictstate properties andsetMonetizationVerdictaction touseSynthesisNucleusstore to fix compile-time type verification warnings.Gate Status
pnpm type-checkpassed with 0 errors.pnpm lintpassed with 0 errors (warnings only).pnpm buildpassed and bundled successfully.Verification & Architecture
Summary by Sourcery
Implement segmented multi-stream analysis with chunked persistence and dynamic frontend reconstruction for YouTube transcript analyses.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
Summary by cubic
Splits the 11-dimension analysis into three parallel streams, persists each chunk, and auto-stitches the final result to bypass token and serverless limits. The UI now streams partial results and rebuilds clean markdown in real time.
New Features
public.analysis_chunks; auto-stitching in/api/analyses/persistafter all 3 complete with schema checks and KG writes.reconstructMarkdown.monetizationVerdictand setter; completion fires only when all streams finish.chunkIndexthreaded through request, engine, and persist calls.Migration
supabase/migrations/20260613133000_add_analysis_chunks_table.sql.Written for commit 9c7ceab. Summary will update on new commits.
Summary by CodeRabbit
New Features
Chores