@thierrynakoa/fire-flow 12.2.1 → 13.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/CREDITS.md +25 -0
  2. package/DOMINION-FLOW-OVERVIEW.md +182 -38
  3. package/README.md +399 -455
  4. package/TROUBLESHOOTING.md +264 -264
  5. package/agents/fire-debugger.md +54 -0
  6. package/agents/fire-executor.md +1610 -1033
  7. package/agents/fire-fact-checker.md +1 -1
  8. package/agents/fire-planner.md +85 -17
  9. package/agents/fire-project-researcher.md +1 -1
  10. package/agents/fire-researcher.md +4 -22
  11. package/agents/{fire-phoenix-analyst.md → fire-resurrection-analyst.md} +394 -394
  12. package/agents/fire-reviewer.md +552 -499
  13. package/agents/fire-verifier.md +114 -19
  14. package/bin/cli.js +18 -101
  15. package/commands/fire-0-orient.md +2 -2
  16. package/commands/fire-1a-new.md +50 -15
  17. package/commands/fire-1c-setup.md +33 -5
  18. package/commands/fire-1d-discuss.md +87 -1
  19. package/commands/fire-2-plan.md +556 -527
  20. package/commands/fire-3-execute.md +2046 -1356
  21. package/commands/fire-4-verify.md +975 -906
  22. package/commands/fire-5-handoff.md +46 -5
  23. package/commands/fire-6-resume.md +2 -31
  24. package/commands/fire-add-new-skill.md +138 -19
  25. package/commands/fire-autonomous.md +14 -2
  26. package/commands/fire-complete-milestone.md +1 -1
  27. package/commands/fire-cost.md +179 -183
  28. package/commands/fire-debug.md +1 -6
  29. package/commands/fire-loop-resume.md +2 -2
  30. package/commands/fire-loop-stop.md +1 -1
  31. package/commands/fire-loop.md +2 -15
  32. package/commands/fire-map-codebase.md +1 -1
  33. package/commands/fire-migrate-database.md +548 -0
  34. package/commands/fire-new-milestone.md +1 -1
  35. package/commands/fire-reflect.md +1 -2
  36. package/commands/fire-research.md +142 -21
  37. package/commands/{fire-phoenix.md → fire-resurrect.md} +859 -603
  38. package/commands/fire-scaffold.md +297 -0
  39. package/commands/fire-search.md +1 -2
  40. package/commands/fire-security-scan.md +483 -484
  41. package/commands/fire-setup.md +359 -0
  42. package/commands/fire-skill.md +770 -0
  43. package/commands/fire-skills-diff.md +506 -506
  44. package/commands/fire-skills-history.md +388 -388
  45. package/commands/fire-skills-rollback.md +7 -7
  46. package/commands/fire-skills-sync.md +470 -470
  47. package/commands/fire-test.md +5 -5
  48. package/commands/fire-todos.md +1 -1
  49. package/commands/fire-update.md +5 -5
  50. package/commands/fire-validate-skills.md +282 -0
  51. package/commands/fire-vuln-scan.md +492 -493
  52. package/hooks/run-hook.sh +8 -8
  53. package/hooks/run-session-end.sh +7 -7
  54. package/hooks/session-end.sh +90 -90
  55. package/hooks/session-start.sh +1 -1
  56. package/package.json +4 -24
  57. package/plugin.json +7 -7
  58. package/references/autonomy-levels.md +235 -0
  59. package/references/behavioral-directives.md +95 -3
  60. package/references/blocker-tracking.md +1 -1
  61. package/references/circuit-breaker.md +93 -2
  62. package/references/context-engineering.md +227 -9
  63. package/references/honesty-protocols.md +70 -1
  64. package/references/issue-to-pr-pipeline.md +149 -150
  65. package/references/metrics-and-trends.md +1 -2
  66. package/references/research-improvements.md +4 -108
  67. package/references/sdlc-mapping.md +73 -0
  68. package/references/state-machine.md +151 -0
  69. package/skills-library/AVAILABLE_TOOLS_REFERENCE.md +333 -0
  70. package/skills-library/SKILLS-INDEX.md +57 -558
  71. package/skills-library/SKILLS_LIBRARY_INDEX.md +532 -0
  72. package/skills-library/_general/api-patterns/api-field-name-mismatch.md +107 -0
  73. package/skills-library/_general/api-patterns/streaming-command-timeout.md +122 -0
  74. package/skills-library/_general/api-patterns/streaming-proxy-cors-bypass.md +102 -0
  75. package/skills-library/_general/automation/settings-gui-generator.md +172 -0
  76. package/skills-library/_general/database-solutions/data-type-mapping-reference.md +181 -0
  77. package/skills-library/_general/database-solutions/mysql-limit-offset-string-coercion.md +102 -0
  78. package/skills-library/_general/database-solutions/mysql-to-pg-migration.md +195 -0
  79. package/skills-library/_general/database-solutions/orm-schema-portability.md +193 -0
  80. package/skills-library/_general/database-solutions/persistent-analysis-storage.md +207 -0
  81. package/skills-library/_general/database-solutions/pg-to-mysql-schema-migration-methodology.md +190 -0
  82. package/skills-library/_general/database-solutions/sql-dialect-compatibility-matrix.md +306 -0
  83. package/skills-library/_general/database-solutions/sqlite-to-pg-migration.md +219 -0
  84. package/skills-library/_general/frontend/canvas-bubble-animation-grouping.md +270 -0
  85. package/skills-library/_general/frontend/color-token-migration.md +112 -0
  86. package/skills-library/_general/frontend/framer-motion-layoutid-grouping.md +150 -0
  87. package/skills-library/_general/frontend/pyqt6-settings-dialog.md +191 -0
  88. package/skills-library/_general/frontend/react-flow-animated-layout-switching.md +101 -0
  89. package/skills-library/_general/frontend/react-hooks-order-debugging.md +141 -0
  90. package/skills-library/_general/frontend/redux-localstorage-auth-desync.md +126 -0
  91. package/skills-library/_general/frontend/safari-csp-theme-color-debugging.md +124 -0
  92. package/skills-library/_general/frontend/safari-sw-cache-poisoning.md +138 -0
  93. package/skills-library/_general/frontend/svg-sparkline-no-charting-library.md +131 -0
  94. package/skills-library/_general/growth-marketing/oss-daily-growth-intelligence.md +224 -0
  95. package/skills-library/_general/integrations/claude-code-local-mcp-integration.md +250 -0
  96. package/skills-library/_general/integrations/mcp-composite-tool-orchestration.md +200 -0
  97. package/skills-library/_general/methodology/AGENT_SDK_STANDALONE_TOOLING.md +181 -0
  98. package/skills-library/_general/methodology/AGENT_TEAMS_GUIDE.md +169 -0
  99. package/skills-library/_general/methodology/ALAS_STATEFUL_EXECUTION.md +207 -0
  100. package/skills-library/_general/methodology/AUTO_REVIEWER_SUBAGENT.md +211 -0
  101. package/skills-library/_general/methodology/CONSISTENCY_CHECK_AMBIGUITY_GATE.md +96 -0
  102. package/skills-library/_general/methodology/DEAD_ENDS_SHELF.md +4 -4
  103. package/skills-library/_general/methodology/DISTILL_NOT_DUMP.md +108 -0
  104. package/skills-library/_general/methodology/EXECUTION_PROGRESS_MONITOR.md +157 -0
  105. package/skills-library/_general/methodology/HIERARCHICAL_REVIEW_MARS.md +122 -0
  106. package/skills-library/_general/methodology/MCP_INTER_AGENT_BRIDGE.md +207 -0
  107. package/skills-library/_general/methodology/MERMAID_WIZARD_DIAGRAMS.md +77 -0
  108. package/skills-library/_general/methodology/MISSING_DIMENSION_DETECTOR.md +89 -0
  109. package/skills-library/_general/methodology/MULTI_AGENT_COORDINATION.md +397 -0
  110. package/skills-library/_general/methodology/OBSERVATION_MASKING.md +100 -0
  111. package/skills-library/_general/methodology/PHOENIX_REBUILD_METHODOLOGY.md +82 -11
  112. package/skills-library/_general/methodology/REVIEW_BACKTRACK_PANEL.md +140 -0
  113. package/skills-library/_general/methodology/REVIEW_FIX_LOOP.md +117 -0
  114. package/skills-library/_general/methodology/VOTING_VERDICT_ARBITRATION.md +155 -0
  115. package/skills-library/_general/methodology/ZERO_FRICTION_CLI_SETUP.md +2 -2
  116. package/skills-library/_general/methodology/dead-code-activation.md +123 -0
  117. package/skills-library/_general/methodology/debug-swarm-researcher-escape-hatch.md +240 -240
  118. package/skills-library/_general/methodology/shell-autonomous-loop-fixplan.md +1 -1
  119. package/skills-library/_general/patterns-standards/GOF_DESIGN_PATTERNS_FOR_AI_AGENTS.md +5 -5
  120. package/skills-library/_general/patterns-standards/cascading-failure-diagnosis.md +119 -0
  121. package/skills-library/_general/patterns-standards/domain-specific-layout-algorithms.md +209 -0
  122. package/skills-library/_general/patterns-standards/python-desktop-app-architecture.md +399 -0
  123. package/skills-library/_general/patterns-standards/realtime-monitoring-dashboard.md +457 -0
  124. package/skills-library/_general/patterns-standards/togglable-processing-pipeline.md +169 -0
  125. package/skills-library/_general/performance/liveclock-extraction.md +112 -0
  126. package/skills-library/_general/performance/ref-based-canvas-animation.md +117 -0
  127. package/skills-library/_general/performance/use-visible-interval.md +131 -0
  128. package/skills-library/_general/testing/playwright-firefox-withcredentials-auth-issue.md +104 -0
  129. package/skills-library/_quarantine/README.md +30 -0
  130. package/skills-library/api-patterns/BROADCAST_SCHEDULER_SHARED_EXECUTE_FUNCTION.md +150 -0
  131. package/skills-library/api-patterns/ERROR_RESPONSE_STANDARDS.md +145 -0
  132. package/skills-library/api-patterns/EXPRESS_ROUTE_ORDERING_MIDDLEWARE_INTERCEPTION.md +326 -0
  133. package/skills-library/api-patterns/PAGINATION_PATTERNS.md +137 -0
  134. package/skills-library/api-patterns/PODCAST_PROGRESS_TRACKING_THREE_ROOT_CAUSES.md +277 -0
  135. package/skills-library/api-patterns/RATE_LIMITING_TOGGLE.md +155 -0
  136. package/skills-library/api-patterns/graphql-content-queries.md +708 -0
  137. package/skills-library/appointment-scheduler-design.md +423 -0
  138. package/skills-library/automation/AUTO_POPULATE_COMPLETE_GUIDE.md +631 -0
  139. package/skills-library/automation/CC_WORKFLOW_STUDIO.md +83 -0
  140. package/skills-library/automation/CLAUDE_CODE_SWARM_MODE.md +95 -0
  141. package/skills-library/automation/DAEMON_TRIGGER_FILE_IPC.md +195 -0
  142. package/skills-library/automation/scheduled-content-publishing.md +608 -0
  143. package/skills-library/awesome-workflows/Blogging-Platform-Instructions/view_commands.md +25 -0
  144. package/skills-library/awesome-workflows/CREDENTIAL-SECURITY-WORKFLOW.md +109 -0
  145. package/skills-library/awesome-workflows/DEBUGGING-WORKFLOW.md +124 -0
  146. package/skills-library/awesome-workflows/Design-Review-Workflow/README.md +31 -0
  147. package/skills-library/awesome-workflows/Design-Review-Workflow/design-principles-example.md +129 -0
  148. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-agent.md +107 -0
  149. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-claude-md-snippet.md +24 -0
  150. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-slash-command.md +38 -0
  151. package/skills-library/awesome-workflows/PARALLEL-RESEARCH-WORKFLOW.md +89 -0
  152. package/skills-library/awesome-workflows/PHASE-EXECUTION-WORKFLOW.md +97 -0
  153. package/skills-library/awesome-workflows/SESSION-HANDOFF-WORKFLOW.md +116 -0
  154. package/skills-library/cms-patterns/content-branch-preview.md +515 -0
  155. package/skills-library/cms-patterns/inline-visual-editing.md +666 -0
  156. package/skills-library/cms-patterns/mdx-component-content.md +649 -0
  157. package/skills-library/cms-patterns/media-manager-abstraction.md +827 -0
  158. package/skills-library/cms-patterns/schema-driven-form-generator.md +838 -0
  159. package/skills-library/complexity-metrics/complexity-divider.md +707 -0
  160. package/skills-library/complexity-metrics/work-with-complexity.md +193 -0
  161. package/skills-library/creative-multimedia/animation-stack-guide.md +577 -0
  162. package/skills-library/creative-multimedia/audio-enhancement-pipeline.md +625 -0
  163. package/skills-library/creative-multimedia/content-repurposing-pipeline.md +1146 -0
  164. package/skills-library/creative-multimedia/data-visualization-generator.md +862 -0
  165. package/skills-library/creative-multimedia/doc-to-podcast-pipeline.md +2184 -0
  166. package/skills-library/creative-multimedia/ffmpeg-command-generator.md +405 -0
  167. package/skills-library/creative-multimedia/image-optimization-pipeline.md +605 -0
  168. package/skills-library/creative-multimedia/multi-format-content-generator.md +1759 -0
  169. package/skills-library/creative-multimedia/og-image-generator.md +635 -0
  170. package/skills-library/creative-multimedia/podcast-audio-composition.md +1355 -0
  171. package/skills-library/creative-multimedia/podcast-quality-evaluation.md +1452 -0
  172. package/skills-library/creative-multimedia/podcast-script-generation.md +1841 -0
  173. package/skills-library/creative-multimedia/svg-generation.md +750 -0
  174. package/skills-library/creative-multimedia/text-to-speech-provider-selector.md +1414 -0
  175. package/skills-library/creative-multimedia/transcription-pipeline-selector.md +677 -0
  176. package/skills-library/creative-multimedia/video-streaming-setup.md +559 -0
  177. package/skills-library/database-solutions/AI_RESPONSE_DATABASE_CACHING.md +520 -0
  178. package/skills-library/database-solutions/CONDITIONAL_SQL_MIGRATION_PATTERN.md +119 -0
  179. package/skills-library/database-solutions/DATABASE_COLUMN_NAME_MISMATCH.md +393 -0
  180. package/skills-library/database-solutions/DATABASE_SCHEMA.md +394 -0
  181. package/skills-library/database-solutions/DATABASE_SCHEMA_VERIFICATION_GUIDE.md +348 -0
  182. package/skills-library/database-solutions/DATABASE_STRATEGY.md +71 -0
  183. package/skills-library/database-solutions/ES_MODULE_SEED_SCRIPT_PATTERN.md +52 -0
  184. package/skills-library/database-solutions/MIGRATION_GUIDE.md +3 -0
  185. package/skills-library/database-solutions/PLPGSQL_VARIABLE_CONFLICT_FIX.md +208 -0
  186. package/skills-library/database-solutions/POSTGRESQL_JSONB_DOUBLE_STRINGIFY_FIX.md +245 -0
  187. package/skills-library/database-solutions/POSTGRESQL_LICENSE_TABLE_DESIGN.md +393 -0
  188. package/skills-library/database-solutions/POSTGRESQL_UUID_DOCUMENT_RAG_DUAL_SCOPE.md +732 -0
  189. package/skills-library/database-solutions/POSTGRES_SQL_TEMPLATE_BINDING_ERROR.md +240 -0
  190. package/skills-library/database-solutions/PRISMA_DB_PUSH_DATA_LOSS_PREVENTION.md +141 -0
  191. package/skills-library/database-solutions/PRODUCTION_QUERY_OPTIMIZATION_RESTART_FIX.md +389 -0
  192. package/skills-library/database-solutions/RLS_SECURITY_GUIDE.md +107 -0
  193. package/skills-library/database-solutions/SCHEMA_ENHANCEMENTS_GUIDE.md +373 -0
  194. package/skills-library/database-solutions/SCHEMA_MIGRATION_GUIDE.md +368 -0
  195. package/skills-library/database-solutions/SCHEMA_VERIFICATION_QUICK_REFERENCE.md +104 -0
  196. package/skills-library/database-solutions/ai-erd-generator.md +1213 -0
  197. package/skills-library/database-solutions/content-publishing-states.md +631 -0
  198. package/skills-library/database-solutions/database-schema-designer.md +522 -0
  199. package/skills-library/database-solutions/er-diagram-components.md +569 -0
  200. package/skills-library/database-solutions/er-to-ddl-mapping.md +1405 -0
  201. package/skills-library/database-solutions/erd-creator-textbook-research.md +433 -0
  202. package/skills-library/database-solutions/erd-react-flow-architecture.md +1965 -0
  203. package/skills-library/database-solutions/mariadb-aggregate-function-replacement.md +145 -0
  204. package/skills-library/database-solutions/normalization-validator.md +778 -0
  205. package/skills-library/database-solutions/postgres-full-text-search-content.md +494 -0
  206. package/skills-library/database-solutions/postgresql-to-mysql-runtime-translation.md +286 -0
  207. package/skills-library/database-solutions/regex-alternation-ordering-sql-types.md +92 -0
  208. package/skills-library/database-solutions/reserved-word-context-aware-quoting.md +142 -0
  209. package/skills-library/database-solutions/sql-ddl-generator.md +756 -0
  210. package/skills-library/database-solutions/supabase-connection-pooler-fix.md +102 -0
  211. package/skills-library/deployment-security/CPANEL_NODE_DEPLOYMENT.md +166 -0
  212. package/skills-library/deployment-security/DEPLOYMENT.md +275 -0
  213. package/skills-library/deployment-security/DEPLOYMENT_CHECKLIST.md +363 -0
  214. package/skills-library/deployment-security/DEPLOYMENT_PLAN.md +669 -0
  215. package/skills-library/deployment-security/KNEX_DATABASE_ABSTRACTION.md +444 -0
  216. package/skills-library/deployment-security/LICENSE_KEY_SYSTEM.md +206 -0
  217. package/skills-library/deployment-security/NODE18_DEPENDENCY_COMPATIBILITY.md +284 -0
  218. package/skills-library/deployment-security/PHP_INSTALLER_WIZARD_GUIDE.md +315 -0
  219. package/skills-library/deployment-security/PM2_ENVIRONMENT_VARIABLE_CACHING.md +256 -0
  220. package/skills-library/deployment-security/PM2_MEMORY_EXHAUSTION_FIX.md +370 -0
  221. package/skills-library/deployment-security/PRODUCTION_DEPLOYMENT_GUIDE.md +592 -0
  222. package/skills-library/deployment-security/PRODUCTION_HARDENING_DOCUMENTATION.md +307 -0
  223. package/skills-library/deployment-security/PRODUCTION_RECOVERY_CHERRY_PICK_PATTERN.md +202 -0
  224. package/skills-library/deployment-security/PYINSTALLER_CUDA_WHISPER_BUNDLING.md +236 -0
  225. package/skills-library/deployment-security/SECURITY.md +41 -0
  226. package/skills-library/deployment-security/SMTP_SSL_HOSTNAME_MISMATCH_SHARED_HOSTING.md +220 -0
  227. package/skills-library/deployment-security/SPA_SEO_OPTIMIZATION_CPANEL.md +200 -0
  228. package/skills-library/deployment-security/SUPABASE_EDGE_FUNCTIONS.md +338 -0
  229. package/skills-library/deployment-security/VERCEL_GITHUB_DEPLOYMENT_GUIDE.md +858 -0
  230. package/skills-library/deployment-security/VPS_DEPLOYMENT_READINESS.md +356 -0
  231. package/skills-library/deployment-security/deployment-changes-not-applying.md +241 -0
  232. package/skills-library/deployment-security/env-file-management-production-local.md +203 -0
  233. package/skills-library/deployment-security/express-secure-file-downloads.md +413 -0
  234. package/skills-library/deployment-security/react-production-deployment-desktop-guide.md +2011 -0
  235. package/skills-library/deployment-security/self-hosted-supabase-coolify-guide.md +1684 -0
  236. package/skills-library/deployment-security/unique-features-ai-strategy-plaid-security.md +1613 -0
  237. package/skills-library/deployment-security/vps-deployment.md +135 -0
  238. package/skills-library/document-processing/WORD_EXPORT_MARKDOWN_FORMATTING.md +482 -0
  239. package/skills-library/document-processing/document-ai-landingai-integration.md +677 -0
  240. package/skills-library/document-processing/express-secure-file-downloads-mern.md +413 -0
  241. package/skills-library/document-processing/express-secure-file-downloads.md +413 -0
  242. package/skills-library/document-processing/md-to-word-converter.md +318 -0
  243. package/skills-library/document-processing/pdf-forms-integration/README.md +101 -0
  244. package/skills-library/document-processing/pdf-forms-integration/SKILL.md +662 -0
  245. package/skills-library/ecommerce/ADMIN_PRODUCTS_GUIDE.md +428 -0
  246. package/skills-library/ecommerce/ECOMMERCE_API_REFERENCE.md +776 -0
  247. package/skills-library/ecommerce/ECOMMERCE_COMPLETION_SUMMARY.md +673 -0
  248. package/skills-library/ecommerce/ECOMMERCE_IMPLEMENTATION_GUIDE.md +729 -0
  249. package/skills-library/ecommerce/ECOMMERCE_QUICK_REFERENCE.md +521 -0
  250. package/skills-library/ecommerce/ECOMMERCE_TESTING_CHECKLIST.md +565 -0
  251. package/skills-library/ecommerce/ECOMMERCE_WORKFLOW_GUIDE.md +1059 -0
  252. package/skills-library/ecommerce/PRODUCT_CREATION_EXPANDED.md +522 -0
  253. package/skills-library/ecommerce/agentic-commerce-protocol.md +203 -0
  254. package/skills-library/ecommerce/cart-abandonment-recovery.md +236 -0
  255. package/skills-library/ecommerce/cart-architecture-patterns.md +300 -0
  256. package/skills-library/ecommerce/cart-item-count-indicator.md +264 -0
  257. package/skills-library/ecommerce/checkout-ux-conversion.md +227 -0
  258. package/skills-library/ecommerce/composable-commerce-selection.md +166 -0
  259. package/skills-library/ecommerce/ecommerce-analytics-patterns.md +167 -0
  260. package/skills-library/ecommerce/fraud-detection-patterns.md +179 -0
  261. package/skills-library/ecommerce/inventory-stock-management.md +270 -0
  262. package/skills-library/ecommerce/order-saga-state-machine.md +336 -0
  263. package/skills-library/ecommerce/payment-provider-abstraction.md +245 -0
  264. package/skills-library/ecommerce/pci-compliance-checklist.md +192 -0
  265. package/skills-library/ecommerce/refund-chargeback-handling.md +177 -0
  266. package/skills-library/ecommerce/shipping-carrier-integration.md +218 -0
  267. package/skills-library/ecommerce/webhook-idempotency-patterns.md +253 -0
  268. package/skills-library/excalidraw-diagrams/.github/workflows/ci.yml +558 -0
  269. package/skills-library/excalidraw-diagrams/.github/workflows/prompt-gallery.yml +448 -0
  270. package/skills-library/excalidraw-diagrams/.github/workflows/release.yml +42 -0
  271. package/skills-library/excalidraw-diagrams/.github/workflows/test-reusable-ci.yml +25 -0
  272. package/skills-library/excalidraw-diagrams/CLAUDE.md +57 -0
  273. package/skills-library/excalidraw-diagrams/LICENSE +21 -0
  274. package/skills-library/excalidraw-diagrams/README.md +178 -0
  275. package/skills-library/excalidraw-diagrams/SKILL.md +715 -0
  276. package/skills-library/form-solutions/BUTTON_TYPE_FORM_SUBMISSION.md +336 -0
  277. package/skills-library/form-solutions/FILLABLE_PDF_IMPLEMENTATION.md +226 -0
  278. package/skills-library/form-solutions/SURVEYJS_QUESTIONNAIRE_SYSTEM.md +367 -0
  279. package/skills-library/form-solutions/tiptap-minimal-setup.md +690 -0
  280. package/skills-library/frontend/scholarly-classification-bubble-map.md +149 -0
  281. package/skills-library/infrastructure/ci-cd-pipeline-builder.md +517 -0
  282. package/skills-library/infrastructure/observability-designer.md +264 -0
  283. package/skills-library/infrastructure/performance-profiler.md +621 -0
  284. package/skills-library/installer-wizard-patterns.md +249 -0
  285. package/skills-library/integrations/CLAUDE_CODE_TOKEN_ANALYTICS.md +160 -0
  286. package/skills-library/integrations/CONFIGURABLE_AI_PROVIDER_SELECTION.md +728 -0
  287. package/skills-library/integrations/SOCKET_IO_BROADCAST_ALL_VS_ROOM.md +141 -0
  288. package/skills-library/integrations/VIRTUAL_MEETINGS_IMPLEMENTATION.md +374 -0
  289. package/skills-library/integrations/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
  290. package/skills-library/integrations/YOUTUBE_API_SETUP.md +141 -0
  291. package/skills-library/integrations/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
  292. package/skills-library/integrations/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
  293. package/skills-library/integrations/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
  294. package/skills-library/integrations/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
  295. package/skills-library/integrations/ai-ml/GEMINI_AI_RAG_PIPELINE_COMPLETE_GUIDE.md +195 -0
  296. package/skills-library/integrations/ai-ml/GEMINI_IMAGE_GENERATION_SETUP.md +64 -0
  297. package/skills-library/integrations/cloudflare/cloudflare-turnstile-debugging.md +202 -0
  298. package/skills-library/integrations/cloudflare/cloudflare-turnstile-implementation.md +476 -0
  299. package/skills-library/integrations/cloudflare-turnstile-debugging.md +202 -0
  300. package/skills-library/integrations/cloudflare-turnstile-implementation.md +476 -0
  301. package/skills-library/integrations/ghost-creator-monetization-pattern.md +454 -0
  302. package/skills-library/integrations/headless-cms-architecture.md +484 -0
  303. package/skills-library/integrations/headless-cms-stack-selection.md +183 -0
  304. package/skills-library/integrations/payload-cms-patterns.md +674 -0
  305. package/skills-library/integrations/realtimestt-openwakeword-cuda-windows.md +229 -0
  306. package/skills-library/integrations/rss-podcast-integration.md +300 -0
  307. package/skills-library/integrations/wordpress/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
  308. package/skills-library/integrations/youtube/YOUTUBE_API_SETUP.md +141 -0
  309. package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
  310. package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
  311. package/skills-library/integrations/youtube/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
  312. package/skills-library/integrations/youtube/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
  313. package/skills-library/marketing/campaign-analytics.md +97 -0
  314. package/skills-library/marketing/content-creator.md +105 -0
  315. package/skills-library/marketing/marketing-strategy-pmm.md +94 -0
  316. package/skills-library/marketing/social-media-analyzer.md +81 -0
  317. package/skills-library/methodology/ADVANCED_ORCHESTRATION_PATTERNS.md +401 -0
  318. package/skills-library/methodology/AGENT_SELF_IMPROVEMENT_LOOP.md +179 -0
  319. package/skills-library/methodology/BREATH_BASED_PARALLEL_EXECUTION.md +1 -1
  320. package/skills-library/methodology/CLEANSING_CYCLE.md +358 -0
  321. package/skills-library/methodology/CONFIDENCE_ANNOTATION_PATTERN.md +143 -0
  322. package/skills-library/methodology/CRITICAL_PATTERNS_DOCUMENTATION_COMPLETE.md +204 -0
  323. package/skills-library/methodology/DELIVERABLES_SUMMARY.md +341 -0
  324. package/skills-library/methodology/DIFFICULTY_AWARE_AGENT_ROUTING.md +252 -0
  325. package/skills-library/methodology/EVOLUTIONARY_SKILL_SYNTHESIS.md +219 -0
  326. package/skills-library/methodology/GLOMERULUS_DECISION_GATE.md +223 -0
  327. package/skills-library/methodology/HIBERNATION_SYSTEM.md +231 -0
  328. package/skills-library/methodology/INSTRUMENTATION_OVER_RESTRICTION.md +192 -0
  329. package/skills-library/methodology/MASTER_COMPLETION_SUMMARY.md +444 -0
  330. package/skills-library/methodology/MASTER_SESSION_COMPLETION.md +743 -0
  331. package/skills-library/methodology/MERN_QUICK_REFERENCE.md +358 -0
  332. package/skills-library/methodology/ORGAN_AGENT_MAPPING.md +177 -0
  333. package/skills-library/methodology/PARALLEL_WAVE_BASED_REFACTORING.md +440 -0
  334. package/skills-library/methodology/QUICK_REFERENCE.md +358 -0
  335. package/skills-library/methodology/SDFT_ONPOLICY_SELF_DISTILLATION.md +186 -0
  336. package/skills-library/methodology/SELF_QUESTIONING_TASK_GENERATION.md +270 -0
  337. package/skills-library/methodology/SESSION_COMPLETION_SUMMARY.md +304 -0
  338. package/skills-library/methodology/SESSION_SUMMARY.md +432 -0
  339. package/skills-library/methodology/WARRIOR_WORKFLOW_DEBUGGING_PROTOCOL.md +252 -0
  340. package/skills-library/methodology/tech-debt-tracker.md +570 -0
  341. package/skills-library/parallel-debug/SKILL.md +60 -0
  342. package/skills-library/patterns-standards/API_PATTERN_FIX_SUMMARY.md +236 -0
  343. package/skills-library/patterns-standards/BATCH_OPERATIONS_WITH_PROGRESS_MODAL.md +362 -0
  344. package/skills-library/patterns-standards/CRITICAL_CODING_PATTERNS.md +639 -0
  345. package/skills-library/patterns-standards/DARK_MODE_MODAL_VISIBILITY.md +258 -0
  346. package/skills-library/patterns-standards/ERROR_RESILIENCE_IMPLEMENTATION.md +375 -0
  347. package/skills-library/patterns-standards/ES_MODULE_IMPORT_HOISTING_DOTENV.md +298 -0
  348. package/skills-library/patterns-standards/NESTED_BACKDROP_FILTER_CSS_ARTIFACT_FIX.md +76 -0
  349. package/skills-library/patterns-standards/ORDERED_DETECTOR_PIPELINE_GRACEFUL_FALLBACK.md +333 -0
  350. package/skills-library/patterns-standards/PHASE_IMPORT_ERROR_DEBUGGING.md +271 -0
  351. package/skills-library/patterns-standards/PYNPUT_GLOBAL_HOTKEY_VK_MATCHING.md +252 -0
  352. package/skills-library/patterns-standards/REACT_USEEFFECT_CASCADE_RESET_FIX.md +132 -0
  353. package/skills-library/patterns-standards/SUBMENU_HOVER_DROPDOWN_PATTERN.md +225 -0
  354. package/skills-library/patterns-standards/TAILWIND_TEXT_VISIBILITY_OVERRIDE.md +322 -0
  355. package/skills-library/patterns-standards/THEME_AWARE_CSS_VARIABLES_PATTERN.md +209 -0
  356. package/skills-library/patterns-standards/THEME_USER_OBJECT_PROPERTY_NAMING.md +194 -0
  357. package/skills-library/patterns-standards/TOOLTIP_BLOCKING_CLICKS_FIX.md +267 -0
  358. package/skills-library/patterns-standards/claude-code-plugin-structure.md +235 -0
  359. package/skills-library/patterns-standards/react-i18next-setup.md +429 -0
  360. package/skills-library/patterns-standards/thesys-c1-generative-ui-integration.md +967 -0
  361. package/skills-library/plugin-development/CLAUDE_CODE_COMMAND_REGISTRATION_SILENT_FAILURE.md +315 -0
  362. package/skills-library/plugin-development/plugin-command-namespace-vs-global.md +390 -0
  363. package/skills-library/plugin-development/plugin-doc-auto-generation.md +172 -0
  364. package/skills-library/security/GITHUB_REPO_SECURITY_AUDIT.md +115 -0
  365. package/skills-library/security/admin-deletion-safety.md +396 -0
  366. package/skills-library/security/application-vuln-patterns.md +477 -0
  367. package/skills-library/security/env-secrets-manager.md +686 -0
  368. package/skills-library/security/secure-ai-application-templates.md +347 -0
  369. package/skills-library/security/sql-injection-prevention-postgresjs.md +151 -0
  370. package/skills-library/supabase-connection-pooler-fix.md +102 -0
  371. package/skills-library/system-context/POWERSHELL_BASH_INTEROP.md +82 -0
  372. package/skills-library/system-context/SERVICE_LIFECYCLE_MANAGEMENT.md +119 -0
  373. package/skills-library/system-context/SKILL.md +40 -0
  374. package/skills-library/system-context/WINDOWS_DEV_ENVIRONMENT.md +73 -0
  375. package/skills-library/testing/E2E_PLAYWRIGHT_PATTERNS.md +99 -0
  376. package/skills-library/testing/INTEGRATION_TEST_STRATEGY.md +82 -0
  377. package/skills-library/testing/RED_GREEN_BUGFIX_GATE.md +203 -0
  378. package/skills-library/testing/TEST_DATA_MANAGEMENT.md +69 -0
  379. package/skills-library/testing/VITEST_UNIT_TEST_PATTERNS.md +75 -0
  380. package/skills-library/testing/playwright-api-security-tests.md +202 -0
  381. package/skills-library/toolbox/SKILL.md +84 -0
  382. package/skills-library/toolbox/code-graph-and-web-scraping-mcps.md +237 -0
  383. package/skills-library/ui-ux-pro-max/ACCESSIBILITY_ESSENTIALS.md +115 -0
  384. package/skills-library/ui-ux-pro-max/DESIGN_SYSTEM_SCAFFOLDING.md +133 -0
  385. package/skills-library/ui-ux-pro-max/RESPONSIVE_LAYOUT_PATTERNS.md +119 -0
  386. package/skills-library/ui-ux-pro-max/SKILL.md +386 -0
  387. package/skills-library/ui-ux-pro-max/data/charts.csv +26 -0
  388. package/skills-library/ui-ux-pro-max/data/colors.csv +97 -0
  389. package/skills-library/ui-ux-pro-max/data/icons.csv +101 -0
  390. package/skills-library/ui-ux-pro-max/data/landing.csv +31 -0
  391. package/skills-library/ui-ux-pro-max/data/products.csv +97 -0
  392. package/skills-library/ui-ux-pro-max/data/react-performance.csv +45 -0
  393. package/skills-library/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  394. package/skills-library/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  395. package/skills-library/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  396. package/skills-library/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  397. package/skills-library/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  398. package/skills-library/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  399. package/skills-library/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  400. package/skills-library/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  401. package/skills-library/ui-ux-pro-max/data/stacks/react.csv +54 -0
  402. package/skills-library/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  403. package/skills-library/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  404. package/skills-library/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  405. package/skills-library/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  406. package/skills-library/ui-ux-pro-max/data/styles.csv +68 -0
  407. package/skills-library/ui-ux-pro-max/data/typography.csv +58 -0
  408. package/skills-library/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  409. package/skills-library/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  410. package/skills-library/ui-ux-pro-max/data/web-interface.csv +31 -0
  411. package/skills-library/wordpress-style-theme-components.md +1526 -0
  412. package/templates/ASSUMPTIONS.md +1 -1
  413. package/templates/DECISION_LOG.md +0 -1
  414. package/templates/phase-prompt.md +1 -1
  415. package/templates/phoenix-comparison.md +6 -6
  416. package/templates/skill-api-integration.md +106 -0
  417. package/templates/skill-architecture-pattern.md +92 -0
  418. package/templates/skill-debug-pattern.md +98 -0
  419. package/templates/skill-devops-recipe.md +107 -0
  420. package/templates/skill-general.md +65 -0
  421. package/templates/skill-ui-component.md +113 -0
  422. package/tools/uat-runner.py +179 -0
  423. package/version.json +7 -3
  424. package/workflows/handoff-session.md +2 -2
  425. package/workflows/new-project.md +2 -2
  426. package/workflows/plan-phase.md +1 -1
  427. package/.claude-plugin/plugin.json +0 -64
  428. package/skills-library/_general/methodology/LIVE_BREADCRUMB_PROTOCOL.md +0 -242
  429. package/skills-library/_general/methodology/llm-judge-memory-crud.md +0 -241
  430. package/skills-library/methodology/REFLEXION_MEMORY_PATTERN.md +0 -183
  431. package/skills-library/methodology/RESEARCH_BACKED_WORKFLOW_UPGRADE.md +0 -263
  432. package/skills-library/methodology/SABBATH_REST_PATTERN.md +0 -267
  433. package/skills-library/methodology/STONE_AND_SCAFFOLD.md +0 -220
  434. package/skills-library/specialists/api-architecture/api-designer.md +0 -49
  435. package/skills-library/specialists/api-architecture/graphql-architect.md +0 -49
  436. package/skills-library/specialists/api-architecture/mcp-developer.md +0 -51
  437. package/skills-library/specialists/api-architecture/microservices-architect.md +0 -50
  438. package/skills-library/specialists/api-architecture/websocket-engineer.md +0 -48
  439. package/skills-library/specialists/backend/django-expert.md +0 -52
  440. package/skills-library/specialists/backend/fastapi-expert.md +0 -52
  441. package/skills-library/specialists/backend/laravel-specialist.md +0 -52
  442. package/skills-library/specialists/backend/nestjs-expert.md +0 -51
  443. package/skills-library/specialists/backend/rails-expert.md +0 -53
  444. package/skills-library/specialists/backend/spring-boot-engineer.md +0 -56
  445. package/skills-library/specialists/data-ml/fine-tuning-expert.md +0 -48
  446. package/skills-library/specialists/data-ml/ml-pipeline.md +0 -47
  447. package/skills-library/specialists/data-ml/pandas-pro.md +0 -47
  448. package/skills-library/specialists/data-ml/rag-architect.md +0 -51
  449. package/skills-library/specialists/data-ml/spark-engineer.md +0 -47
  450. package/skills-library/specialists/frontend/angular-architect.md +0 -52
  451. package/skills-library/specialists/frontend/flutter-expert.md +0 -51
  452. package/skills-library/specialists/frontend/nextjs-developer.md +0 -54
  453. package/skills-library/specialists/frontend/react-native-expert.md +0 -50
  454. package/skills-library/specialists/frontend/vue-expert.md +0 -51
  455. package/skills-library/specialists/infrastructure/chaos-engineer.md +0 -74
  456. package/skills-library/specialists/infrastructure/cloud-architect.md +0 -70
  457. package/skills-library/specialists/infrastructure/database-optimizer.md +0 -64
  458. package/skills-library/specialists/infrastructure/devops-engineer.md +0 -70
  459. package/skills-library/specialists/infrastructure/kubernetes-specialist.md +0 -52
  460. package/skills-library/specialists/infrastructure/monitoring-expert.md +0 -70
  461. package/skills-library/specialists/infrastructure/sre-engineer.md +0 -70
  462. package/skills-library/specialists/infrastructure/terraform-engineer.md +0 -51
  463. package/skills-library/specialists/languages/cpp-pro.md +0 -74
  464. package/skills-library/specialists/languages/csharp-developer.md +0 -69
  465. package/skills-library/specialists/languages/dotnet-core-expert.md +0 -54
  466. package/skills-library/specialists/languages/golang-pro.md +0 -51
  467. package/skills-library/specialists/languages/java-architect.md +0 -49
  468. package/skills-library/specialists/languages/javascript-pro.md +0 -68
  469. package/skills-library/specialists/languages/kotlin-specialist.md +0 -68
  470. package/skills-library/specialists/languages/php-pro.md +0 -49
  471. package/skills-library/specialists/languages/python-pro.md +0 -52
  472. package/skills-library/specialists/languages/react-expert.md +0 -51
  473. package/skills-library/specialists/languages/rust-engineer.md +0 -50
  474. package/skills-library/specialists/languages/sql-pro.md +0 -56
  475. package/skills-library/specialists/languages/swift-expert.md +0 -69
  476. package/skills-library/specialists/languages/typescript-pro.md +0 -51
  477. package/skills-library/specialists/platform/atlassian-mcp.md +0 -52
  478. package/skills-library/specialists/platform/embedded-systems.md +0 -53
  479. package/skills-library/specialists/platform/game-developer.md +0 -53
  480. package/skills-library/specialists/platform/salesforce-developer.md +0 -53
  481. package/skills-library/specialists/platform/shopify-expert.md +0 -49
  482. package/skills-library/specialists/platform/wordpress-pro.md +0 -49
  483. package/skills-library/specialists/quality/code-documenter.md +0 -51
  484. package/skills-library/specialists/quality/code-reviewer.md +0 -67
  485. package/skills-library/specialists/quality/debugging-wizard.md +0 -51
  486. package/skills-library/specialists/quality/fullstack-guardian.md +0 -51
  487. package/skills-library/specialists/quality/legacy-modernizer.md +0 -50
  488. package/skills-library/specialists/quality/playwright-expert.md +0 -65
  489. package/skills-library/specialists/quality/spec-miner.md +0 -56
  490. package/skills-library/specialists/quality/test-master.md +0 -65
  491. package/skills-library/specialists/security/secure-code-guardian.md +0 -55
  492. package/skills-library/specialists/security/security-reviewer.md +0 -53
  493. package/skills-library/specialists/workflow/architecture-designer.md +0 -53
  494. package/skills-library/specialists/workflow/cli-developer.md +0 -70
  495. package/skills-library/specialists/workflow/feature-forge.md +0 -65
  496. package/skills-library/specialists/workflow/prompt-engineer.md +0 -54
  497. package/skills-library/specialists/workflow/the-fool.md +0 -62
  498. /package/skills-library/{performance → _general/performance}/cache-augmented-generation.md +0 -0
  499. /package/skills-library/{debugging → parallel-debug}/FAILURE_TAXONOMY_CLASSIFICATION.md +0 -0
  500. /package/skills-library/{debugging → parallel-debug}/THREE_AGENT_HYPOTHESIS_DEBUGGING.md +0 -0
@@ -0,0 +1,1452 @@
1
+ ---
2
+ name: podcast-quality-evaluation
3
+ category: creative-multimedia
4
+ version: 1.0.0
5
+ contributed: 2026-03-10
6
+ contributor: dominion-flow-research
7
+ last_updated: 2026-03-10
8
+ tags: [podcast, evaluation, quality, metrics, tts, audio-quality, content-verification]
9
+ difficulty: medium
10
+ ---
11
+
12
+ # Podcast Quality Evaluation
13
+ ## Description
14
+
15
+ Evaluate AI-generated podcast audio across three dimensions: text quality, speech quality, and audio quality. This skill provides automated scoring functions, human evaluation rubrics, and pass/fail quality gates for podcast production pipelines. Built on research from PodEval, PodBench, and prosody evaluation literature.
16
+
17
+ ## When to Use
18
+
19
+ - Building an AI podcast generation pipeline and need quality checks
20
+ - Evaluating TTS output before publishing (Google Cloud TTS, Anthropic voice, ElevenLabs, etc.)
21
+ - Comparing different TTS engines or voice models for podcast use
22
+ - Setting up CI/CD quality gates for automated podcast production
23
+ - Auditing existing podcast audio for production quality issues
24
+ - Training or fine-tuning voice models and need objective metrics
25
+
26
+ ---
27
+
28
+ ## 1. Three-Dimensional Evaluation Framework
29
+
30
+ PodEval establishes that podcast quality cannot be captured by a single metric.
31
+ Three orthogonal dimensions must be measured independently.
32
+
33
+ ```
34
+ ┌─────────────────────────────────────────────────────────────┐
35
+ │ PODCAST QUALITY EVALUATION │
36
+ │ │
37
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
38
+ │ │ TEXT QUALITY │ │SPEECH QUALITY│ │ AUDIO QUALITY│ │
39
+ │ │ │ │ │ │ │ │
40
+ │ │ • Factual │ │ • Naturalness│ │ • SNR │ │
41
+ │ │ accuracy │ │ • Speaker │ │ • Loudness │ │
42
+ │ │ • Source │ │ similarity │ │ • Dynamic │ │
43
+ │ │ coverage │ │ • Prosody │ │ range │ │
44
+ │ │ • Halluc. │ │ score │ │ • Clipping │ │
45
+ │ │ rate │ │ • Emotion │ │ • Artifacts │ │
46
+ │ │ │ │ match │ │ │ │
47
+ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
48
+ │ │ │ │ │
49
+ │ v v v │
50
+ │ ┌─────────────────────────────────────────────────┐ │
51
+ │ │ COMPOSITE QUALITY SCORE │ │
52
+ │ │ (weighted by use-case: publish, draft, etc.) │ │
53
+ │ └─────────────────────────────────────────────────┘ │
54
+ └─────────────────────────────────────────────────────────────┘
55
+ ```
56
+
57
+ ### Dimension Detail
58
+
59
+ | Dimension | What It Measures | Key Metrics | Tools |
60
+ |-----------|-----------------|-------------|-------|
61
+ | **Text Quality** | Content accuracy, faithfulness to source material, hallucination avoidance | Factual accuracy score, source coverage %, hallucination rate, topical coherence | Embedding similarity, LLM-as-judge (Claude), ROUGE-L |
62
+ | **Speech Quality** | Naturalness of synthesized speech, speaker consistency across segments | MOS (Mean Opinion Score), speaker similarity (cosine), prosody score, emotion alignment | Resemblyzer, librosa, FFmpeg pitch analysis |
63
+ | **Audio Quality** | Production quality, mixing, mastering standards | SNR (dB), loudness (LUFS), dynamic range, clipping detection, silence distribution | FFmpeg loudnorm, ebur128, silencedetect filters |
64
+
65
+ ### Weighting by Use Case
66
+
67
+ Different podcast types demand different weight distributions:
68
+
69
+ ```typescript
70
+ interface QualityWeights {
71
+ text: number; // 0-1
72
+ speech: number; // 0-1
73
+ audio: number; // 0-1
74
+ }
75
+
76
+ const WEIGHTS_BY_USE_CASE: Record<string, QualityWeights> = {
77
+ // Educational/news — accuracy is paramount
78
+ 'educational': { text: 0.50, speech: 0.25, audio: 0.25 },
79
+
80
+ // Entertainment/storytelling — speech quality matters most
81
+ 'entertainment': { text: 0.25, speech: 0.45, audio: 0.30 },
82
+
83
+ // Professional/corporate — production quality is key
84
+ 'corporate': { text: 0.35, speech: 0.25, audio: 0.40 },
85
+
86
+ // Ministry/sermon — balance of content and delivery
87
+ 'ministry': { text: 0.40, speech: 0.35, audio: 0.25 },
88
+
89
+ // Default balanced
90
+ 'default': { text: 0.34, speech: 0.33, audio: 0.33 },
91
+ };
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 2. PodBench Evaluation Methodology
97
+
98
+ PodBench defines four evaluation axes for AI-generated podcast scripts and audio.
99
+ Each axis uses a combination of automated metrics and LLM-based assessment.
100
+
101
+ ### 2.1 Content Fidelity
102
+
103
+ Does the generated podcast script accurately represent the source material?
104
+
105
+ **Automated checks:**
106
+ - Embedding similarity between source chunks and script segments
107
+ - Named entity preservation (all key entities from source appear in script)
108
+ - Claim verification (extract claims from script, verify against source)
109
+
110
+ **LLM-based assessment (using Claude):**
111
+
112
+ ```typescript
113
+ const CONTENT_FIDELITY_PROMPT = `
114
+ You are a podcast quality evaluator. Compare the generated podcast script
115
+ against the source documents.
116
+
117
+ Source Documents:
118
+ {sources}
119
+
120
+ Generated Script:
121
+ {script}
122
+
123
+ Evaluate on these criteria (score 1-5 each):
124
+ 1. COVERAGE: Does the script cover the key points from the sources?
125
+ 2. ACCURACY: Are all claims in the script supported by the sources?
126
+ 3. HALLUCINATION: Does the script introduce unsupported claims? (5 = no hallucinations)
127
+ 4. ATTRIBUTION: When the script references facts, could a listener trace them to sources?
128
+
129
+ Return JSON:
130
+ {
131
+ "coverage": { "score": number, "missing_topics": string[] },
132
+ "accuracy": { "score": number, "unsupported_claims": string[] },
133
+ "hallucination": { "score": number, "fabricated_claims": string[] },
134
+ "attribution": { "score": number, "notes": string },
135
+ "overall_fidelity": number
136
+ }
137
+ `;
138
+ ```
139
+
140
+ ### 2.2 Structural Compliance
141
+
142
+ Does the output follow the requested podcast format?
143
+
144
+ | Format | Required Structure |
145
+ |--------|--------------------|
146
+ | **Interview** | Host intro, guest introduction, Q&A segments, closing |
147
+ | **Deep-dive** | Topic intro, background, main analysis, implications, summary |
148
+ | **Debate** | Topic statement, position A, position B, rebuttals, synthesis |
149
+ | **News roundup** | Headlines, story segments (3-5), transitions, closing |
150
+ | **Sermon recap** | Scripture reading, context, main points, application, prayer |
151
+
152
+ **Structural compliance checker:**
153
+
154
+ ```typescript
155
+ interface StructuralCheck {
156
+ format: string;
157
+ requiredSections: string[];
158
+ foundSections: string[];
159
+ missingSections: string[];
160
+ score: number; // 0-1
161
+ }
162
+
163
+ function checkStructuralCompliance(
164
+ script: string,
165
+ format: string,
166
+ requiredSections: string[]
167
+ ): StructuralCheck {
168
+ const foundSections: string[] = [];
169
+ const missingSections: string[] = [];
170
+
171
+ for (const section of requiredSections) {
172
+ // Use fuzzy matching — section might not be labeled exactly
173
+ const sectionPatterns = getSectionPatterns(section);
174
+ const found = sectionPatterns.some(pattern =>
175
+ new RegExp(pattern, 'i').test(script)
176
+ );
177
+
178
+ if (found) {
179
+ foundSections.push(section);
180
+ } else {
181
+ missingSections.push(section);
182
+ }
183
+ }
184
+
185
+ return {
186
+ format,
187
+ requiredSections,
188
+ foundSections,
189
+ missingSections,
190
+ score: foundSections.length / requiredSections.length,
191
+ };
192
+ }
193
+
194
+ function getSectionPatterns(section: string): string[] {
195
+ const patternMap: Record<string, string[]> = {
196
+ 'host_intro': [
197
+ 'welcome to', 'hello and welcome', 'hey everyone',
198
+ 'good (morning|afternoon|evening)'
199
+ ],
200
+ 'guest_introduction': [
201
+ 'joining (us|me) today', 'our guest',
202
+ 'please welcome', 'i\'m here with'
203
+ ],
204
+ 'closing': [
205
+ 'thank you for (listening|tuning in)', 'until next time',
206
+ 'that\'s (all|it) for today', 'wrap(ping)? up'
207
+ ],
208
+ 'scripture_reading': [
209
+ 'let\'s (read|turn to|open)', 'the (scripture|passage|verse|text)',
210
+ 'reads from'
211
+ ],
212
+ 'application': [
213
+ 'how (does|can) this apply', 'practical(ly)?',
214
+ 'in our (daily )?lives', 'take(away)?'
215
+ ],
216
+ };
217
+ return patternMap[section] || [section.replace(/_/g, '[\\s_-]')];
218
+ }
219
+ ```
220
+
221
+ ### 2.3 Speaker Consistency
222
+
223
+ Each speaker should maintain a consistent voice, personality, and perspective throughout.
224
+
225
+ **What to check:**
226
+ - **Voice persona** — Does Speaker A stay in character? (e.g., "the skeptic" shouldn't suddenly agree without reason)
227
+ - **Language register** — Consistent formality level per speaker
228
+ - **Knowledge level** — A "beginner-friendly host" shouldn't suddenly use advanced jargon
229
+ - **Catchphrases/patterns** — Recurring verbal patterns feel natural and consistent
230
+
231
+ **Automated detection:**
232
+
233
+ ```typescript
234
+ interface SpeakerSegment {
235
+ speaker: string;
236
+ text: string;
237
+ startTime?: number;
238
+ endTime?: number;
239
+ }
240
+
241
+ interface SpeakerConsistencyReport {
242
+ speakers: Map<string, {
243
+ totalWords: number;
244
+ avgSentenceLength: number;
245
+ vocabularyLevel: 'simple' | 'moderate' | 'advanced';
246
+ formalityScore: number; // 0 (casual) to 1 (formal)
247
+ questionRatio: number; // % of sentences that are questions
248
+ consistencyScore: number; // 0-1 across all segments
249
+ }>;
250
+ overallConsistency: number;
251
+ }
252
+
253
+ function analyzeSpeakerConsistency(
254
+ segments: SpeakerSegment[]
255
+ ): SpeakerConsistencyReport {
256
+ const speakerTexts = new Map<string, string[]>();
257
+
258
+ // Group segments by speaker
259
+ for (const seg of segments) {
260
+ const existing = speakerTexts.get(seg.speaker) || [];
261
+ existing.push(seg.text);
262
+ speakerTexts.set(seg.speaker, existing);
263
+ }
264
+
265
+ const speakers = new Map();
266
+
267
+ for (const [speaker, texts] of speakerTexts) {
268
+ const allText = texts.join(' ');
269
+ const words = allText.split(/\s+/);
270
+ const sentences = allText.split(/[.!?]+/).filter(Boolean);
271
+ const questions = sentences.filter(s =>
272
+ s.trim().endsWith('?') || s.includes('?')
273
+ );
274
+
275
+ // Calculate per-segment metrics for consistency measurement
276
+ const segmentMetrics = texts.map(text => ({
277
+ sentenceLength: text.split(/[.!?]+/).filter(Boolean)
278
+ .reduce((sum, s) => sum + s.split(/\s+/).length, 0)
279
+ / Math.max(1, text.split(/[.!?]+/).filter(Boolean).length),
280
+ questionRatio: (text.match(/\?/g) || []).length
281
+ / Math.max(1, text.split(/[.!?]+/).filter(Boolean).length),
282
+ }));
283
+
284
+ // Consistency = inverse of coefficient of variation across segments
285
+ const avgLengths = segmentMetrics.map(m => m.sentenceLength);
286
+ const mean = avgLengths.reduce((a, b) => a + b, 0) / avgLengths.length;
287
+ const stdDev = Math.sqrt(
288
+ avgLengths.reduce((sum, v) => sum + (v - mean) ** 2, 0) / avgLengths.length
289
+ );
290
+ const cv = mean > 0 ? stdDev / mean : 0;
291
+ const consistencyScore = Math.max(0, 1 - cv);
292
+
293
+ speakers.set(speaker, {
294
+ totalWords: words.length,
295
+ avgSentenceLength: words.length / Math.max(1, sentences.length),
296
+ vocabularyLevel: classifyVocabulary(allText),
297
+ formalityScore: measureFormality(allText),
298
+ questionRatio: questions.length / Math.max(1, sentences.length),
299
+ consistencyScore,
300
+ });
301
+ }
302
+
303
+ const scores = Array.from(speakers.values()).map(
304
+ (s: any) => s.consistencyScore
305
+ );
306
+ const overallConsistency = scores.reduce(
307
+ (a: number, b: number) => a + b, 0
308
+ ) / scores.length;
309
+
310
+ return { speakers, overallConsistency };
311
+ }
312
+
313
+ function classifyVocabulary(text: string): 'simple' | 'moderate' | 'advanced' {
314
+ const words = text.toLowerCase().split(/\s+/);
315
+ const avgWordLength = words.reduce((sum, w) => sum + w.length, 0) / words.length;
316
+ if (avgWordLength < 4.5) return 'simple';
317
+ if (avgWordLength < 5.5) return 'moderate';
318
+ return 'advanced';
319
+ }
320
+
321
+ function measureFormality(text: string): number {
322
+ const informalMarkers =
323
+ /\b(yeah|gonna|wanna|kinda|sorta|like|right\?|you know|stuff|things|cool|awesome|hey)\b/gi;
324
+ const formalMarkers =
325
+ /\b(however|furthermore|consequently|nevertheless|regarding|therefore|indeed|certainly|substantial|significant)\b/gi;
326
+ const informal = (text.match(informalMarkers) || []).length;
327
+ const formal = (text.match(formalMarkers) || []).length;
328
+ const total = informal + formal;
329
+ if (total === 0) return 0.5;
330
+ return formal / total;
331
+ }
332
+ ```
333
+
334
+ ### 2.4 Quantitative Constraints
335
+
336
+ Hard requirements that can be measured precisely:
337
+
338
+ | Constraint | Target | Tolerance | How to Measure |
339
+ |------------|--------|-----------|----------------|
340
+ | Duration | Varies (5-60 min) | +/-10% | FFmpeg duration probe |
341
+ | Speaker balance | 50/50 (2 speakers) | 40-60% range | Word count or audio time per speaker |
342
+ | Segment count | Per format | +/-1 segment | Script section counting |
343
+ | Transition presence | Between every segment | 0 missing | Pattern matching for transition phrases |
344
+ | Intro length | 30-90 seconds | +/-15 seconds | Timestamp analysis |
345
+ | Outro length | 20-60 seconds | +/-10 seconds | Timestamp analysis |
346
+
347
+ ---
348
+
349
+ ## 3. Automated Quality Checks (TypeScript)
350
+
351
+ ### 3.1 Faithfulness Scorer
352
+
353
+ Compare the generated script against source documents using embedding similarity.
354
+ Uses Anthropic Claude for verification.
355
+
356
+ ```typescript
357
+ import Anthropic from '@anthropic-ai/sdk';
358
+ import { readFileSync } from 'fs';
359
+
360
+ interface FaithfulnessReport {
361
+ overallScore: number; // 0-1
362
+ claimCount: number;
363
+ verifiedClaims: number;
364
+ unverifiedClaims: string[];
365
+ hallucinations: string[];
366
+ sourceCoverage: number; // 0-1 — what % of source key points appear
367
+ }
368
+
369
+ async function scoreFaithfulness(
370
+ script: string,
371
+ sources: string[],
372
+ apiKey: string
373
+ ): Promise<FaithfulnessReport> {
374
+ const client = new Anthropic({ apiKey });
375
+
376
+ // Step 1: Extract claims from the script
377
+ const claimExtraction = await client.messages.create({
378
+ model: 'claude-sonnet-4-20250514',
379
+ max_tokens: 4096,
380
+ messages: [{
381
+ role: 'user',
382
+ content: `Extract all factual claims from this podcast script.
383
+ Return as JSON array of strings. Only include verifiable factual statements,
384
+ not opinions or conversational filler.
385
+
386
+ Script:
387
+ ${script}`
388
+ }],
389
+ });
390
+
391
+ const claims: string[] = JSON.parse(
392
+ extractJsonFromResponse(claimExtraction.content[0])
393
+ );
394
+
395
+ // Step 2: Verify each claim against sources
396
+ const sourceText = sources.join('\n\n---SOURCE BOUNDARY---\n\n');
397
+
398
+ const verification = await client.messages.create({
399
+ model: 'claude-sonnet-4-20250514',
400
+ max_tokens: 4096,
401
+ messages: [{
402
+ role: 'user',
403
+ content: `Verify each claim against the source documents.
404
+
405
+ Source Documents:
406
+ ${sourceText}
407
+
408
+ Claims to verify:
409
+ ${JSON.stringify(claims, null, 2)}
410
+
411
+ For each claim, respond with JSON:
412
+ {
413
+ "results": [
414
+ {
415
+ "claim": "...",
416
+ "verified": true/false,
417
+ "hallucination": true/false,
418
+ "note": "..."
419
+ }
420
+ ],
421
+ "source_key_points_missed": ["..."]
422
+ }`
423
+ }],
424
+ });
425
+
426
+ const results = JSON.parse(
427
+ extractJsonFromResponse(verification.content[0])
428
+ );
429
+
430
+ const verified = results.results.filter((r: any) => r.verified);
431
+ const hallucinations = results.results
432
+ .filter((r: any) => r.hallucination)
433
+ .map((r: any) => r.claim);
434
+ const unverified = results.results
435
+ .filter((r: any) => !r.verified && !r.hallucination)
436
+ .map((r: any) => r.claim);
437
+
438
+ // Source coverage: key points mentioned vs missed
439
+ const missedPoints = results.source_key_points_missed || [];
440
+ const totalKeyPoints = verified.length + missedPoints.length;
441
+ const sourceCoverage = totalKeyPoints > 0
442
+ ? verified.length / totalKeyPoints
443
+ : 1;
444
+
445
+ const overallScore = claims.length > 0
446
+ ? (verified.length / claims.length)
447
+ * (1 - hallucinations.length / claims.length)
448
+ : 0;
449
+
450
+ return {
451
+ overallScore: Math.max(0, Math.min(1, overallScore)),
452
+ claimCount: claims.length,
453
+ verifiedClaims: verified.length,
454
+ unverifiedClaims: unverified,
455
+ hallucinations,
456
+ sourceCoverage,
457
+ };
458
+ }
459
+
460
+ function extractJsonFromResponse(content: any): string {
461
+ const text = content.type === 'text' ? content.text : String(content);
462
+ const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
463
+ if (jsonMatch) return jsonMatch[1].trim();
464
+ const firstBrace = text.indexOf('{');
465
+ const lastBrace = text.lastIndexOf('}');
466
+ if (firstBrace !== -1 && lastBrace !== -1) {
467
+ return text.substring(firstBrace, lastBrace + 1);
468
+ }
469
+ return text;
470
+ }
471
+ ```
472
+
473
+ ### 3.2 Audio Quality Analyzer
474
+
475
+ Use FFmpeg to measure SNR, loudness, clipping, and silence distribution.
476
+
477
+ **Note:** These examples use `execFileSync` from `child_process` with argument
478
+ arrays for safe subprocess execution. In production, use a wrapper like
479
+ `execFileNoThrow` for proper error handling.
480
+
481
+ ```typescript
482
+ import { execFileSync } from 'child_process';
483
+
484
+ interface AudioQualityReport {
485
+ duration: number; // seconds
486
+ loudness: {
487
+ integrated: number; // LUFS
488
+ range: number; // LU
489
+ truePeak: number; // dBTP
490
+ passesLoudnessTarget: boolean;
491
+ };
492
+ snr: {
493
+ estimated: number; // dB
494
+ passesThreshold: boolean;
495
+ };
496
+ clipping: {
497
+ detected: boolean;
498
+ clippedSamples: number;
499
+ percentClipped: number;
500
+ };
501
+ silence: {
502
+ segments: SilenceSegment[];
503
+ awkwardPauses: number; // pauses > 3 seconds
504
+ totalSilence: number; // seconds
505
+ silenceRatio: number; // silence / total duration
506
+ };
507
+ overallPass: boolean;
508
+ }
509
+
510
+ interface SilenceSegment {
511
+ start: number;
512
+ end: number;
513
+ duration: number;
514
+ }
515
+
516
+ async function analyzeAudioQuality(
517
+ audioPath: string
518
+ ): Promise<AudioQualityReport> {
519
+ // --- Loudness (EBU R128) ---
520
+ const loudnessRaw = execFileSync(
521
+ 'ffmpeg',
522
+ ['-i', audioPath, '-af', 'ebur128=peak=true', '-f', 'null', '-'],
523
+ { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] }
524
+ );
525
+
526
+ const integrated = parseFloat(
527
+ loudnessRaw.match(/I:\s+(-?[\d.]+)\s+LUFS/)?.[1] || '-99'
528
+ );
529
+ const range = parseFloat(
530
+ loudnessRaw.match(/LRA:\s+([\d.]+)\s+LU/)?.[1] || '0'
531
+ );
532
+ const truePeak = parseFloat(
533
+ loudnessRaw.match(/Peak:\s+(-?[\d.]+)\s+dBFS/)?.[1] || '0'
534
+ );
535
+
536
+ // Podcast target: -16 to -14 LUFS (Spotify/Apple standard)
537
+ const passesLoudnessTarget = integrated >= -16 && integrated <= -14;
538
+
539
+ // --- Duration ---
540
+ const durationRaw = execFileSync(
541
+ 'ffprobe',
542
+ ['-v', 'quiet', '-show_entries', 'format=duration', '-of', 'csv=p=0', audioPath],
543
+ { encoding: 'utf-8' }
544
+ );
545
+ const duration = parseFloat(durationRaw.trim());
546
+
547
+ // --- SNR estimation ---
548
+ const statsRaw = execFileSync(
549
+ 'ffmpeg',
550
+ ['-i', audioPath, '-af', 'astats=metadata=1:reset=1', '-f', 'null', '-'],
551
+ { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] }
552
+ );
553
+
554
+ const rmsLevelMatch = statsRaw.match(/RMS level dB:\s+(-?[\d.]+)/);
555
+ const noiseFloorMatch = statsRaw.match(/Noise floor dB:\s+(-?[\d.]+)/);
556
+ const rmsLevel = parseFloat(rmsLevelMatch?.[1] || '-30');
557
+ const noiseFloor = parseFloat(noiseFloorMatch?.[1] || '-60');
558
+ const estimatedSNR = Math.abs(noiseFloor) - Math.abs(rmsLevel);
559
+
560
+ // --- Clipping detection ---
561
+ const clipRaw = execFileSync(
562
+ 'ffmpeg',
563
+ ['-i', audioPath, '-af', 'astats=metadata=1', '-f', 'null', '-'],
564
+ { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] }
565
+ );
566
+
567
+ const clippedMatch = clipRaw.match(/Number of samples clipped:\s+(\d+)/);
568
+ const totalMatch = clipRaw.match(/Number of samples:\s+(\d+)/);
569
+ const clippedSamples = parseInt(clippedMatch?.[1] || '0', 10);
570
+ const totalSamples = parseInt(totalMatch?.[1] || '1', 10);
571
+
572
+ // --- Silence detection ---
573
+ const silenceRaw = execFileSync(
574
+ 'ffmpeg',
575
+ ['-i', audioPath, '-af', 'silencedetect=noise=-40dB:d=0.5', '-f', 'null', '-'],
576
+ { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] }
577
+ );
578
+
579
+ const silenceSegments: SilenceSegment[] = [];
580
+ const silenceStartRegex = /silence_start:\s+([\d.]+)/g;
581
+ const silenceEndRegex =
582
+ /silence_end:\s+([\d.]+)\s*\|\s*silence_duration:\s+([\d.]+)/g;
583
+
584
+ let startMatch: RegExpExecArray | null;
585
+ const starts: number[] = [];
586
+ while ((startMatch = silenceStartRegex.exec(silenceRaw)) !== null) {
587
+ starts.push(parseFloat(startMatch[1]));
588
+ }
589
+
590
+ let endMatch: RegExpExecArray | null;
591
+ let idx = 0;
592
+ while ((endMatch = silenceEndRegex.exec(silenceRaw)) !== null) {
593
+ silenceSegments.push({
594
+ start: starts[idx] || 0,
595
+ end: parseFloat(endMatch[1]),
596
+ duration: parseFloat(endMatch[2]),
597
+ });
598
+ idx++;
599
+ }
600
+
601
+ const awkwardPauses = silenceSegments.filter(s => s.duration > 3).length;
602
+ const totalSilence = silenceSegments.reduce(
603
+ (sum, s) => sum + s.duration, 0
604
+ );
605
+
606
+ // --- Composite result ---
607
+ const clippingDetected = clippedSamples > 0;
608
+ const overallPass =
609
+ passesLoudnessTarget &&
610
+ estimatedSNR >= 30 &&
611
+ !clippingDetected &&
612
+ awkwardPauses === 0;
613
+
614
+ return {
615
+ duration,
616
+ loudness: { integrated, range, truePeak, passesLoudnessTarget },
617
+ snr: { estimated: estimatedSNR, passesThreshold: estimatedSNR >= 30 },
618
+ clipping: {
619
+ detected: clippingDetected,
620
+ clippedSamples,
621
+ percentClipped: (clippedSamples / totalSamples) * 100,
622
+ },
623
+ silence: {
624
+ segments: silenceSegments,
625
+ awkwardPauses,
626
+ totalSilence,
627
+ silenceRatio: totalSilence / duration,
628
+ },
629
+ overallPass,
630
+ };
631
+ }
632
+ ```
633
+
634
+ ### 3.3 Duration Accuracy
635
+
636
+ ```typescript
637
+ interface DurationReport {
638
+ targetSeconds: number;
639
+ actualSeconds: number;
640
+ deviationPercent: number;
641
+ passes: boolean;
642
+ }
643
+
644
+ async function checkDurationAccuracy(
645
+ audioPath: string,
646
+ targetMinutes: number,
647
+ tolerancePercent: number = 10
648
+ ): Promise<DurationReport> {
649
+ const durationRaw = execFileSync(
650
+ 'ffprobe',
651
+ ['-v', 'quiet', '-show_entries', 'format=duration', '-of', 'csv=p=0', audioPath],
652
+ { encoding: 'utf-8' }
653
+ );
654
+
655
+ const actualSeconds = parseFloat(durationRaw.trim());
656
+ const targetSeconds = targetMinutes * 60;
657
+ const deviation = Math.abs(actualSeconds - targetSeconds);
658
+ const deviationPercent = (deviation / targetSeconds) * 100;
659
+
660
+ return {
661
+ targetSeconds,
662
+ actualSeconds,
663
+ deviationPercent,
664
+ passes: deviationPercent <= tolerancePercent,
665
+ };
666
+ }
667
+ ```
668
+
669
+ ### 3.4 Speaker Balance
670
+
671
+ ```typescript
672
+ interface SpeakerBalanceReport {
673
+ speakers: Map<string, {
674
+ wordCount: number;
675
+ percentage: number;
676
+ audioSeconds?: number;
677
+ }>;
678
+ balanced: boolean; // all speakers within 40-60% for 2-speaker
679
+ dominantSpeaker: string | null; // speaker with >70% airtime
680
+ }
681
+
682
+ function checkSpeakerBalance(
683
+ segments: SpeakerSegment[],
684
+ maxDominance: number = 0.70
685
+ ): SpeakerBalanceReport {
686
+ const speakerWords = new Map<string, number>();
687
+ let totalWords = 0;
688
+
689
+ for (const seg of segments) {
690
+ const words = seg.text.split(/\s+/).filter(Boolean).length;
691
+ speakerWords.set(
692
+ seg.speaker,
693
+ (speakerWords.get(seg.speaker) || 0) + words
694
+ );
695
+ totalWords += words;
696
+ }
697
+
698
+ const speakers = new Map<string, {
699
+ wordCount: number;
700
+ percentage: number;
701
+ }>();
702
+ let dominantSpeaker: string | null = null;
703
+
704
+ for (const [speaker, words] of speakerWords) {
705
+ const percentage = totalWords > 0 ? words / totalWords : 0;
706
+ speakers.set(speaker, { wordCount: words, percentage });
707
+ if (percentage > maxDominance) {
708
+ dominantSpeaker = speaker;
709
+ }
710
+ }
711
+
712
+ // For 2-speaker podcasts, balanced means 40-60% each
713
+ const speakerCount = speakers.size;
714
+ let balanced = true;
715
+
716
+ if (speakerCount === 2) {
717
+ for (const { percentage } of speakers.values()) {
718
+ if (percentage < 0.40 || percentage > 0.60) {
719
+ balanced = false;
720
+ break;
721
+ }
722
+ }
723
+ } else {
724
+ // For N speakers, check no one exceeds maxDominance
725
+ balanced = dominantSpeaker === null;
726
+ }
727
+
728
+ return { speakers, balanced, dominantSpeaker };
729
+ }
730
+ ```
731
+
732
+ ### 3.5 Silence Detection
733
+
734
+ Flag awkward pauses (>3 seconds) or completely missing pauses between segments.
735
+
736
+ ```typescript
737
+ interface SilenceReport {
738
+ awkwardPauses: SilenceSegment[]; // > 3 seconds
739
+ missingPauses: number[]; // timestamps where pause expected but absent
740
+ naturalPauses: number; // 0.3-2.0 second pauses (healthy)
741
+ totalSilenceRatio: number;
742
+ assessment: 'good' | 'too-much-silence' | 'too-little-silence' | 'awkward-pauses';
743
+ }
744
+
745
+ function assessSilencePattern(
746
+ silenceSegments: SilenceSegment[],
747
+ totalDuration: number
748
+ ): SilenceReport {
749
+ const awkwardPauses = silenceSegments.filter(s => s.duration > 3.0);
750
+ const naturalPauses = silenceSegments.filter(
751
+ s => s.duration >= 0.3 && s.duration <= 2.0
752
+ ).length;
753
+ const totalSilence = silenceSegments.reduce(
754
+ (sum, s) => sum + s.duration, 0
755
+ );
756
+ const totalSilenceRatio = totalSilence / totalDuration;
757
+
758
+ // Healthy podcast: 15-25% silence (breathing room, natural pauses)
759
+ let assessment: SilenceReport['assessment'];
760
+ if (awkwardPauses.length > 0) {
761
+ assessment = 'awkward-pauses';
762
+ } else if (totalSilenceRatio > 0.30) {
763
+ assessment = 'too-much-silence';
764
+ } else if (totalSilenceRatio < 0.10) {
765
+ assessment = 'too-little-silence';
766
+ } else {
767
+ assessment = 'good';
768
+ }
769
+
770
+ return {
771
+ awkwardPauses,
772
+ missingPauses: [], // Requires segment boundary timestamps to detect
773
+ naturalPauses,
774
+ totalSilenceRatio,
775
+ assessment,
776
+ };
777
+ }
778
+ ```
779
+
780
+ ---
781
+
782
+ ## 4. Prosody Evaluation
783
+
784
+ Based on "Toward Objective Prosody Evaluation in TTS" (arXiv 2511.02104).
785
+ Prosody — the rhythm, stress, and intonation of speech — is the primary differentiator
786
+ between robotic and natural-sounding TTS output.
787
+
788
+ ### 4.1 What to Measure
789
+
790
+ | Feature | Natural Speech | Robotic TTS | How to Extract |
791
+ |---------|---------------|-------------|----------------|
792
+ | **Pitch variation** | 50-200 Hz range, contextual rises/falls | Flat or exaggerated | FFmpeg `astats` + Python librosa |
793
+ | **Speaking rate** | 120-180 WPM, varies by emphasis | Constant pace | Word count / duration per segment |
794
+ | **Pause distribution** | Natural clusters around clauses | Evenly spaced or absent | Silence detection |
795
+ | **Emphasis** | Key words stressed naturally | Uniform stress or wrong words | Pitch + energy peaks |
796
+ | **Sentence-final patterns** | Falling for statements, rising for questions | Monotone endings | Pitch contour analysis |
797
+
798
+ ### 4.2 FFmpeg Pitch Extraction
799
+
800
+ ```bash
801
+ # Extract pitch contour using FFmpeg + audio analysis
802
+
803
+ # Step 1: Convert to mono WAV for consistent analysis
804
+ ffmpeg -i podcast.mp3 -ac 1 -ar 16000 -f wav podcast_mono.wav
805
+
806
+ # Step 2: Extract volume envelope (proxy for emphasis)
807
+ ffmpeg -i podcast_mono.wav \
808
+ -af "astats=metadata=1:reset=1,ametadata=print:key=lavfi.astats.Overall.RMS_level:file=rms_levels.txt" \
809
+ -f null -
810
+
811
+ # Step 3: Detect pitch periods (fundamental frequency)
812
+ ffmpeg -i podcast_mono.wav \
813
+ -af "aresample=16000,asetnsamples=n=400:p=0,showcqt=s=1920x1080" \
814
+ -frames:v 1 pitch_analysis.png
815
+ ```
816
+
817
+ ### 4.3 Python Librosa Analysis (Reference)
818
+
819
+ For deeper prosody analysis, use Python librosa.
820
+ (Learning opportunity — Python is on Thierry's learning roadmap.)
821
+
822
+ ```python
823
+ """
824
+ Prosody analyzer for AI-generated podcast audio.
825
+ Uses librosa for acoustic feature extraction.
826
+
827
+ Measures:
828
+ - Pitch variation (F0 contour)
829
+ - Speaking rate estimation
830
+ - Energy distribution
831
+ - Pause patterns
832
+
833
+ Requires: pip install librosa numpy soundfile
834
+ """
835
+
836
+ import librosa
837
+ import numpy as np
838
+ from dataclasses import dataclass
839
+ @dataclass
840
+ class ProsodyReport:
841
+ """Results of prosody analysis."""
842
+ pitch_mean: float # Hz — average fundamental frequency
843
+ pitch_std: float # Hz — variation (higher = more expressive)
844
+ pitch_range: float # Hz — max - min
845
+ speaking_rate: float # estimated syllables per second
846
+ energy_variation: float # coefficient of variation of RMS energy
847
+ pause_count: int # number of detected pauses
848
+ avg_pause_duration: float # seconds
849
+ monotone_score: float # 0 (very monotone) to 1 (very expressive)
850
+ naturalness_estimate: float # 0-1 composite score
851
+ def analyze_prosody(audio_path: str, sr: int = 22050) -> ProsodyReport:
852
+ """
853
+ Analyze prosody features of an audio file.
854
+
855
+ Args:
856
+ audio_path: Path to audio file (WAV, MP3, etc.)
857
+ sr: Sample rate for analysis (default 22050 Hz)
858
+
859
+ Returns:
860
+ ProsodyReport with all measured features
861
+
862
+ How it works (linear walkthrough):
863
+ 1. Load audio at target sample rate
864
+ 2. Extract pitch (F0) using pyin algorithm — robust for speech
865
+ 3. Compute RMS energy per frame — shows emphasis patterns
866
+ 4. Count onsets as proxy for syllable rate
867
+ 5. Detect silent frames for pause analysis
868
+ 6. Calculate monotone score from pitch coefficient of variation
869
+ 7. Combine into weighted naturalness estimate
870
+ """
871
+ # Load audio
872
+ y, sr = librosa.load(audio_path, sr=sr)
873
+ duration = librosa.get_duration(y=y, sr=sr)
874
+
875
+ # --- Pitch (F0) extraction ---
876
+ # pyin = probabilistic YIN algorithm, robust for speech
877
+ f0, voiced_flag, voiced_probs = librosa.pyin(
878
+ y, fmin=50, fmax=500, sr=sr
879
+ )
880
+
881
+ # Filter to only voiced segments (where pitch is detectable)
882
+ voiced_f0 = f0[~np.isnan(f0)]
883
+
884
+ pitch_mean = float(np.mean(voiced_f0)) if len(voiced_f0) > 0 else 0
885
+ pitch_std = float(np.std(voiced_f0)) if len(voiced_f0) > 0 else 0
886
+ pitch_range = float(np.ptp(voiced_f0)) if len(voiced_f0) > 0 else 0
887
+
888
+ # --- Energy (RMS) ---
889
+ rms = librosa.feature.rms(y=y, frame_length=2048, hop_length=512)[0]
890
+ energy_mean = float(np.mean(rms))
891
+ energy_std = float(np.std(rms))
892
+ energy_variation = energy_std / energy_mean if energy_mean > 0 else 0
893
+
894
+ # --- Speaking rate estimation ---
895
+ # Onset detection as proxy for syllable rate
896
+ onset_env = librosa.onset.onset_strength(y=y, sr=sr)
897
+ onsets = librosa.onset.onset_detect(onset_envelope=onset_env, sr=sr)
898
+ speaking_rate = len(onsets) / duration if duration > 0 else 0
899
+
900
+ # --- Pause detection ---
901
+ # Frames where RMS is below threshold
902
+ silence_threshold = 0.01 # adjust based on recording
903
+ is_silent = rms[0] < silence_threshold
904
+ pause_boundaries = np.diff(is_silent.astype(int))
905
+ pause_starts = np.where(pause_boundaries == 1)[0]
906
+ pause_ends = np.where(pause_boundaries == -1)[0]
907
+
908
+ # Align starts and ends
909
+ if len(pause_ends) > 0 and len(pause_starts) > 0:
910
+ if pause_ends[0] < pause_starts[0]:
911
+ pause_ends = pause_ends[1:]
912
+ min_len = min(len(pause_starts), len(pause_ends))
913
+ pause_starts = pause_starts[:min_len]
914
+ pause_ends = pause_ends[:min_len]
915
+ # Convert frames to seconds
916
+ pause_durations = (pause_ends - pause_starts) * 512 / sr
917
+ pause_count = len(pause_durations)
918
+ avg_pause = float(np.mean(pause_durations)) if pause_count > 0 else 0
919
+ else:
920
+ pause_count = 0
921
+ avg_pause = 0.0
922
+
923
+ # --- Monotone score ---
924
+ # Natural speech: pitch CV typically 0.15-0.35
925
+ # Monotone TTS: pitch CV typically < 0.08
926
+ pitch_cv = pitch_std / pitch_mean if pitch_mean > 0 else 0
927
+ monotone_score = min(1.0, pitch_cv / 0.25)
928
+
929
+ # --- Composite naturalness ---
930
+ # Weighted combination of prosody features
931
+ naturalness_estimate = (
932
+ 0.35 * monotone_score
933
+ + 0.25 * min(1.0, energy_variation / 0.5)
934
+ + 0.20 * (1.0 if 3.0 <= speaking_rate <= 6.0 else 0.5)
935
+ + 0.20 * (1.0 if 0.3 <= avg_pause <= 1.5 else 0.5)
936
+ )
937
+
938
+ return ProsodyReport(
939
+ pitch_mean=pitch_mean,
940
+ pitch_std=pitch_std,
941
+ pitch_range=pitch_range,
942
+ speaking_rate=speaking_rate,
943
+ energy_variation=energy_variation,
944
+ pause_count=pause_count,
945
+ avg_pause_duration=avg_pause,
946
+ monotone_score=monotone_score,
947
+ naturalness_estimate=min(1.0, naturalness_estimate),
948
+ )
949
+ # --- Usage example ---
950
+ if __name__ == '__main__':
951
+ import sys
952
+ if len(sys.argv) < 2:
953
+ print("Usage: python prosody_analyzer.py <audio_file>")
954
+ sys.exit(1)
955
+
956
+ report = analyze_prosody(sys.argv[1])
957
+ print(f"Pitch: mean={report.pitch_mean:.1f}Hz, "
958
+ f"std={report.pitch_std:.1f}Hz, "
959
+ f"range={report.pitch_range:.1f}Hz")
960
+ print(f"Speaking rate: {report.speaking_rate:.1f} syllables/sec")
961
+ print(f"Energy variation: {report.energy_variation:.3f}")
962
+ print(f"Pauses: {report.pause_count} detected, "
963
+ f"avg {report.avg_pause_duration:.2f}s")
964
+ print(f"Monotone score: {report.monotone_score:.2f} "
965
+ f"(0=monotone, 1=expressive)")
966
+ print(f"Naturalness estimate: {report.naturalness_estimate:.2f}")
967
+ ```
968
+
969
+ ### 4.4 Prosody Red Flags
970
+
971
+ Automated detection of common TTS prosody failures:
972
+
973
+ | Red Flag | Detection Method | Threshold |
974
+ |----------|-----------------|-----------|
975
+ | **Monotone delivery** | Pitch standard deviation | < 15 Hz = FAIL |
976
+ | **Unnatural emphasis** | Energy spikes on function words (the, a, is) | > 2x surrounding energy = FLAG |
977
+ | **Robotic pacing** | Constant inter-word duration | CV < 0.05 = FAIL |
978
+ | **Wrong question intonation** | Pitch doesn't rise at end of questions | < 10% pitch rise = FLAG |
979
+ | **Breathing artifacts** | Periodic noise at consistent intervals | Detectable pattern = FLAG |
980
+ | **Word boundary glitches** | Sudden pitch/energy discontinuities | > 100Hz jump between frames = FLAG |
981
+
982
+ ---
983
+
984
+ ## 5. Human Evaluation Rubric
985
+
986
+ When automated metrics pass but you need final human judgment.
987
+ Based on PodEval's subjective evaluation protocol with spammer detection.
988
+
989
+ ### 5.1 Scoring Grid
990
+
991
+ | Criterion | 1 (Poor) | 2 (Below Average) | 3 (Acceptable) | 4 (Good) | 5 (Excellent) |
992
+ |-----------|----------|--------------------|-----------------|----------|----------------|
993
+ | **Naturalness** | Robotic, clearly AI-generated | Mostly robotic with rare natural moments | Mostly natural, occasional artifacts | Natural with very minor tells | Indistinguishable from human podcast |
994
+ | **Engagement** | Boring, monotone, would stop listening | Low engagement, attention wanders | Interesting but uneven pacing | Engaging, holds attention well | Compelling throughout, want to hear more |
995
+ | **Accuracy** | Multiple factual errors or fabrications | Several inaccuracies | Minor inaccuracies that don't mislead | Accurate with trivial nitpicks | Fully faithful to sources, well-researched |
996
+ | **Flow** | Disjointed, awkward transitions, no coherence | Choppy transitions, some logical gaps | Mostly smooth with a few rough spots | Natural flow with minor hitches | Seamless conversation, natural topic evolution |
997
+ | **Production** | Noticeable artifacts, volume issues, distortion | Some audio issues (pops, uneven volume) | Clean but basic production | Good production, professional feel | Broadcast-quality, indistinguishable from major podcasts |
998
+
999
+ ### 5.2 Evaluation Protocol
1000
+
1001
+ To get reliable human evaluation results:
1002
+
1003
+ 1. **Recruit 3-5 evaluators** with diverse listening habits
1004
+ 2. **Blind evaluation** — don't tell evaluators whether content is AI-generated
1005
+ 3. **Include calibration samples** — mix in 2-3 known human-recorded podcast clips
1006
+ 4. **Spammer detection** (from PodEval):
1007
+ - Flag evaluators who give the same score for all samples
1008
+ - Flag evaluators who complete evaluation in < 30% of audio duration
1009
+ - Flag evaluators whose scores are > 2 standard deviations from group mean
1010
+ - Remove flagged evaluators and recalculate
1011
+ 5. **Inter-rater reliability** — calculate Krippendorff's alpha (target: > 0.6)
1012
+
1013
+ ### 5.3 Evaluation Form Template
1014
+
1015
+ ```markdown
1016
+ ## Podcast Quality Evaluation
1017
+
1018
+ **Sample ID:** ___________
1019
+ **Evaluator ID:** ___________
1020
+ **Date:** ___________
1021
+
1022
+ Listen to the complete audio sample before scoring.
1023
+
1024
+ ### Scores (circle one per row)
1025
+
1026
+ | Criterion | 1 | 2 | 3 | 4 | 5 |
1027
+ |---------------|-----|-----|-----|-----|-----|
1028
+ | Naturalness | O | O | O | O | O |
1029
+ | Engagement | O | O | O | O | O |
1030
+ | Accuracy | O | O | O | O | O |
1031
+ | Flow | O | O | O | O | O |
1032
+ | Production | O | O | O | O | O |
1033
+
1034
+ ### Open-ended
1035
+
1036
+ 1. What was the most noticeable quality issue (if any)?
1037
+ _________________________________________________
1038
+
1039
+ 2. Did anything sound unnatural or AI-generated? What specifically?
1040
+ _________________________________________________
1041
+
1042
+ 3. Would you subscribe to a podcast at this quality level? (Y/N)
1043
+ Why? ____________________________________________
1044
+
1045
+ **Time to complete evaluation:** _________ minutes
1046
+ ```
1047
+
1048
+ ---
1049
+
1050
+ ## 6. Quality Gates for Pipeline
1051
+
1052
+ Define pass/fail thresholds for automated CI/CD podcast production.
1053
+
1054
+ ### 6.1 Gate Definitions
1055
+
1056
+ ```typescript
1057
+ interface QualityGate {
1058
+ name: string;
1059
+ check: (report: any) => boolean;
1060
+ severity: 'blocker' | 'warning';
1061
+ description: string;
1062
+ }
1063
+
1064
+ const QUALITY_GATES: QualityGate[] = [
1065
+ // --- BLOCKERS (must pass to publish) ---
1066
+ {
1067
+ name: 'faithfulness',
1068
+ check: (r: FaithfulnessReport) => r.overallScore >= 0.85,
1069
+ severity: 'blocker',
1070
+ description:
1071
+ 'Faithfulness score >= 0.85 (85% of claims verified against sources)',
1072
+ },
1073
+ {
1074
+ name: 'no-hallucinations',
1075
+ check: (r: FaithfulnessReport) => r.hallucinations.length === 0,
1076
+ severity: 'blocker',
1077
+ description: 'Zero hallucinated claims detected',
1078
+ },
1079
+ {
1080
+ name: 'audio-snr',
1081
+ check: (r: AudioQualityReport) => r.snr.estimated >= 30,
1082
+ severity: 'blocker',
1083
+ description: 'Signal-to-noise ratio >= 30 dB',
1084
+ },
1085
+ {
1086
+ name: 'loudness-target',
1087
+ check: (r: AudioQualityReport) =>
1088
+ r.loudness.integrated >= -16 && r.loudness.integrated <= -14,
1089
+ severity: 'blocker',
1090
+ description:
1091
+ 'Integrated loudness between -16 and -14 LUFS (podcast standard)',
1092
+ },
1093
+ {
1094
+ name: 'no-clipping',
1095
+ check: (r: AudioQualityReport) => !r.clipping.detected,
1096
+ severity: 'blocker',
1097
+ description: 'No audio clipping detected',
1098
+ },
1099
+ {
1100
+ name: 'speaker-balance',
1101
+ check: (r: SpeakerBalanceReport) => r.balanced,
1102
+ severity: 'blocker',
1103
+ description:
1104
+ 'Speaker balance within 40-60% range (2-speaker format)',
1105
+ },
1106
+ {
1107
+ name: 'duration-accuracy',
1108
+ check: (r: DurationReport) => r.passes,
1109
+ severity: 'blocker',
1110
+ description: 'Duration within +/-10% of target',
1111
+ },
1112
+
1113
+ // --- WARNINGS (flag but don't block) ---
1114
+ {
1115
+ name: 'no-awkward-pauses',
1116
+ check: (r: AudioQualityReport) => r.silence.awkwardPauses === 0,
1117
+ severity: 'warning',
1118
+ description: 'No pauses longer than 3 seconds',
1119
+ },
1120
+ {
1121
+ name: 'true-peak',
1122
+ check: (r: AudioQualityReport) => r.loudness.truePeak <= -1.0,
1123
+ severity: 'warning',
1124
+ description: 'True peak <= -1.0 dBTP (headroom for encoding)',
1125
+ },
1126
+ {
1127
+ name: 'silence-ratio',
1128
+ check: (r: AudioQualityReport) =>
1129
+ r.silence.silenceRatio >= 0.10 && r.silence.silenceRatio <= 0.30,
1130
+ severity: 'warning',
1131
+ description:
1132
+ 'Silence ratio between 10-30% (natural breathing room)',
1133
+ },
1134
+ {
1135
+ name: 'source-coverage',
1136
+ check: (r: FaithfulnessReport) => r.sourceCoverage >= 0.75,
1137
+ severity: 'warning',
1138
+ description: 'At least 75% of source key points covered',
1139
+ },
1140
+ {
1141
+ name: 'prosody-naturalness',
1142
+ check: (r: any) => r.naturalness_estimate >= 0.6,
1143
+ severity: 'warning',
1144
+ description: 'Prosody naturalness score >= 0.6',
1145
+ },
1146
+ ];
1147
+ ```
1148
+
1149
+ ### 6.2 Gate Runner
1150
+
1151
+ ```typescript
1152
+ interface GateResult {
1153
+ gate: string;
1154
+ passed: boolean;
1155
+ severity: 'blocker' | 'warning';
1156
+ description: string;
1157
+ }
1158
+
1159
+ interface PipelineGateReport {
1160
+ results: GateResult[];
1161
+ blockersFailed: number;
1162
+ warningsFailed: number;
1163
+ canPublish: boolean;
1164
+ summary: string;
1165
+ }
1166
+
1167
+ function runQualityGates(reports: {
1168
+ faithfulness?: FaithfulnessReport;
1169
+ audio?: AudioQualityReport;
1170
+ speakerBalance?: SpeakerBalanceReport;
1171
+ duration?: DurationReport;
1172
+ prosody?: any;
1173
+ }): PipelineGateReport {
1174
+ const results: GateResult[] = [];
1175
+
1176
+ const reportMap: Record<string, any> = {
1177
+ 'faithfulness': reports.faithfulness,
1178
+ 'no-hallucinations': reports.faithfulness,
1179
+ 'audio-snr': reports.audio,
1180
+ 'loudness-target': reports.audio,
1181
+ 'no-clipping': reports.audio,
1182
+ 'speaker-balance': reports.speakerBalance,
1183
+ 'duration-accuracy': reports.duration,
1184
+ 'no-awkward-pauses': reports.audio,
1185
+ 'true-peak': reports.audio,
1186
+ 'silence-ratio': reports.audio,
1187
+ 'source-coverage': reports.faithfulness,
1188
+ 'prosody-naturalness': reports.prosody,
1189
+ };
1190
+
1191
+ for (const gate of QUALITY_GATES) {
1192
+ const report = reportMap[gate.name];
1193
+ if (!report) {
1194
+ results.push({
1195
+ gate: gate.name,
1196
+ passed: false,
1197
+ severity: gate.severity,
1198
+ description: `${gate.description} — SKIPPED (no data)`,
1199
+ });
1200
+ continue;
1201
+ }
1202
+
1203
+ const passed = gate.check(report);
1204
+ results.push({
1205
+ gate: gate.name,
1206
+ passed,
1207
+ severity: gate.severity,
1208
+ description: gate.description,
1209
+ });
1210
+ }
1211
+
1212
+ const blockersFailed = results.filter(
1213
+ r => !r.passed && r.severity === 'blocker'
1214
+ ).length;
1215
+ const warningsFailed = results.filter(
1216
+ r => !r.passed && r.severity === 'warning'
1217
+ ).length;
1218
+ const canPublish = blockersFailed === 0;
1219
+
1220
+ const passedCount = results.filter(r => r.passed).length;
1221
+ const summary = canPublish
1222
+ ? `PASS — ${passedCount}/${results.length} gates passed`
1223
+ + ` (${warningsFailed} warnings)`
1224
+ : `FAIL — ${blockersFailed} blocker(s) failed,`
1225
+ + ` ${warningsFailed} warning(s)`;
1226
+
1227
+ return { results, blockersFailed, warningsFailed, canPublish, summary };
1228
+ }
1229
+ ```
1230
+
1231
+ ### 6.3 Quality Gate Summary Table
1232
+
1233
+ | Gate | Threshold | Severity | Rationale |
1234
+ |------|-----------|----------|-----------|
1235
+ | Faithfulness | >= 0.85 | Blocker | Publishing inaccurate content damages credibility |
1236
+ | No hallucinations | 0 fabricated claims | Blocker | Any hallucination is a trust violation |
1237
+ | Audio SNR | >= 30 dB | Blocker | Below 30 dB, noise is audibly distracting |
1238
+ | Loudness | -16 to -14 LUFS | Blocker | Spotify/Apple podcast loudness standard |
1239
+ | No clipping | 0 clipped samples | Blocker | Clipping is immediately audible distortion |
1240
+ | Speaker balance | 40-60% per speaker | Blocker | Unbalanced "conversation" sounds like a monologue |
1241
+ | Duration accuracy | within +/-10% | Blocker | Missing duration target suggests content issues |
1242
+ | No awkward pauses | 0 pauses > 3s | Warning | Awkward but not catastrophic — can be edited |
1243
+ | True peak | <= -1.0 dBTP | Warning | Encoding headroom — matters for lossy formats |
1244
+ | Silence ratio | 10-30% | Warning | Too little = breathless; too much = dead air |
1245
+ | Source coverage | >= 75% | Warning | Missing points reduce value but aren't errors |
1246
+ | Prosody naturalness | >= 0.6 | Warning | Robotic delivery is off-putting but tolerable |
1247
+
1248
+ ---
1249
+
1250
+ ## 7. Putting It All Together: Full Evaluation Pipeline
1251
+
1252
+ ```typescript
1253
+ /**
1254
+ * Complete podcast quality evaluation pipeline.
1255
+ * Runs all checks and produces a unified report.
1256
+ *
1257
+ * Usage:
1258
+ * const report = await evaluatePodcast({
1259
+ * audioPath: './output/episode-42.mp3',
1260
+ * scriptPath: './output/episode-42-script.json',
1261
+ * sourcePaths: ['./sources/article1.txt', './sources/article2.txt'],
1262
+ * targetMinutes: 15,
1263
+ * format: 'deep-dive',
1264
+ * anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
1265
+ * });
1266
+ *
1267
+ * if (report.canPublish) {
1268
+ * console.log('Ready to publish!');
1269
+ * } else {
1270
+ * console.log('Issues found:', report.summary);
1271
+ * }
1272
+ */
1273
+
1274
+ import { readFileSync } from 'fs';
1275
+
1276
+ interface EvaluationConfig {
1277
+ audioPath: string;
1278
+ scriptPath: string; // JSON with { segments: SpeakerSegment[] }
1279
+ sourcePaths: string[];
1280
+ targetMinutes: number;
1281
+ format: string;
1282
+ anthropicApiKey: string;
1283
+ useCase?: string; // for weight selection
1284
+ }
1285
+
1286
+ interface FullEvaluationReport {
1287
+ canPublish: boolean;
1288
+ summary: string;
1289
+ weightedScore: number; // 0-1 composite
1290
+ faithfulness: FaithfulnessReport;
1291
+ audioQuality: AudioQualityReport;
1292
+ speakerBalance: SpeakerBalanceReport;
1293
+ duration: DurationReport;
1294
+ silence: SilenceReport;
1295
+ gates: PipelineGateReport;
1296
+ timestamp: string;
1297
+ }
1298
+
1299
+ async function evaluatePodcast(
1300
+ config: EvaluationConfig
1301
+ ): Promise<FullEvaluationReport> {
1302
+ console.log('[eval] Starting podcast quality evaluation...');
1303
+
1304
+ // Load script and sources
1305
+ const scriptData = JSON.parse(
1306
+ readFileSync(config.scriptPath, 'utf-8')
1307
+ );
1308
+ const segments: SpeakerSegment[] = scriptData.segments;
1309
+ const sources = config.sourcePaths.map(p => readFileSync(p, 'utf-8'));
1310
+ const fullScript = segments
1311
+ .map(s => `${s.speaker}: ${s.text}`)
1312
+ .join('\n');
1313
+
1314
+ // Run all evaluations
1315
+ console.log('[eval] Running faithfulness check...');
1316
+ const faithfulness = await scoreFaithfulness(
1317
+ fullScript, sources, config.anthropicApiKey
1318
+ );
1319
+
1320
+ console.log('[eval] Running audio quality analysis...');
1321
+ const audioQuality = await analyzeAudioQuality(config.audioPath);
1322
+
1323
+ console.log('[eval] Checking speaker balance...');
1324
+ const speakerBalance = checkSpeakerBalance(segments);
1325
+
1326
+ console.log('[eval] Checking duration accuracy...');
1327
+ const duration = await checkDurationAccuracy(
1328
+ config.audioPath, config.targetMinutes
1329
+ );
1330
+
1331
+ console.log('[eval] Assessing silence patterns...');
1332
+ const silence = assessSilencePattern(
1333
+ audioQuality.silence.segments,
1334
+ audioQuality.duration
1335
+ );
1336
+
1337
+ // Run quality gates
1338
+ console.log('[eval] Running quality gates...');
1339
+ const gates = runQualityGates({
1340
+ faithfulness,
1341
+ audio: audioQuality,
1342
+ speakerBalance,
1343
+ duration,
1344
+ });
1345
+
1346
+ // Calculate weighted composite score
1347
+ const weights = WEIGHTS_BY_USE_CASE[config.useCase || 'default'];
1348
+ const textScore = faithfulness.overallScore;
1349
+ const speechScore = speakerBalance.balanced ? 0.8 : 0.4;
1350
+ const audioScore = audioQuality.overallPass ? 0.9 : 0.5;
1351
+ const weightedScore =
1352
+ weights.text * textScore +
1353
+ weights.speech * speechScore +
1354
+ weights.audio * audioScore;
1355
+
1356
+ const report: FullEvaluationReport = {
1357
+ canPublish: gates.canPublish,
1358
+ summary: gates.summary,
1359
+ weightedScore,
1360
+ faithfulness,
1361
+ audioQuality,
1362
+ speakerBalance,
1363
+ duration,
1364
+ silence,
1365
+ gates,
1366
+ timestamp: new Date().toISOString(),
1367
+ };
1368
+
1369
+ // Print summary
1370
+ console.log('\n========================================');
1371
+ console.log(' PODCAST QUALITY EVALUATION REPORT');
1372
+ console.log('========================================');
1373
+ console.log(` Can publish: ${report.canPublish ? 'YES' : 'NO'}`);
1374
+ console.log(` Weighted score: ${(report.weightedScore * 100).toFixed(1)}%`);
1375
+ console.log(` ${report.summary}`);
1376
+ console.log('');
1377
+ console.log(' Gate Results:');
1378
+ for (const result of gates.results) {
1379
+ const icon = result.passed ? 'PASS' : 'FAIL';
1380
+ const sev = result.severity === 'blocker' ? '[BLOCKER]' : '[WARNING]';
1381
+ console.log(
1382
+ ` ${icon} ${sev} ${result.gate}: ${result.description}`
1383
+ );
1384
+ }
1385
+ console.log('========================================\n');
1386
+
1387
+ return report;
1388
+ }
1389
+ ```
1390
+
1391
+ ---
1392
+
1393
+ ## 8. Industry Reference: Platform Loudness Standards
1394
+
1395
+ When targeting specific podcast platforms, use these loudness standards:
1396
+
1397
+ | Platform | Target LUFS | True Peak | Notes |
1398
+ |----------|-------------|-----------|-------|
1399
+ | **Spotify** | -14 LUFS | -1.0 dBTP | Will normalize louder content down |
1400
+ | **Apple Podcasts** | -16 LUFS | -1.0 dBTP | Recommended by Apple |
1401
+ | **YouTube** | -14 LUFS | -1.0 dBTP | Normalizes to -14 |
1402
+ | **Amazon Music** | -14 LUFS | -2.0 dBTP | Slightly more conservative peak |
1403
+ | **General podcast** | -16 to -14 LUFS | -1.0 dBTP | Safe range for all platforms |
1404
+
1405
+ **Recommendation:** Target -16 LUFS with -1.0 dBTP true peak. This is the
1406
+ most universally compatible target and avoids normalization artifacts on
1407
+ any platform.
1408
+
1409
+ ---
1410
+
1411
+ ## 9. Troubleshooting Common Issues
1412
+
1413
+ | Issue | Likely Cause | Fix |
1414
+ |-------|-------------|-----|
1415
+ | Faithfulness score < 0.85 | Script hallucinating or drifting from sources | Tighten system prompt, add source quotes in context |
1416
+ | SNR < 30 dB | TTS model producing noisy output | Switch to higher-quality voice model, add post-processing denoising |
1417
+ | Loudness out of range | No normalization in pipeline | Add `loudnorm` FFmpeg filter: `ffmpeg -i in.mp3 -af loudnorm=I=-16:TP=-1:LRA=11 out.mp3` |
1418
+ | Clipping detected | Gain too high before limiting | Reduce input gain, add limiter with -1 dBTP ceiling |
1419
+ | Speaker imbalance > 60/40 | Script allocates too much to one speaker | Adjust script generation prompt to enforce balance |
1420
+ | Awkward pauses > 3s | TTS inserting long silences at paragraph breaks | Post-process: trim silences > 2s down to 1.5s |
1421
+ | Monotone prosody | TTS model lacks expressiveness | Use SSML markup for emphasis, try different voice, add emotion directives |
1422
+ | Duration off by > 10% | Script too long/short for target | Adjust word count target (avg podcast: ~150 words/minute) |
1423
+
1424
+ ---
1425
+
1426
+ ## 10. Research Citations
1427
+
1428
+ > **PodEval** (arXiv 2510.00485, Oct 2025) — Three-dimensional evaluation framework
1429
+ > covering text quality, speech quality, and audio quality. Introduces spammer detection
1430
+ > methodology for subjective listening tests to improve inter-rater reliability. Key insight:
1431
+ > single-dimension metrics (e.g., only MOS) miss critical quality failures in AI podcasts.
1432
+
1433
+ > **PodBench** (arXiv 2601.14903, Jan 2026) — 800-sample benchmark spanning 4 podcast
1434
+ > formats with multi-faceted evaluation. Combines quantitative constraint checking
1435
+ > (duration, speaker balance, structure) with LLM-based quality assessment using
1436
+ > Claude as evaluator. Key insight: structural compliance and content fidelity are
1437
+ > orthogonal dimensions — a podcast can sound great but be factually wrong.
1438
+
1439
+ > **"Toward Objective Prosody Evaluation in TTS"** (arXiv 2511.02104, Nov 2025) —
1440
+ > Linguistically motivated prosody evaluation framework. Demonstrates that pitch variation,
1441
+ > pause distribution, and speaking rate are the three features most correlated with human
1442
+ > MOS ratings (r > 0.82). Key insight: monotone score (pitch CV) alone predicts 67% of
1443
+ > human naturalness judgments.
1444
+
1445
+ ---
1446
+
1447
+ ## Related Skills
1448
+
1449
+ - `audio-enhancement-pipeline` — FFmpeg filter chains for post-production cleanup
1450
+ - `ffmpeg-command-generator` — Natural language to FFmpeg command conversion
1451
+ - `transcription-pipeline-selector` — Choose the right STT engine for your use case
1452
+ - `content-repurposing-pipeline` — Convert podcast audio into social media content