2sagaco48 1.0.0

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 (365) hide show
  1. package/diagnostics.mjs +141 -0
  2. package/dist/compute.worker-j5ahIpF-.js +1 -0
  3. package/dist/graph.worker-DG6iGCB9.js +1 -0
  4. package/dist/index.html +2314 -0
  5. package/flatten-guide.md +248 -0
  6. package/index.html +12 -0
  7. package/package.json +38 -0
  8. package/src/App.orig.tsx +204 -0
  9. package/src/App.orig1.tsx +27 -0
  10. package/src/App.orig2.tsx +25 -0
  11. package/src/App.tsx +3 -0
  12. package/src/BaseApp.tsx +18 -0
  13. package/src/INTEGRATION_LEDGER.md +22 -0
  14. package/src/PERSIST_CANARY.orig.txt +1 -0
  15. package/src/PERSIST_CANARY.txt +1 -0
  16. package/src/REGRESSION_LEDGER.md +651 -0
  17. package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
  18. package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
  19. package/src/chat/VeritasChat.tsx +69 -0
  20. package/src/chat/synthesis.ts +31 -0
  21. package/src/chat/tier.ts +45 -0
  22. package/src/components/AdaptersPage.orig.tsx +99 -0
  23. package/src/components/AdaptersPage.tsx +3 -0
  24. package/src/components/AdversarialPanel.tsx +136 -0
  25. package/src/components/AnchorBaselinePanel.tsx +75 -0
  26. package/src/components/AnswerPanel.tsx +60 -0
  27. package/src/components/ArtifactPanel.tsx +43 -0
  28. package/src/components/ChatApp.orig.tsx +1582 -0
  29. package/src/components/ChatApp.orig1.tsx +722 -0
  30. package/src/components/ChatApp.tsx +95 -0
  31. package/src/components/ChatAugmentPanels.tsx +73 -0
  32. package/src/components/CitationLedgerPanel.tsx +313 -0
  33. package/src/components/ComputeSandboxPanel.tsx +130 -0
  34. package/src/components/ConfigPanel.tsx +110 -0
  35. package/src/components/ControlPlanePage.tsx +195 -0
  36. package/src/components/CreativeTreeLifePage.tsx +234 -0
  37. package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
  38. package/src/components/DebugTracePanel.tsx +137 -0
  39. package/src/components/DeepReasoningTrace.tsx +48 -0
  40. package/src/components/EntitySheetPanel.tsx +51 -0
  41. package/src/components/FailureModesPanel.tsx +104 -0
  42. package/src/components/GBSDashboard.tsx +239 -0
  43. package/src/components/GraphView.tsx +116 -0
  44. package/src/components/HUD.tsx +125 -0
  45. package/src/components/HypothesisPanel.tsx +67 -0
  46. package/src/components/InnovationGenomeEngine.tsx +792 -0
  47. package/src/components/InnovationPersonaGuide.tsx +348 -0
  48. package/src/components/InnovationPersonaPanel.tsx +289 -0
  49. package/src/components/LiveResourceHUD.tsx +109 -0
  50. package/src/components/LongWriterPanel.tsx +112 -0
  51. package/src/components/MainPipelineV10Bridge.tsx +72 -0
  52. package/src/components/MarkdownLite.tsx +63 -0
  53. package/src/components/MemoryInspector.tsx +38 -0
  54. package/src/components/MemoryMonitor.tsx +52 -0
  55. package/src/components/MemoryStressPanel.tsx +176 -0
  56. package/src/components/ModulesPage.tsx +171 -0
  57. package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
  58. package/src/components/PipelineDebugConsole.tsx +3 -0
  59. package/src/components/PreFlightHUD.tsx +67 -0
  60. package/src/components/PrismaFetchTracePanel.tsx +87 -0
  61. package/src/components/ReportOSPanel.tsx +165 -0
  62. package/src/components/ResourceEstimatorPage.tsx +327 -0
  63. package/src/components/RichText.tsx +181 -0
  64. package/src/components/SagaOsPanel.tsx +605 -0
  65. package/src/components/SagaOsReference.tsx +421 -0
  66. package/src/components/SagaOsV2Panel.tsx +594 -0
  67. package/src/components/SagaStyleTab.tsx +382 -0
  68. package/src/components/SageBlueprintPanel.tsx +416 -0
  69. package/src/components/SharedChatInput.tsx +77 -0
  70. package/src/components/StatFinancePanel.tsx +190 -0
  71. package/src/components/StylePersonaPanel.tsx +128 -0
  72. package/src/components/TemplatesPage.tsx +300 -0
  73. package/src/components/TestPanel.tsx +63 -0
  74. package/src/components/TraceLog.tsx +89 -0
  75. package/src/components/V15CalibrationAugment.tsx +1134 -0
  76. package/src/components/V15CalibrationDialog.tsx +1871 -0
  77. package/src/components/V15Overlay.orig.tsx +120 -0
  78. package/src/components/V15Overlay.tsx +4 -0
  79. package/src/components/V15OverlayWrapper.tsx +362 -0
  80. package/src/components/V15Toggle.tsx +34 -0
  81. package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
  82. package/src/console/EngineeringConsole.tsx +103 -0
  83. package/src/console/FictionSystemPanels.tsx +882 -0
  84. package/src/console/InspectorPanels.tsx +254 -0
  85. package/src/console/ManuscriptGraderPanel.tsx +182 -0
  86. package/src/console/RecursiveReviserPanel.tsx +245 -0
  87. package/src/console/SeedControlPanel.tsx +135 -0
  88. package/src/console/StoryControllerPanel.tsx +443 -0
  89. package/src/console/engineBridge.ts +258 -0
  90. package/src/console/gemini.ts +106 -0
  91. package/src/console/grading.ts +250 -0
  92. package/src/console/util.ts +45 -0
  93. package/src/fiction/continuity.ts +372 -0
  94. package/src/fiction/crash-protection.ts +111 -0
  95. package/src/fiction/diagnostics.ts +208 -0
  96. package/src/fiction/diff.ts +74 -0
  97. package/src/fiction/entity-state-machine.ts +214 -0
  98. package/src/fiction/nive-engine.ts +404 -0
  99. package/src/fiction/pipeline-selftest.ts +132 -0
  100. package/src/fiction/seed-control.ts +193 -0
  101. package/src/fiction/story-engine.ts +326 -0
  102. package/src/fiction/types.ts +68 -0
  103. package/src/flatten-guide.md +379 -0
  104. package/src/index.css +14 -0
  105. package/src/index.orig.css +13 -0
  106. package/src/lib/academic-sources.base.ts +188 -0
  107. package/src/lib/academic-sources.orig.ts +28 -0
  108. package/src/lib/academic-sources.ts +10 -0
  109. package/src/lib/advanced-math.ts +119 -0
  110. package/src/lib/adversarial-engine.base.ts +409 -0
  111. package/src/lib/adversarial-engine.ts +82 -0
  112. package/src/lib/ai-revision.ts +623 -0
  113. package/src/lib/app-state.tsx +426 -0
  114. package/src/lib/artifact-registry.ts +97 -0
  115. package/src/lib/artifacts.ts +180 -0
  116. package/src/lib/atlas-dr.ts +167 -0
  117. package/src/lib/browser-mtls.ts +127 -0
  118. package/src/lib/browser-queue.ts +221 -0
  119. package/src/lib/browser-search-scraper.base.ts +382 -0
  120. package/src/lib/browser-search-scraper.orig.ts +90 -0
  121. package/src/lib/browser-search-scraper.ts +10 -0
  122. package/src/lib/calc-interceptor.ts +82 -0
  123. package/src/lib/citation-lane-tap.ts +78 -0
  124. package/src/lib/citation-ledger-store.ts +369 -0
  125. package/src/lib/citation-ledger.ts +203 -0
  126. package/src/lib/compute-sandbox.ts +215 -0
  127. package/src/lib/compute.worker.ts +153 -0
  128. package/src/lib/connectors/gemini.ts +120 -0
  129. package/src/lib/connectors/jina.ts +233 -0
  130. package/src/lib/connectors/marketdata.ts +51 -0
  131. package/src/lib/connectors/prismafetch.base.ts +175 -0
  132. package/src/lib/connectors/prismafetch.ts +1 -0
  133. package/src/lib/connectors/serpapi.ts +65 -0
  134. package/src/lib/connectors/wikidata.ts +44 -0
  135. package/src/lib/constraints.ts +331 -0
  136. package/src/lib/continuation-detector.base.ts +153 -0
  137. package/src/lib/continuation-detector.ts +48 -0
  138. package/src/lib/contradraft.ts +168 -0
  139. package/src/lib/cors-proxy.ts +120 -0
  140. package/src/lib/coverage.ts +81 -0
  141. package/src/lib/debug/architecture-prescription.ts +556 -0
  142. package/src/lib/debug/covea-repair.ts +543 -0
  143. package/src/lib/debug/helios-ground.ts +319 -0
  144. package/src/lib/debug/intent-decomposer.ts +281 -0
  145. package/src/lib/debug/intent-lattice.ts +349 -0
  146. package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
  147. package/src/lib/debug/pipeline-trace-bus.ts +424 -0
  148. package/src/lib/debug/prompt-forge.ts +678 -0
  149. package/src/lib/debug/repair-sites.ts +513 -0
  150. package/src/lib/debug/research-phase.ts +266 -0
  151. package/src/lib/debug/retrieval-context.ts +76 -0
  152. package/src/lib/debug/retrieval-hardener.ts +142 -0
  153. package/src/lib/debug/scraper-debug-runner.ts +576 -0
  154. package/src/lib/debug/scraper-forensics.ts +418 -0
  155. package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
  156. package/src/lib/debug/self-test.ts +415 -0
  157. package/src/lib/debug/step-attribution.ts +211 -0
  158. package/src/lib/debug/template-rubric.ts +301 -0
  159. package/src/lib/debug/unified-innovation.ts +126 -0
  160. package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
  161. package/src/lib/defense-registry.ts +104 -0
  162. package/src/lib/defenses.ts +162 -0
  163. package/src/lib/deterministic-citation-ledger.ts +174 -0
  164. package/src/lib/elo-registry.base.ts +117 -0
  165. package/src/lib/elo-registry.ts +1 -0
  166. package/src/lib/entity-resolver.ts +291 -0
  167. package/src/lib/failure-modes.ts +267 -0
  168. package/src/lib/feature-registry.ts +69 -0
  169. package/src/lib/flaw-registry.ts +143 -0
  170. package/src/lib/flaws/_template.ts +52 -0
  171. package/src/lib/flaws/builtins.ts +63 -0
  172. package/src/lib/flaws/finance.ts +169 -0
  173. package/src/lib/flaws/fixers.ts +10 -0
  174. package/src/lib/flaws/index.ts +32 -0
  175. package/src/lib/flaws/legal.ts +157 -0
  176. package/src/lib/flaws/medical.ts +188 -0
  177. package/src/lib/flaws/original-defenses-pack.ts +81 -0
  178. package/src/lib/flaws/sample-declarative-pack.json +38 -0
  179. package/src/lib/flaws/selftest.ts +201 -0
  180. package/src/lib/flaws/software-extended.ts +125 -0
  181. package/src/lib/flaws/software-rn-webgl.ts +182 -0
  182. package/src/lib/flaws/statistics-advanced.ts +46 -0
  183. package/src/lib/flaws/statistics.ts +61 -0
  184. package/src/lib/gbse/config.ts +20 -0
  185. package/src/lib/gbse/engine.ts +226 -0
  186. package/src/lib/gbse/graph.ts +118 -0
  187. package/src/lib/gbse/graph.worker.ts +21 -0
  188. package/src/lib/gbse/tests.ts +117 -0
  189. package/src/lib/gbse/tiers.ts +65 -0
  190. package/src/lib/gbse/types.ts +94 -0
  191. package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
  192. package/src/lib/innovation-genome-engine-v2.ts +3 -0
  193. package/src/lib/innovation-genome-engine.orig.ts +330 -0
  194. package/src/lib/innovation-genome-engine.ts +3 -0
  195. package/src/lib/innovation-genome-v10.orig.ts +314 -0
  196. package/src/lib/innovation-genome-v10.ts +2 -0
  197. package/src/lib/innovation-genome-v3.orig.ts +2015 -0
  198. package/src/lib/innovation-genome-v3.ts +2 -0
  199. package/src/lib/innovation-genome-v4.orig.ts +1959 -0
  200. package/src/lib/innovation-genome-v4.ts +2 -0
  201. package/src/lib/innovation-genome-v5.orig.ts +1210 -0
  202. package/src/lib/innovation-genome-v5.ts +2 -0
  203. package/src/lib/innovation-genome-v7.orig.ts +2549 -0
  204. package/src/lib/innovation-genome-v7.ts +2 -0
  205. package/src/lib/innovation-genome-v8.orig.ts +1485 -0
  206. package/src/lib/innovation-genome-v8.ts +2 -0
  207. package/src/lib/innovation-genome-v9.orig.ts +643 -0
  208. package/src/lib/innovation-genome-v9.ts +2 -0
  209. package/src/lib/jina.base.ts +293 -0
  210. package/src/lib/jina.ts +1 -0
  211. package/src/lib/live-telemetry.ts +61 -0
  212. package/src/lib/longwriter.ts +84 -0
  213. package/src/lib/manuscript-grade-v2.ts +298 -0
  214. package/src/lib/memory-governor.ts +173 -0
  215. package/src/lib/memory-stress-tests.ts +248 -0
  216. package/src/lib/model-intelligence.ts +48 -0
  217. package/src/lib/model-rotator.ts +75 -0
  218. package/src/lib/models.orig.ts +513 -0
  219. package/src/lib/models.ts +151 -0
  220. package/src/lib/n-deep.base.ts +592 -0
  221. package/src/lib/n-deep.ts +51 -0
  222. package/src/lib/nih-simulator.ts +56 -0
  223. package/src/lib/nih-vulnerability-fixes.ts +460 -0
  224. package/src/lib/omega-templates.base.ts +294 -0
  225. package/src/lib/omega-templates.ts +225 -0
  226. package/src/lib/omni-nexus.ts +64 -0
  227. package/src/lib/oracle-adapters.ts +67 -0
  228. package/src/lib/oracle-registry.ts +163 -0
  229. package/src/lib/orchestrator.ts +357 -0
  230. package/src/lib/output-boundary.ts +235 -0
  231. package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
  232. package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
  233. package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
  234. package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
  235. package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
  236. package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
  237. package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
  238. package/src/lib/overrides/vite-native-scraper.ts +848 -0
  239. package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
  240. package/src/lib/overrides/vite-native-selftest.ts +183 -0
  241. package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
  242. package/src/lib/philosophy-toolkit.ts +75 -0
  243. package/src/lib/pipeline.orig.ts +659 -0
  244. package/src/lib/pipeline.ts +42 -0
  245. package/src/lib/precache.ts +128 -0
  246. package/src/lib/py-sandbox.ts +205 -0
  247. package/src/lib/quality-score.ts +96 -0
  248. package/src/lib/quant-engine.ts +231 -0
  249. package/src/lib/quant-lib.ts +80 -0
  250. package/src/lib/reportos.ts +175 -0
  251. package/src/lib/research-os.ts +306 -0
  252. package/src/lib/resource-estimator.ts +114 -0
  253. package/src/lib/rpm-governor.ts +93 -0
  254. package/src/lib/runtime-estimator.ts +135 -0
  255. package/src/lib/sage/empty-space-v6.ts +560 -0
  256. package/src/lib/sage/empty-space-v7.ts +222 -0
  257. package/src/lib/sage/empty-space.ts +733 -0
  258. package/src/lib/sage/engine.ts +786 -0
  259. package/src/lib/sage/index.ts +17 -0
  260. package/src/lib/sage/judge.ts +246 -0
  261. package/src/lib/sage/prng.ts +53 -0
  262. package/src/lib/sage/reality-grounding.ts +380 -0
  263. package/src/lib/sage/repetition.ts +151 -0
  264. package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
  265. package/src/lib/sage/saga-os-v2.ts +274 -0
  266. package/src/lib/sage/saga-os-v3-core.ts +1491 -0
  267. package/src/lib/sage/saga-os-v3-release.ts +739 -0
  268. package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
  269. package/src/lib/sage/saga-os-v4.ts +212 -0
  270. package/src/lib/sage/saga-os-v5.ts +205 -0
  271. package/src/lib/sage/saga-os-v6.ts +346 -0
  272. package/src/lib/sage/saga-os-v7.ts +314 -0
  273. package/src/lib/sage/saga-os-v8.ts +505 -0
  274. package/src/lib/sage/saga-os.ts +329 -0
  275. package/src/lib/sage/taxonomy.ts +403 -0
  276. package/src/lib/sage/unified-roll.ts +524 -0
  277. package/src/lib/sage/worked-sketches.ts +44 -0
  278. package/src/lib/sage.ts +166 -0
  279. package/src/lib/scraper-debug-bus.ts +53 -0
  280. package/src/lib/scraper-enhanced.orig.ts +153 -0
  281. package/src/lib/scraper-enhanced.ts +10 -0
  282. package/src/lib/scraper-hardener.base.orig.ts +155 -0
  283. package/src/lib/scraper-hardener.base.ts +317 -0
  284. package/src/lib/scraper-hardener.ts +1 -0
  285. package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
  286. package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
  287. package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
  288. package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
  289. package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
  290. package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
  291. package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
  292. package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
  293. package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
  294. package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
  295. package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
  296. package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
  297. package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
  298. package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
  299. package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
  300. package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
  301. package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
  302. package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
  303. package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
  304. package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
  305. package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
  306. package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
  307. package/src/lib/scraper-vnext/query-strategist.ts +423 -0
  308. package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
  309. package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
  310. package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
  311. package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
  312. package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
  313. package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
  314. package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
  315. package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
  316. package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
  317. package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
  318. package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
  319. package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
  320. package/src/lib/scraper-vnext/strata-engine.ts +13 -0
  321. package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
  322. package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
  323. package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
  324. package/src/lib/scraper-vnext/terminal-final.ts +277 -0
  325. package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
  326. package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
  327. package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
  328. package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
  329. package/src/lib/search-cache.ts +138 -0
  330. package/src/lib/sloop-runner.base.ts +223 -0
  331. package/src/lib/sloop-runner.ts +104 -0
  332. package/src/lib/sloop.ts +114 -0
  333. package/src/lib/small-model-adapters.ts +82 -0
  334. package/src/lib/sscp.ts +86 -0
  335. package/src/lib/system-map.ts +54 -0
  336. package/src/lib/uni-node.ts +86 -0
  337. package/src/lib/universal-rigor-guard.ts +433 -0
  338. package/src/lib/v15-gate-testbed.ts +274 -0
  339. package/src/lib/v15-gemini.base.ts +89 -0
  340. package/src/lib/v15-gemini.ts +1 -0
  341. package/src/lib/v15-grounding.orig.ts +241 -0
  342. package/src/lib/v15-grounding.ts +79 -0
  343. package/src/lib/v15-pipeline.base.ts +769 -0
  344. package/src/lib/v15-pipeline.orig.ts +1154 -0
  345. package/src/lib/v15-pipeline.ts +674 -0
  346. package/src/lib/v15-questions.ts +64 -0
  347. package/src/lib/v15-rate-limiter.orig.ts +91 -0
  348. package/src/lib/v15-rate-limiter.ts +48 -0
  349. package/src/lib/v15-state.base.ts +87 -0
  350. package/src/lib/v15-state.ts +28 -0
  351. package/src/lib/visual-table-generator.tsx +207 -0
  352. package/src/lib/wasm-runtime.ts +133 -0
  353. package/src/lib/williams-persona-guide.ts +265 -0
  354. package/src/lib/williams-style.base.ts +464 -0
  355. package/src/lib/williams-style.ts +59 -0
  356. package/src/lib/worker-pool.ts +96 -0
  357. package/src/lib/writing-tiers.ts +125 -0
  358. package/src/main.orig.tsx +10 -0
  359. package/src/main.tsx +10 -0
  360. package/src/utils/cn.orig.ts +6 -0
  361. package/src/utils/cn.ts +6 -0
  362. package/src/vite-env.d.ts +1 -0
  363. package/tsconfig.json +31 -0
  364. package/unify.mjs +537 -0
  365. package/vite.config.ts +18 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * NARRATIVE INTEGRITY & VERIFICATION ENGINE (NIVE) — TypeScript / Web Runtime
3
+ * ---------------------------------------------------------------------------
4
+ * Faithfully ports and elevates the Section 6 Python reference engine into a
5
+ * production-grade, zero-dependency, deterministic TypeScript audit system.
6
+ *
7
+ * Implements full programmatic auditing for:
8
+ * 1. PROMPT_SCAFFOLDING_LEAK (A.3.1): Developer metadata, word bounds, NIH/SABV tags.
9
+ * 2. EXACT_CLONED_CHAPTER_OPENING & FORMULAIC_OPENINGS (B.2.2 / B.3.1):
10
+ * Detects identical multi-word opening sentences across chapters and
11
+ * rigid procedural frames like "First, because...".
12
+ * 3. CHARACTER_RESURRECTION_VIOLATION (B.1.1): Monotonic mortality tracking
13
+ * where post-mortem somatic action or dialogue attribution triggers a violation.
14
+ * 4. ONTOLOGICAL_METAMORPHOSIS (B.1.2): Detects archetype confusion (e.g. unicorn
15
+ * entity shifting from mare to male hermit to female cottage archivist to chimera).
16
+ * 5. COUNTERFACTUAL_IDENTITY_MUTATION (A.1.1): Detects the 15-fold unlived life
17
+ * mutating professions and swapping biological sex across chapters.
18
+ * 6. SENTENCE_LENGTH_CONSTRAINT_FAILURE (A.3.2): Checks min/max word boundary
19
+ * infractions per sentence.
20
+ * 7. MOTIF_BUDGET_EXCEEDED (A.3.2): Frequency tracking for over-primed motif words.
21
+ * 8. MODULO_SPATIAL_CAROUSEL_TRAP (A.2.1 / B.2.1): Detects cyclical setting ping-pong.
22
+ * 9. MACGUFFIN_INSTABILITY (A.2.2): Detects core truth object shifting physical forms.
23
+ * 10. CLIMAX_EVASION_ADMINISTRATIVE (A.2.3 / B.2.3): Flags resolutions settled by
24
+ * clerical leases, fines, or permits instead of dramatic confrontations.
25
+ */
26
+
27
+ export interface ParsedChapter {
28
+ chapterIndex: number;
29
+ rawHeader: string;
30
+ leakedHeaders: string[];
31
+ cleanNarrative: string;
32
+ }
33
+
34
+ export interface NIVEViolation {
35
+ type:
36
+ | "PROMPT_SCAFFOLDING_LEAK"
37
+ | "EXACT_CLONED_CHAPTER_OPENING"
38
+ | "FORMULAIC_SYNTACTIC_OPENING"
39
+ | "CHARACTER_RESURRECTION_VIOLATION"
40
+ | "ONTOLOGICAL_METAMORPHOSIS"
41
+ | "COUNTERFACTUAL_IDENTITY_MUTATION"
42
+ | "SENTENCE_LENGTH_CONSTRAINT_FAILURE"
43
+ | "MOTIF_BUDGET_EXCEEDED"
44
+ | "MODULO_SPATIAL_CAROUSEL_TRAP"
45
+ | "MACGUFFIN_INSTABILITY"
46
+ | "CLIMAX_EVASION_ADMINISTRATIVE";
47
+ severity: "critical" | "warning" | "info";
48
+ chapter?: number;
49
+ chapters?: number[];
50
+ character?: string;
51
+ count?: number;
52
+ evidence?: string[];
53
+ details?: Record<string, unknown>;
54
+ message: string;
55
+ }
56
+
57
+ export interface NIVEConfig {
58
+ sentenceRange?: [number, number];
59
+ motifLimits?: Record<string, number>;
60
+ mortality?: Record<string, number>; // character name -> chapter index of somatic death
61
+ trackCounterfactual?: boolean;
62
+ trackCarousel?: boolean;
63
+ }
64
+
65
+ export class NarrativeIntegrityEngine {
66
+ public rawText: string;
67
+ public manuscriptId: string;
68
+ public config: NIVEConfig;
69
+ public chapters: ParsedChapter[] = [];
70
+ public violations: NIVEViolation[] = [];
71
+
72
+ constructor(text: string, manuscriptId = "MS-ACTIVE", config: NIVEConfig = {}) {
73
+ this.rawText = text || "";
74
+ this.manuscriptId = manuscriptId;
75
+ this.config = {
76
+ sentenceRange: [6, 28],
77
+ motifLimits: { truth: 6, cold: 6, vault: 6, refrigeratory: 3, borderland: 6 },
78
+ trackCounterfactual: true,
79
+ trackCarousel: true,
80
+ ...config,
81
+ };
82
+ this.parseChapters();
83
+ }
84
+
85
+ private parseChapters(): void {
86
+ if (!this.rawText.trim()) return;
87
+
88
+ // Split on Markdown chapter headings (## N.) or standard numbered chapters
89
+ const splitRegex = /(?:^|\n)(?=(?:##\s+(?:\d+\.?\s*)?|\b\d+\.\s+[A-Z]))/i;
90
+ const rawSegments = this.rawText.trim().split(splitRegex).map((s) => s.trim()).filter(Boolean);
91
+
92
+ let idx = 1;
93
+ for (const raw of rawSegments) {
94
+ const lines = raw.split("\n");
95
+ const header = lines[0]?.trim() || `Chapter ${idx}`;
96
+ const bodyLines = lines.slice(1);
97
+
98
+ const leakedHeaders: string[] = [];
99
+ const cleanLines: string[] = [];
100
+
101
+ for (const line of bodyLines) {
102
+ const lStr = line.trim();
103
+ if (
104
+ [
105
+ "Title:", "Chapter:", "POV:", "Tone:", "Setting:", "Characters:",
106
+ "World Mechanisms:", "Relationship Texture:", "Reality Grounding:",
107
+ "Reality Level:", "Dramatic Job:", "Beat to realize:", "Constraint:",
108
+ "Prose Constraints:", "Target Length:", "Specifics Floor:", "Key Beat:",
109
+ "Specific Device:", "Genre:", "Stakes Rotation:",
110
+ ].some((prefix) => lStr.startsWith(prefix)) ||
111
+ [
112
+ "Self-Correction during drafting", "Check:", "SABV", "L5 Realist?",
113
+ "Let's write", "Let's begin", "Writing...", "Let's execute",
114
+ "CONTINUITY LEDGER —", "STRUCTURAL OBLIGATION —", "CRAFT BUDGETS FOR THIS CHAPTER",
115
+ "_Resuming at chapter", "chapter(s) restored, not regenerated",
116
+ ].some((needle) => lStr.includes(needle))
117
+ ) {
118
+ leakedHeaders.push(line);
119
+ } else {
120
+ cleanLines.push(line);
121
+ }
122
+ }
123
+
124
+ const cleanText = cleanLines.join("\n").trim();
125
+ this.chapters.push({
126
+ chapterIndex: idx++,
127
+ rawHeader: header,
128
+ leakedHeaders,
129
+ cleanNarrative: cleanText,
130
+ });
131
+ }
132
+ }
133
+
134
+ public auditPromptScaffoldingLeaks(): void {
135
+ for (const ch of this.chapters) {
136
+ if (ch.leakedHeaders.length > 0) {
137
+ this.violations.push({
138
+ type: "PROMPT_SCAFFOLDING_LEAK",
139
+ severity: "critical",
140
+ chapter: ch.chapterIndex,
141
+ count: ch.leakedHeaders.length,
142
+ evidence: ch.leakedHeaders.slice(0, 3),
143
+ message: `Chapter ${ch.chapterIndex} contains ${ch.leakedHeaders.length} leaked prompt metadata or scratchpad line(s).`,
144
+ });
145
+ }
146
+ }
147
+ }
148
+
149
+ public auditClonedChapterOpenings(): void {
150
+ const openings: Map<number, string> = new Map();
151
+ for (const ch of this.chapters) {
152
+ const sentences = ch.cleanNarrative
153
+ .replace(/\s+/g, " ")
154
+ .split(/(?<=[.!?])\s+/)
155
+ .map((s) => s.trim())
156
+ .filter((s) => s.length > 15);
157
+ if (sentences.length > 0) {
158
+ openings.set(ch.chapterIndex, sentences[0]);
159
+ }
160
+ }
161
+
162
+ const indices = Array.from(openings.keys());
163
+ for (let i = 0; i < indices.length; i++) {
164
+ for (let j = i + 1; j < indices.length; j++) {
165
+ const id1 = indices[i];
166
+ const id2 = indices[j];
167
+ const s1 = openings.get(id1)!;
168
+ const s2 = openings.get(id2)!;
169
+
170
+ // Exact cloned opening
171
+ if (s1.toLowerCase() === s2.toLowerCase()) {
172
+ this.violations.push({
173
+ type: "EXACT_CLONED_CHAPTER_OPENING",
174
+ severity: "critical",
175
+ chapters: [id1, id2],
176
+ evidence: [s1],
177
+ message: `Chapters ${id1} and ${id2} share an identical opening sentence (Few-Shot Prompt Cloning).`,
178
+ });
179
+ } else if (
180
+ s1.toLowerCase().startsWith("first, because") &&
181
+ s2.toLowerCase().startsWith("first, because")
182
+ ) {
183
+ this.violations.push({
184
+ type: "FORMULAIC_SYNTACTIC_OPENING",
185
+ severity: "warning",
186
+ chapters: [id1, id2],
187
+ evidence: [s1.slice(0, 60), s2.slice(0, 60)],
188
+ message: `Chapters ${id1} and ${id2} use rigid syntactic templating ("First, because...").`,
189
+ });
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ public auditCharacterMortality(mortalityRules?: Record<string, number>): void {
196
+ const rules = mortalityRules || this.config.mortality;
197
+ if (!rules) return;
198
+
199
+ const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
200
+ for (const [charName, deathCh] of Object.entries(rules)) {
201
+ const cn = esc(charName);
202
+ for (const ch of this.chapters) {
203
+ if (ch.chapterIndex > deathCh) {
204
+ let matches: RegExpMatchArray | null = null;
205
+ try {
206
+ const actionPattern = new RegExp(
207
+ `(?:"${cn}\\s+(?:said|replied|whispered|murmured|asked|shouted)|(?:said|replied|whispered|murmured)\\s+${cn}|\\b${cn}\\s+(?:walked|stepped|turned|looked|reached|shoved|stirred|held|pulled|struck|sat|stood|laughed|nodded|chose|spoke)\\b)`,
208
+ "i"
209
+ );
210
+ matches = ch.cleanNarrative.match(actionPattern);
211
+ } catch {
212
+ matches = null; // malformed name → skip this rule rather than crash the whole scan
213
+ }
214
+ if (matches) {
215
+ this.violations.push({
216
+ type: "CHARACTER_RESURRECTION_VIOLATION",
217
+ severity: "critical",
218
+ character: charName,
219
+ chapter: ch.chapterIndex,
220
+ evidence: matches.slice(0, 2),
221
+ message: `Character "${charName}" was somatically deceased in Ch ${deathCh} but resumes active dialogue or agency in Ch ${ch.chapterIndex}.`,
222
+ });
223
+ }
224
+ }
225
+ }
226
+ }
227
+ }
228
+
229
+ public auditCounterfactualStability(): void {
230
+ const professions = [
231
+ "baker", "administrator", "vault clerk", "scholar", "gardener",
232
+ "clockmaker", "carpenter", "quarry laborer", "mason", "surveyor",
233
+ ];
234
+ const occurrences: Array<{ chapter: number; profession: string; isFemale?: boolean }> = [];
235
+
236
+ for (const ch of this.chapters) {
237
+ const textLower = ch.cleanNarrative.toLowerCase();
238
+ if (
239
+ textLower.includes("unlived") ||
240
+ textLower.includes("counterfactual") ||
241
+ textLower.includes("life he didn't choose") ||
242
+ textLower.includes("version of yourself") ||
243
+ textLower.includes("foreclosed future")
244
+ ) {
245
+ for (const prof of professions) {
246
+ if (textLower.includes(prof)) {
247
+ const isFemale = /\b(?:woman|she|wife|her|baker's daughter|elder songs)\b/i.test(ch.cleanNarrative);
248
+ occurrences.push({ chapter: ch.chapterIndex, profession: prof, isFemale });
249
+ break;
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ const uniqueProfs = new Set(occurrences.map((o) => o.profession));
256
+ if (uniqueProfs.size >= 3) {
257
+ this.violations.push({
258
+ type: "COUNTERFACTUAL_IDENTITY_MUTATION",
259
+ severity: "critical",
260
+ count: uniqueProfs.size,
261
+ evidence: occurrences.map((o) => `Ch ${o.chapter}: ${o.profession}${o.isFemale ? " (Sex Flip)" : ""}`),
262
+ message: `The protagonist's counterfactual unlived life mutated across ${uniqueProfs.size} distinct identities without dynamic state anchoring.`,
263
+ });
264
+ }
265
+ }
266
+
267
+ public auditSentenceLengths(): void {
268
+ if (!this.config.sentenceRange) return;
269
+ const [minLen, maxLen] = this.config.sentenceRange;
270
+ let totalSentences = 0;
271
+ const infractions: Array<{ chapter: number; length: number; snippet: string }> = [];
272
+
273
+ for (const ch of this.chapters) {
274
+ const sents = ch.cleanNarrative
275
+ .replace(/\s+/g, " ")
276
+ .split(/(?<=[.!?])\s+/)
277
+ .map((s) => s.trim())
278
+ .filter((s) => s.length > 5);
279
+
280
+ for (const sent of sents) {
281
+ const words = sent.split(/\s+/).filter(Boolean);
282
+ const wCount = words.length;
283
+ totalSentences++;
284
+ if (wCount < minLen || wCount > maxLen) {
285
+ infractions.push({
286
+ chapter: ch.chapterIndex,
287
+ length: wCount,
288
+ snippet: sent.slice(0, 60),
289
+ });
290
+ }
291
+ }
292
+ }
293
+
294
+ const violationRate = totalSentences > 0 ? (infractions.length / totalSentences) * 100 : 0;
295
+ if (violationRate > 15.0) {
296
+ this.violations.push({
297
+ type: "SENTENCE_LENGTH_CONSTRAINT_FAILURE",
298
+ severity: "warning",
299
+ count: infractions.length,
300
+ details: { totalSentences, violationRate: Math.round(violationRate * 10) / 10 },
301
+ evidence: infractions.slice(0, 4).map((i) => `Ch ${i.chapter} (${i.length}w): "${i.snippet}..."`),
302
+ message: `${Math.round(violationRate)}% of sentences violate the [${minLen}, ${maxLen}] word length constraint.`,
303
+ });
304
+ }
305
+ }
306
+
307
+ public auditMotifFrequencies(): void {
308
+ if (!this.config.motifLimits) return;
309
+ const fullText = this.chapters.map((c) => c.cleanNarrative).join(" ").toLowerCase();
310
+
311
+ const escM = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
312
+ for (const [motif, limit] of Object.entries(this.config.motifLimits)) {
313
+ let matches: RegExpMatchArray | null = null;
314
+ try {
315
+ matches = fullText.match(new RegExp(`\\b${escM(motif)}\\b`, "gi"));
316
+ } catch {
317
+ matches = null;
318
+ }
319
+ const count = matches ? matches.length : 0;
320
+ if (count > limit) {
321
+ this.violations.push({
322
+ type: "MOTIF_BUDGET_EXCEEDED",
323
+ severity: "warning",
324
+ count,
325
+ details: { motif, limit, actual: count, overflowRatio: Math.round((count / limit) * 10) / 10 },
326
+ message: `Motif "${motif}" appeared ${count} times (budget: ${limit}; exceeded by ${(count / limit).toFixed(1)}x).`,
327
+ });
328
+ }
329
+ }
330
+ }
331
+
332
+ public auditSpatialCarousel(): void {
333
+ // Detect cyclic ping-pong between <= 3 locations across 6+ chapters
334
+ const placePattern = /\b(?:Borderland|Dystopia|Village|Tulkaaklang|Granary Tower|Tanyard|Tithe Barn|Holdstrketh)\b/gi;
335
+ const trajectory: Array<{ chapter: number; place: string }> = [];
336
+
337
+ for (const ch of this.chapters) {
338
+ const match = ch.cleanNarrative.match(placePattern);
339
+ if (match) {
340
+ trajectory.push({ chapter: ch.chapterIndex, place: match[0].toLowerCase() });
341
+ }
342
+ }
343
+
344
+ if (trajectory.length >= 6) {
345
+ const distinct = new Set(trajectory.map((t) => t.place));
346
+ if (distinct.size <= 3 && trajectory.length >= 6) {
347
+ this.violations.push({
348
+ type: "MODULO_SPATIAL_CAROUSEL_TRAP",
349
+ severity: "warning",
350
+ count: trajectory.length,
351
+ evidence: trajectory.slice(0, 6).map((t) => `Ch ${t.chapter} -> ${t.place}`),
352
+ message: `Characters are trapped in a modulo setting carousel cycling between only ${distinct.size} locations across ${trajectory.length} checkpoints.`,
353
+ });
354
+ }
355
+ }
356
+ }
357
+
358
+ public auditClimaxEvasion(): void {
359
+ const finalChapters = this.chapters.slice(-3);
360
+ for (const ch of finalChapters) {
361
+ const match = ch.cleanNarrative.match(
362
+ /\b(?:lease has expired|administrative decree|renewal of records|petition is denied|court will issue an injunction|filing of an audit|fine of \d+)\b/i
363
+ );
364
+ if (match) {
365
+ this.violations.push({
366
+ type: "CLIMAX_EVASION_ADMINISTRATIVE",
367
+ severity: "critical",
368
+ chapter: ch.chapterIndex,
369
+ evidence: [match[0]],
370
+ message: `Act III Climax (Ch ${ch.chapterIndex}) was resolved by administrative paperwork ("${match[0]}") rather than dramatic confrontation.`,
371
+ });
372
+ }
373
+ }
374
+ }
375
+
376
+ public runAllAudits(): {
377
+ manuscriptId: string;
378
+ chapterCount: number;
379
+ totalViolations: number;
380
+ violations: NIVEViolation[];
381
+ cleanScore: number;
382
+ } {
383
+ this.auditPromptScaffoldingLeaks();
384
+ this.auditClonedChapterOpenings();
385
+ this.auditCharacterMortality();
386
+ if (this.config.trackCounterfactual) this.auditCounterfactualStability();
387
+ this.auditSentenceLengths();
388
+ this.auditMotifFrequencies();
389
+ if (this.config.trackCarousel) this.auditSpatialCarousel();
390
+ this.auditClimaxEvasion();
391
+
392
+ const criticals = this.violations.filter((v) => v.severity === "critical").length;
393
+ const warnings = this.violations.filter((v) => v.severity === "warning").length;
394
+ const cleanScore = Math.max(0, 100 - criticals * 20 - warnings * 8);
395
+
396
+ return {
397
+ manuscriptId: this.manuscriptId,
398
+ chapterCount: this.chapters.length,
399
+ totalViolations: this.violations.length,
400
+ violations: this.violations,
401
+ cleanScore,
402
+ };
403
+ }
404
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * PIPELINE SELF-TEST — pure, no network, no LLM.
3
+ * ---------------------------------------------------------------------------
4
+ * Proves the fiction generation stack can commit a full N-chapter novel
5
+ * without stalls, truncations, or chapter-count drift. Uses a deterministic
6
+ * mock generator so the test is reproducible in the build environment.
7
+ *
8
+ * What it verifies (hand-traceable):
9
+ * 1. parseChaptersFromText is stable on ##-headed manuscripts
10
+ * 2. foldFacts / continuity ledger stays bounded across N chapters
11
+ * 3. stripHarnessResidue kills resumption notices / scaffolding bleed
12
+ * 4. A full N-chapter commit loop yields exactly N chapters (no stuck chapter)
13
+ * 5. Seed locks apply to snapshot without throwing
14
+ * 6. decayEditBudget + classifyRevisionUnits stay honest under known diffs
15
+ */
16
+ import { parseChaptersFromText } from "./story-engine";
17
+ import {
18
+ EMPTY_LEDGER,
19
+ extractChapterFacts,
20
+ foldFacts,
21
+ stripHarnessResidue,
22
+ truncationVerdict,
23
+ normalizeChapterTitle,
24
+ } from "./continuity";
25
+ import { applySeedLocks, DEFAULT_SEED_CONTROL, selectSeedDirectives, buildSeedDirectiveBlock } from "./seed-control";
26
+ import { createStorySnapshot } from "./story-engine";
27
+ import { classifyRevisionUnits, decayEditBudget } from "../lib/ai-revision";
28
+
29
+ export type SelfTestCase = { name: string; pass: boolean; detail?: string };
30
+ export type SelfTestReport = { pass: boolean; cases: SelfTestCase[]; ranAt: number };
31
+
32
+ function mockChapter(i: number, title: string): string {
33
+ // Deterministic multi-paragraph body well above the 40-char parse floor and
34
+ // above the 60% truncation threshold for a 400-word commission.
35
+ const sentences = Array.from({ length: 24 }, (_, s) =>
36
+ `In scene ${s + 1} of chapter ${i}, the courier crossed the limestone step and noted the iron latch on the east door.`,
37
+ );
38
+ return `## ${title}\n\n${sentences.join(" ")}`;
39
+ }
40
+
41
+ export function runFictionPipelineSelfTest(chapterCount = 16): SelfTestReport {
42
+ const cases: SelfTestCase[] = [];
43
+ const seed = 42;
44
+
45
+ // 1. Title normalization
46
+ cases.push({
47
+ name: "normalizeChapterTitle collapses The The",
48
+ pass: normalizeChapterTitle("The The frontier Reckoning") === "The frontier Reckoning",
49
+ });
50
+
51
+ // 2. Residue strip kills resumption bleed
52
+ const bleed = "_Resuming at chapter 2; 1 finished chapter(s) restored, not regenerated._\nReal prose continues here with enough body.";
53
+ const stripped = stripHarnessResidue(bleed);
54
+ cases.push({
55
+ name: "stripHarnessResidue removes resumption notice",
56
+ pass: stripped.removed.includes("resumption-notice") && !/_Resuming at chapter/.test(stripped.text),
57
+ detail: `removed=${stripped.removed.join(",")}`,
58
+ });
59
+
60
+ // 3. Full N-chapter commit simulation
61
+ const snapshot = createStorySnapshot(seed, chapterCount);
62
+ applySeedLocks(snapshot, {
63
+ ...DEFAULT_SEED_CONTROL,
64
+ locks: { setting: "Locked Estuary", coreConcept: "the sealed canister", theme: "debts inherit", motif: "mended cloth" },
65
+ });
66
+ cases.push({
67
+ name: "applySeedLocks mutates primary setting",
68
+ pass: snapshot.blueprint.primarySetting.name === "Locked Estuary",
69
+ });
70
+
71
+ let ledger = EMPTY_LEDGER;
72
+ let manuscript = "";
73
+ for (let i = 0; i < chapterCount; i++) {
74
+ const title = snapshot.blueprint.chapters[i]?.title || `Chapter ${i + 1}`;
75
+ const body = mockChapter(i + 1, title).replace(/^##.+\n\n/, "");
76
+ // Seed block must build without throw
77
+ const seedBlock = buildSeedDirectiveBlock(selectSeedDirectives(seed, i, DEFAULT_SEED_CONTROL), DEFAULT_SEED_CONTROL);
78
+ if (!seedBlock.includes("SEED DIRECTIVES")) {
79
+ cases.push({ name: `seed block ch${i + 1}`, pass: false, detail: "missing header" });
80
+ }
81
+ const block = `## ${title}\n\n${body}`;
82
+ manuscript = manuscript ? `${manuscript}\n\n${block}` : block;
83
+ const facts = extractChapterFacts({ chapter: i + 1, title, text: body, snapshot });
84
+ ledger = foldFacts(ledger, facts);
85
+ const v = truncationVerdict(body, 400);
86
+ if (v.truncated) {
87
+ cases.push({ name: `mock ch${i + 1} not truncated`, pass: false, detail: v.reason });
88
+ }
89
+ }
90
+ const parsed = parseChaptersFromText(manuscript);
91
+ cases.push({
92
+ name: `full ${chapterCount}-chapter commit parses to exactly ${chapterCount}`,
93
+ pass: parsed.length === chapterCount,
94
+ detail: `parsed=${parsed.length}`,
95
+ });
96
+ cases.push({
97
+ name: "ledger facts length equals chapter count",
98
+ pass: ledger.facts.length === chapterCount,
99
+ detail: `facts=${ledger.facts.length}`,
100
+ });
101
+ cases.push({
102
+ name: "ledger motifTotals bounded (≤120)",
103
+ pass: Object.keys(ledger.motifTotals).length <= 120,
104
+ });
105
+
106
+ // 4. Parse must ignore bleed-only preambles
107
+ const withBleed = `_SAGE fiction run started: ${chapterCount} chapters_\n\n${manuscript}`;
108
+ const cleaned = stripHarnessResidue(withBleed).text;
109
+ const reparsed = parseChaptersFromText(cleaned);
110
+ cases.push({
111
+ name: "parse after bleed-strip still yields full chapter count",
112
+ pass: reparsed.length === chapterCount,
113
+ detail: `parsed=${reparsed.length}`,
114
+ });
115
+
116
+ // 5. Revision unit honesty
117
+ const before = "The red door opened. A cold wind entered the hall.\n\nShe waited.";
118
+ const after = "The blue door opened. A cold wind entered the hall.\n\nShe waited by the stair.";
119
+ const units = classifyRevisionUnits(before, after);
120
+ cases.push({
121
+ name: "classifyRevisionUnits counts word-level change",
122
+ pass: units.word >= 1 || units.phrase >= 1 || units.sentence >= 1,
123
+ detail: JSON.stringify(units),
124
+ });
125
+ const decay = decayEditBudget(20, false);
126
+ cases.push({
127
+ name: "decayEditBudget prefers word over page",
128
+ pass: (decay.find((d) => d.unit === "word")?.maxEdits ?? 0) > (decay.find((d) => d.unit === "page")?.maxEdits ?? 0),
129
+ });
130
+
131
+ return { pass: cases.every((c) => c.pass), cases, ranAt: Date.now() };
132
+ }