Refactor/huge simplify - #415
Merged
Merged
Conversation
…e_feed_page_v1 Delete the legacy multi-request feed fallback (listEpisodeFeedPaged, its column projection, and the isMissingFeedRpcError/NODE_ENV=test seams); GET /episodes now always reads from listHydratedEpisodeFeedPage, which throws on a real RPC failure instead of silently degrading. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…adations The record_episode_video_visual_failure_diagnostics, mark_episode_video_completion_notified, mark_episode_video_visual_failure_notified, and retry-RPC migrations are confirmed applied in prod, so the "missing migration" degradation branches (isMissingSupabaseRpc, the telegram 'unavailable' outcome/copy, isTelegramRetryMigrationMissing) are no longer reachable and are removed along with their rollout-compatibility tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s env keys Storyboard scene-splitting always uses the deterministic provider now; VIDEO_STORYBOARD_PROVIDER and NVIDIA_STORYBOARD_MODEL are dropped from the manifest and both env files (NVIDIA_API_KEY/NVIDIA_BASE_URL stay, used by scene-alignment). Removed the nvidia storyboard test suite and the 'unsupported provider' branch it guarded in episode-video-visual-processor.test.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…cape renderer Only podcast-slide-video.v4 manifests exist now: manifest.ts drops the legacy landscape/v1/v2/v3 schemas, asset kinds (none, bundledMap), and the fullBleed license gate; renderer.ts, ffmpeg-video.ts, templates.tsx, satori-stage.ts, sharp-stage.ts, and rasterizer.ts drop the static slide-video render path (buildStaticSlideFilter, renderStaticSlideVideo, rasterizeSlide, the legacy Cover/PhotoFact/Statistic/Document/SourceQuote templates, and the bundled US-states map asset) since every render now goes through the vertical pipeline. subtitles.ts's portraitSubtitleLayoutFor no longer branches on a 1080x1920 legacy clip size, since that shape can no longer parse. Tests are updated to match: legacy-manifest and static-path fixtures/tests are removed, and the handful of tests that exercised the same behavior through the vertical path (renderer.test.ts, ffmpeg-video-v8.test.ts, manifest.test.ts's contiguous-slide/asset-source/caption-bounds/schema cases) are re-pointed at vertical fixtures instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
remoteImageAssetSchema's `motion` field is now required (every completed v10 visual payload already carries it), so ffmpeg-video.ts no longer needs a legacy branch: legacyKenBurnsFilter, editorialMotionOf, and the kenBurnsFilter dispatcher are deleted, editorialKenBurnsFilter is renamed to stillFrameFilter (the only still-frame path left), imagePreparationFilter always takes the contain/pad branch, and transitionForSlide always alternates smoothright/smoothleft instead of falling back to a crossfade. kenBurnsSeedForEpisode/kenBurnsPanForScene/KEN_BURNS_MOTIONS stay, since the editorial drift filter still uses them for pan direction. ffmpeg-video.test.ts drops the legacy-path and legacy-zoom-formula tests and adds `motion` to its manifest fixture; manifest.test.ts, renderer.test.ts, episode-video.test.ts, episode-video-processor.test.ts, assets.test.ts, and storyboard.test.ts each gain `motion` on their remoteImage fixtures to satisfy the now-required field. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
These process/CLI entry points are already package.json scripts, which knip discovers on its own; only the two `node --import` preload targets need an explicit entry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n frame math escapeFilterPath was byte-identical in ffmpeg-video.ts and runtime-preflight.ts; moved it to a shared src/lib/ffmpeg-filter-path.ts. The still-frame and editorial-drift filters in ffmpeg-video.ts also computed the same duration-in-frames expression independently; both now call one slideDurationFrames helper. Pre-existing jscpd clones (predating this sequence's Phase A1 commits), surfaced by the phase's aggregate dup:check gate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Concept-card copy now reads its OpenRouter completion cost through the shared completionMetadata() helper (usage.cost), the same path every other LLM ledger line uses, instead of a bespoke completion.costUsd field the client never populates. Unreported cost now records as 0 instead of null, matching every other ledger line; slide-copy.test.ts fixtures and assertions move to usage.cost accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
LANGUAGE_CLASSROOM_LANGUAGE_CODES is now a re-export of SUPPORTED_PRIMARY_LANGUAGE_CODES (kept as a distinct name via a knip @alias tag for an upcoming cross-package swap), and the single isLanguageClassroomLanguageCode guard now lives in classroom-language.ts. video-jobs.ts and video-completion-notifier.ts both import it instead of each rolling their own membership check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tIntEnv Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d key reads Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace sentry.ts's local trimToUndefined with lib/typeGuards.ts's nonemptyString, and supabase-client.ts's local readOptionalString with lib/string.ts's readNullableString (error-message formatting only). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
render-capacity's selectRows and loadOptionalVisualFailureNotices now throw through the shared throwSupabaseError helper instead of a local supabaseErrorMessage formatter, restoring the supabaseError property isTransientNetworkError walks. The message-less-error fallback text in render-capacity.test.ts changes accordingly to match formatSupabaseError's output. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…l loops Extract the single-flight, immediate-first-run, unref'd setInterval lifecycle shared by render-capacity's reconciler and the two Telegram reap-and-notify sweepers (video completion, video visual failure) into src/lib/polling-sweeper.ts (createPollingSweeper, plus createSweepNotifier for the two notifiers' start/sweep/stop shape). render-capacity.ts keeps its own runOnce export and its start/stop logging; the two notifiers collapse to a few lines each. Deleted the old jscpd:ignore pairs that wrapped the duplicated timer state machine in both notifier files; a much smaller jscpd:ignore remains around the still-parallel default-notify/logger/interval wiring in each factory, since that thin adapter code is genuinely irreducible between the two notifiers' differing notify/logger types. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tatus guard src/services/video-progress.ts is now the single source for the public video-generation status whitelist and the completed-video asset check. db.ts, episode-feed-page.ts, and video-status.ts each delegate to it instead of repeating the guard and the URL/thumbnail/duration validation; the episode-feed-page.ts jscpd:ignore block covering that duplication is removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the `[video-worker] event k=v ...` formatter into src/services/video/log.ts (logVideoWorkerEvent), and have the render processor, the visual processor's logVisualProgress wrapper, and the worker's own lifecycle log lines call it instead of each formatting the line inline. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…effort call Extract buildTelegramWarning(title, detail, footer) for the three title/reason/footer Telegram warning messages, and bestEffortTelegramCall(operation, fn) for the identical log-and-Sentry-capture wrapper shared by sendTelegramNotification and answerTelegramCallbackQuery. Output text is byte-identical; covered by the existing telegram.test.ts assertions with no test changes needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…o lib Move src/services/video/abort.ts (and its test) to src/lib/abort.ts and update every importer's path. Convert the narration-download deadline in episode-video-processor.ts and the remote-image download deadline in assets.ts to the shared runWithDeadline helper; leave the render deadline in episode-video-processor.ts and the OpenRouter request deadline in llm.ts on their existing combineAbortSignalWithTimeout pattern, since both need their own finally/catch handling around the race. Updated the narration-download timeout test to match runWithDeadline's message phrasing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the settle-once/abort-with-grace-period pattern shared by ffmpeg-video.ts's runProcess and rasterizer.ts's runRasterStage into src/lib/spawn-process.ts (settleOnce, killOnAbort), dropping the jscpd:ignore pair in ffmpeg-video.ts. rasterizer.ts keeps its own `typeof child.kill !== 'function'` guard for test doubles that stub only the EventEmitter surface. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r for the CLIs
Move src/social/is-main-module.ts to src/lib/is-main-module.ts and adopt it
across every dev-CLI entry point, replacing each file's own
process.argv[1]/import.meta.url comparison (including two buggy
`file://${process.argv[1]}` forms in slide-preview-cli.ts and
review/cli.ts). Each try/catch wrapper now goes through the shared
lib/cli-runner.ts runCli(), which now logs the full error (keeping the
stack) instead of just its message, since this is dev-CLI-only output.
video/cli.ts and storyboard/smoke-cli.ts now build their flag parsing on
lib/cli-args.ts's parseFlagArgs, with a new assertOnlyKnownFlags() helper
(and its own test) factored out to keep the two parsers' shared
positional/unknown-flag validation from re-duplicating.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…peline)
Add src/services/supabase-rows.ts (many/maybeOne/expectNoError) and use it to
replace the repeated `{ data, error } = await query; if (error) throw...`
boilerplate in db.ts, video-jobs.ts, ingest-jobs.ts, telegram-commands.ts,
video/review/review-store.ts, and render-capacity.ts (selectRows -> many).
No test files changed; existing mocks already return {data,error} thenables.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the repeated `{ data, error } = await query; if (error) throw...`
boilerplate in src/social/daemon-store.ts and release-cohort-store.ts with
the shared many()/maybeOne()/expectNoError() helpers from
services/supabase-rows.ts, including daemon-store.ts's local
unwrapCandidates() (now redundant with many()). affectedSocialPublishJobRow
(the CAS upsert/update helper) is left untouched, and
claimReleaseCohortJobs' response.data/response.error RPC unwrap is left as
is. No test files changed; existing mocks already return {data,error}
thenables.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…snapshot compat shape daemon-store.ts drops latestPendingSocialPublishSchedule, listPastDueSocialPublishJobs/PastDueSocialPublishJob, rescheduleSocialPublishJob, and claimSocialPublishBatch (the daemon claims through claimReleaseCohortJobs instead), none of which any production caller reached. getSocialQueueSnapshot's queue snapshot drops the nextByPlatform compat field and the withQueueLanes non-enumerable-property trick in favor of a plain object exposing nextByLane/waitingVideos/laneCount/lanes directly; daemon.ts's queue logging reads those fields without the old fallback. Touches fixture shape only (nextByPlatform -> nextByLane, added laneCount/lanes/waitingVideos) in the guarded daemon-release-cohort-contract.test.ts, daemon.test.ts, and daemon-store-queue-snapshot-timing.test.ts; deletes daemon-store-latest-pending-schedule.test.ts and the corresponding cases in daemon-store.test.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tput recoveryEpisodes was threaded from planPendingSocialReleaseCohorts through alignPendingSocialReleaseCohorts's return value, but daemon.ts never reads it (only alignedLanes/rescheduledEpisodes drive the log line). Removed the field from ReleaseCohortPlan and ReleaseCohortAlignmentResult and updated the fixtures/assertions that mirrored its shape, including the guarded release-cohort-store.test.ts (fixture-shape only: two toEqual assertions dropped the recoveryEpisodes key, no new behavior asserted). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Route the daemon's platform/language log lines through log-format.js's platformLabel/languageLabel (account-snapshots.ts, rolling-metrics.ts, daemon.ts) instead of composing icon+name inline, and extract the four identical cohort-not-ready log blocks in daemon.ts into logCohortNotReady(). Add a shared jst.ts (JST_OFFSET_MS) used by slot-policy.ts, language-allocation.ts, release-cohort-plan.ts, and daemon.ts's formatJst; export metric-collectors.ts's EMPTY_COUNTS for daemon.ts's unavailable-metric write. Output is byte-identical to before. daemon-metric-terminal.test.ts's metric-collectors.js factory mock gains EMPTY_COUNTS (fixture-shape only) so the new import resolves under mock. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add publishStep(platform) in publish-error.ts and reuse it as each publisher's per-step SocialPublishError wrapper (threads/x/rednote's identical local helpers, and youtube's four try/catch blocks). Add requireVideoPath(platform, input) in publishers.ts to replace the three duplicated "select video path or throw" blocks, preserving each platform's exact error message. createSocialPublishJobs drops its now-unnecessary async, so its one caller (publish-batch.ts) and publishers.test.ts's direct-throw assertions were updated from promise-rejection to synchronous-throw form to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lpers; drop the threads-auth jscpd exclusion Extract the Threads/YouTube OAuth session read-parse-or-invalid and atomic-write logic into a new src/social/session-file.ts (readJsonSessionFile, writeJsonSessionFileAtomically), and move the shared *AuthOptions fields and authorization-URL query params into src/social/oauth-loopback.ts (OAuthLoopbackAuthOptions, AuthorizationUrlInput, applyAuthorizationParams). Drop the **/social/threads-auth.ts jscpd exclusion now that jscpd reports zero clones between threads-auth.ts and youtube-auth.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… post matching Extract the shared nearest-timestamp-index scan used by inspectXPublishedPostAt (X timeline) and closestRednoteCardToTimestamp (Rednote manager cards) into one closestIndexByTimestamp helper. Both call sites keep their own error messages and thresholds unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the shared Chrome-launch call (system Chrome channel, fixed 1440x900 viewport, per-call headless) out of x-playwright.ts, rednote-browser.ts, and metric-collectors.ts into a new src/social/browser.ts, so each platform still owns its own profile/navigation lifecycle but no longer repeats the launch options. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the shared episode/video/teaser-asset preparation out of cli.ts's loadSocialAssets and publish-batch.ts's publishSocialBatch into a new src/social/prepare-batch-assets.ts, so the CLI's progress log lines and the daemon's already-prepared-asset short-circuit both route through one function instead of two near-identical copies. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
podcastPlayer.web.ts now returns createPodcastPlayerSnapshot(...) like the native player, instead of hand-building the PodcastPlayer object (including hasPreviousEpisode/hasNextEpisode and the finiteSeconds wrapping the helper already does). Dropped the now-unused hasNextPodcastEpisode/ hasPreviousPodcastEpisode imports. Tried lifting the surrounding jscpd:ignore block entirely; it exposed several pre-existing native/web structural duplicates well beyond the useState twin (cancelPendingHandoff, playEpisodeSection, skipToSection, etc.), so the ignore stays at its original scope with only the return body's content changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the repeated Sentry.ErrorBoundary + QueryClientProvider wrapping in AppProviderShell into a module-level ProviderChrome component instead of duplicating the same JSX across the missing-Privy-config, wallet, and non-wallet return branches. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the sign-in prompt shell (ScreenScrollView + padded ConnectGateCard) into components/connect/ConnectGatePage.tsx and use it from both AuthenticatedRoute.tsx and AuthenticatedRoute.ios.tsx; the web-only AccountUnavailableCard branch is untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Share the AuthenticatedActionContextValue interface from integration/authenticatedActionModel.ts between the web/native and iOS providers, and have the iOS hook return a module-level PASSTHROUGH_ACTION constant instead of recreating callbacks on every render. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the duplicated net_portfolio_value/total_net_usd fallback from useHomeData and usePortfolioData into portfolioMetrics.netPortfolioValueFrom. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
toggle and playFromQueue shared an identical auth+gating block; both now route through a single startAudio(episode, run) helper, letting the jscpd:ignore markers around the duplicated block come off (0 clones). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
useInvest's amountUsd now reuses amountUsdFromInput from investAmountModel instead of re-parsing the grouped input inline. The four draft setters that only clear the frozen single-chain execution (amount, destination, base and Arbitrum funding tokens) now go through a shared withFreezeClear helper; setScope keeps its bespoke body since it also resets destination. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add formatOr(value, format, fallback='—') to lib/format.ts and reuse it for the "no data yet" pattern in useDefaultStrategyBacktest, usePortfolioData's numeric metric formatter, HlpPlanSummary's usd6/address labels, DecisionPacketCard's evidence number formatter, and simulationPreviewModel's address/integer formatters. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract AssetRow/AssetListSkeleton, PartialWalletWarning, PortfolioImportState, StrategyStatusCard, and ActionButton (renamed HomeActionButton) into components/home/, leaving HomeScreen.tsx as composition + data wiring. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ChainKey, DemoAsset, MetricTone, ActivityKind/Status/Step, ActivityCategoryDelta/Flow, ActivityWalletRef, ActivityEvent, ActivityGroup, and ActivityFilter move to integration/activityTypes.ts; data/demo.ts keeps only the DEMO fixture and its DemoData container type, importing the moved types where it needs them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Collapse pillarsFromTarget/allocationFromTarget and their empty-state
twins into one compositionRows(target, demoRows, isDemo, {valueKey,
round}) helper backed by a shared COMPOSITION_ROWS table, and drop the
usePortfolioData allocationColor palette (only ever called for
'Stables'). Strategy/Portfolio/AllocationBar now render
tokens.color.pillar.* / tokens.color.accent directly instead of a
'var(--x)' string resolved at render time, so lib/colors.ts and
resolveColor are unused and removed.
data/demo.ts now imports design-tokens, so tests/HomeScreen.test.tsx
defers its @/data/demo import the same way it already defers the
harness import (a static import ran the mocked
@zapengine/design-tokens/tokens factory before its own `harness`
binding initialized); tests/support/homeScreenHarness.tsx's
design-tokens mock gains the pillar swatch fixture-shape it was
missing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Activity groups now carry a stable bucket code ('today'/'week'/'earlier')
instead of a display string, moving Today/This week/Earlier translation to
the screen via activity.group.* keys. Deletes the now-orphaned
date.today/thisWeek/earlier dictionary entries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Consolidate the ten identical `return null` iOS screen stubs (Home,
Activity, Portfolio, Send, Strategy, Wallets, HlpProgress, InvestAmount,
InvestProgress, InvestRoute) into a single shared PodcastOnlyStubScreen
in src/screens/podcastOnlyStub.tsx. Each `.ios.tsx` file now re-exports
it under its route's expected name, keeping the file itself in place so
route imports and knip's `.ios.{ts,tsx}` entry pattern still resolve.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Introduce appKeyValueStorage.{web,native,ts} as the single place the
key-value backend forks by platform; localeStorage.ts and
podcastStorage.ts become plain single files built on top of it, and
the four separate .web/.native store files are deleted. Extract the
duplicated write-serialization queue from podcastStorageCore.ts and
localeStorageCore.ts into a shared createSerializedWriter helper in
keyValueStorage.ts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wrap estimateTranscriptTiming's per-render call in useMemo, keyed on the episode script and active clock duration, so segments are only recomputed when those inputs change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Consolidate EpisodeMediaPlayer's six mutable video-clock refs (time, duration, playing, rate, last-persisted time, failure-handled) into a single useRef<VideoClockState>. Memoize the media-tab availability, classroom sections, video panel state, and video progress derivations on the specific episode fields they read (hlsUrl, languageCode, audioTracks, video, videoGeneration) instead of the whole episode object, since mergeEpisodeProgress spreads a new episode object on every position/listened update and would otherwise force a recompute on every playback tick. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wire already-translated podcast.* keys into EpisodeMediaPlayer and EpisodeDetailScreen (Video/Transcript/Language classroom/Back/playback speed a11y/no-classroom/no-transcript/episode-unavailable), align podcast.videoReady/videoUnavailable copy with what the UI already shows, and reuse podcast.listened/podcast.unheard for the episode hero badge instead of its own ad-hoc "Listened"/"Unplayed" strings. Add a needsVerification entry to HomeScreen's PORTFOLIO_IMPORT_COPY (home.etlNeedsVerificationTitle/Body, en values unchanged from the prior inline strings, plus JA/zh-Hant) so the ownership-not-verified branch goes through the same t()/copy table as the other ETL states. Delete the 52 orphaned keys (common/podcast/home/strategy/portfolio partials plus all of wallets.*/send.*/connect.*) from all three dictionaries now that nothing references them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PodcastPlayerProvider now exposes a status context (everything except currentTime/duration) alongside a clock context, so PodcastScreen (which never reads the running position) subscribes only to the former and stops re-rendering on every playback tick. usePodcastPlayer() keeps merging both for existing consumers (EpisodeDetailScreen, PodcastProgressTracker) that need the ticking clock. PodcastScreen isolates the one clock read behind a small NowPlayingBarConnected wrapper so the now-playing bar still updates live without pulling the rest of the screen along with it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the shared queue-mutations/reduce-onto-stored-value/single-post-hydration-write pattern out of usePodcastSpeedPreferences and PodcastProgressProvider into one hook, useHydratedStore. useEpisodeSortDirection and ContentLanguageProvider write immediately instead of merging, so they keep their own shape and are left untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The status memo's ~21-field object literal and its dependency array were token-identical, tripping the repo's zero-tolerance jscpd gate. Split into a data half and an actions half (mirroring this file's existing status/clock split), combined by a two-dependency memo — same fine-grained per-field dependency tracking as before, just no single list long enough to clone against itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the shared podcast language vocabulary (PODCAST_LANGUAGE_CODES, PodcastLanguageCode, DEFAULT_PODCAST_LANGUAGE_CODE, isPodcastLanguageCode, PODCAST_LANGUAGE_LABELS) and the video-stage vocabulary (PODCAST_VIDEO_VISUAL_STAGES / PODCAST_VIDEO_RENDER_STAGES / PODCAST_VIDEO_PROGRESS_STAGES) to packages/types/src/shared/podcast.ts, a new shared/errors.ts (errorMessage, toError), and a <= 0 fast path plus optional AbortSignal support on shared/async.ts's sleep. All reachable from shared/index.ts. podcast-pipeline, control-center, and the app will consume this in follow-up commits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
types.ts's SUPPORTED_PRIMARY_LANGUAGE_CODES/DEFAULT_LANGUAGE_CODE/PrimaryLanguageCode now re-export @zapengine/types/shared's podcast language vocabulary instead of duplicating the ['zh-Hant','ja','en'] literal; LANGUAGE_CLASSROOM_LANGUAGE_CODES keeps its own distinct alias per the existing @alias note. video-progress.ts's VISUAL_JOB_PROGRESS_STAGES/RENDER_JOB_PROGRESS_STAGES now re-export the shared stage lists (migration-023 CHECK comment and STAGE_SPANS untouched). lib/sleep.ts and lib/errorMessage.ts become thin re-exports of the shared implementations. Remaining literal language-code guards in telegram-commands.ts, social/cli.ts, and social/daemon-store.ts now read SUPPORTED_PRIMARY_LANGUAGE_CODES instead of hardcoding the list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
server/services/podcast-pipeline.ts, pipeline-queues.ts, shared/podcast-pipeline.ts, shared/podcast-visual.ts, and client/components/episode-queue.ts now read @zapengine/types/shared's PODCAST_LANGUAGE_CODES/PodcastLanguageCode instead of duplicating the ['zh-Hant','ja','en'] literal. PodcastUnitEconomics.tsx's language ordering/labels derive from PODCAST_LANGUAGE_LABELS for the three canonical codes, keeping an explicit local 'zh-Hans' for legacy cost rows the shared vocabulary has no notion of. The three private errorMessage copies (app.ts, register-podcast-abandon.ts, operations/signal.ts) now consume @zapengine/types/shared's errorMessage for the Error-instance case; the two route-handler copies with PostgREST duck-typed message extraction are consolidated into one postgrestErrorMessage helper in services/supabase.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
config/contentLanguages.ts's CONTENT_LANGUAGE_OPTIONS now sources its badge/native labels from @zapengine/types/shared's PODCAST_LANGUAGE_LABELS instead of duplicating the literal strings, keeping the app's own English-first display order local. lib/intlDates.ts's CONTENT_LANGUAGE_LOCALES reads each code's intlLocale from the same source. podcastFeed.ts's PODCAST_VIDEO_GENERATION_STAGES/PodcastVideoGenerationStage now re-export the shared PODCAST_VIDEO_PROGRESS_STAGES/PodcastVideoProgressStage stage union, and its PodcastLanguageClassroomKeyword/PodcastLanguageClassroomLesson plain data shapes are re-exported from a new shared/podcast.ts addition (mirroring the pipeline's LanguageClassroomKeyword/LanguageClassroomLesson shape) rather than being redefined locally; the two apps' classroom parsers stay separate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
vercel
Bot
temporarily deployed
to
Preview – zap-engine-control-center
September 8, 2026 01:27
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Context
Scope
Out of scope
Product contract / invariants
Affected invariant:
Why:
Acceptance criteria
Implementation
Contract alignment
AGENTS.mdremains accurate.Validation
...— PASSValidation gaps
Known unrelated failures
Reviewer notes