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.
- package/diagnostics.mjs +141 -0
- package/dist/compute.worker-j5ahIpF-.js +1 -0
- package/dist/graph.worker-DG6iGCB9.js +1 -0
- package/dist/index.html +2314 -0
- package/flatten-guide.md +248 -0
- package/index.html +12 -0
- package/package.json +38 -0
- package/src/App.orig.tsx +204 -0
- package/src/App.orig1.tsx +27 -0
- package/src/App.orig2.tsx +25 -0
- package/src/App.tsx +3 -0
- package/src/BaseApp.tsx +18 -0
- package/src/INTEGRATION_LEDGER.md +22 -0
- package/src/PERSIST_CANARY.orig.txt +1 -0
- package/src/PERSIST_CANARY.txt +1 -0
- package/src/REGRESSION_LEDGER.md +651 -0
- package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
- package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
- package/src/chat/VeritasChat.tsx +69 -0
- package/src/chat/synthesis.ts +31 -0
- package/src/chat/tier.ts +45 -0
- package/src/components/AdaptersPage.orig.tsx +99 -0
- package/src/components/AdaptersPage.tsx +3 -0
- package/src/components/AdversarialPanel.tsx +136 -0
- package/src/components/AnchorBaselinePanel.tsx +75 -0
- package/src/components/AnswerPanel.tsx +60 -0
- package/src/components/ArtifactPanel.tsx +43 -0
- package/src/components/ChatApp.orig.tsx +1582 -0
- package/src/components/ChatApp.orig1.tsx +722 -0
- package/src/components/ChatApp.tsx +95 -0
- package/src/components/ChatAugmentPanels.tsx +73 -0
- package/src/components/CitationLedgerPanel.tsx +313 -0
- package/src/components/ComputeSandboxPanel.tsx +130 -0
- package/src/components/ConfigPanel.tsx +110 -0
- package/src/components/ControlPlanePage.tsx +195 -0
- package/src/components/CreativeTreeLifePage.tsx +234 -0
- package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
- package/src/components/DebugTracePanel.tsx +137 -0
- package/src/components/DeepReasoningTrace.tsx +48 -0
- package/src/components/EntitySheetPanel.tsx +51 -0
- package/src/components/FailureModesPanel.tsx +104 -0
- package/src/components/GBSDashboard.tsx +239 -0
- package/src/components/GraphView.tsx +116 -0
- package/src/components/HUD.tsx +125 -0
- package/src/components/HypothesisPanel.tsx +67 -0
- package/src/components/InnovationGenomeEngine.tsx +792 -0
- package/src/components/InnovationPersonaGuide.tsx +348 -0
- package/src/components/InnovationPersonaPanel.tsx +289 -0
- package/src/components/LiveResourceHUD.tsx +109 -0
- package/src/components/LongWriterPanel.tsx +112 -0
- package/src/components/MainPipelineV10Bridge.tsx +72 -0
- package/src/components/MarkdownLite.tsx +63 -0
- package/src/components/MemoryInspector.tsx +38 -0
- package/src/components/MemoryMonitor.tsx +52 -0
- package/src/components/MemoryStressPanel.tsx +176 -0
- package/src/components/ModulesPage.tsx +171 -0
- package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
- package/src/components/PipelineDebugConsole.tsx +3 -0
- package/src/components/PreFlightHUD.tsx +67 -0
- package/src/components/PrismaFetchTracePanel.tsx +87 -0
- package/src/components/ReportOSPanel.tsx +165 -0
- package/src/components/ResourceEstimatorPage.tsx +327 -0
- package/src/components/RichText.tsx +181 -0
- package/src/components/SagaOsPanel.tsx +605 -0
- package/src/components/SagaOsReference.tsx +421 -0
- package/src/components/SagaOsV2Panel.tsx +594 -0
- package/src/components/SagaStyleTab.tsx +382 -0
- package/src/components/SageBlueprintPanel.tsx +416 -0
- package/src/components/SharedChatInput.tsx +77 -0
- package/src/components/StatFinancePanel.tsx +190 -0
- package/src/components/StylePersonaPanel.tsx +128 -0
- package/src/components/TemplatesPage.tsx +300 -0
- package/src/components/TestPanel.tsx +63 -0
- package/src/components/TraceLog.tsx +89 -0
- package/src/components/V15CalibrationAugment.tsx +1134 -0
- package/src/components/V15CalibrationDialog.tsx +1871 -0
- package/src/components/V15Overlay.orig.tsx +120 -0
- package/src/components/V15Overlay.tsx +4 -0
- package/src/components/V15OverlayWrapper.tsx +362 -0
- package/src/components/V15Toggle.tsx +34 -0
- package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
- package/src/console/EngineeringConsole.tsx +103 -0
- package/src/console/FictionSystemPanels.tsx +882 -0
- package/src/console/InspectorPanels.tsx +254 -0
- package/src/console/ManuscriptGraderPanel.tsx +182 -0
- package/src/console/RecursiveReviserPanel.tsx +245 -0
- package/src/console/SeedControlPanel.tsx +135 -0
- package/src/console/StoryControllerPanel.tsx +443 -0
- package/src/console/engineBridge.ts +258 -0
- package/src/console/gemini.ts +106 -0
- package/src/console/grading.ts +250 -0
- package/src/console/util.ts +45 -0
- package/src/fiction/continuity.ts +372 -0
- package/src/fiction/crash-protection.ts +111 -0
- package/src/fiction/diagnostics.ts +208 -0
- package/src/fiction/diff.ts +74 -0
- package/src/fiction/entity-state-machine.ts +214 -0
- package/src/fiction/nive-engine.ts +404 -0
- package/src/fiction/pipeline-selftest.ts +132 -0
- package/src/fiction/seed-control.ts +193 -0
- package/src/fiction/story-engine.ts +326 -0
- package/src/fiction/types.ts +68 -0
- package/src/flatten-guide.md +379 -0
- package/src/index.css +14 -0
- package/src/index.orig.css +13 -0
- package/src/lib/academic-sources.base.ts +188 -0
- package/src/lib/academic-sources.orig.ts +28 -0
- package/src/lib/academic-sources.ts +10 -0
- package/src/lib/advanced-math.ts +119 -0
- package/src/lib/adversarial-engine.base.ts +409 -0
- package/src/lib/adversarial-engine.ts +82 -0
- package/src/lib/ai-revision.ts +623 -0
- package/src/lib/app-state.tsx +426 -0
- package/src/lib/artifact-registry.ts +97 -0
- package/src/lib/artifacts.ts +180 -0
- package/src/lib/atlas-dr.ts +167 -0
- package/src/lib/browser-mtls.ts +127 -0
- package/src/lib/browser-queue.ts +221 -0
- package/src/lib/browser-search-scraper.base.ts +382 -0
- package/src/lib/browser-search-scraper.orig.ts +90 -0
- package/src/lib/browser-search-scraper.ts +10 -0
- package/src/lib/calc-interceptor.ts +82 -0
- package/src/lib/citation-lane-tap.ts +78 -0
- package/src/lib/citation-ledger-store.ts +369 -0
- package/src/lib/citation-ledger.ts +203 -0
- package/src/lib/compute-sandbox.ts +215 -0
- package/src/lib/compute.worker.ts +153 -0
- package/src/lib/connectors/gemini.ts +120 -0
- package/src/lib/connectors/jina.ts +233 -0
- package/src/lib/connectors/marketdata.ts +51 -0
- package/src/lib/connectors/prismafetch.base.ts +175 -0
- package/src/lib/connectors/prismafetch.ts +1 -0
- package/src/lib/connectors/serpapi.ts +65 -0
- package/src/lib/connectors/wikidata.ts +44 -0
- package/src/lib/constraints.ts +331 -0
- package/src/lib/continuation-detector.base.ts +153 -0
- package/src/lib/continuation-detector.ts +48 -0
- package/src/lib/contradraft.ts +168 -0
- package/src/lib/cors-proxy.ts +120 -0
- package/src/lib/coverage.ts +81 -0
- package/src/lib/debug/architecture-prescription.ts +556 -0
- package/src/lib/debug/covea-repair.ts +543 -0
- package/src/lib/debug/helios-ground.ts +319 -0
- package/src/lib/debug/intent-decomposer.ts +281 -0
- package/src/lib/debug/intent-lattice.ts +349 -0
- package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
- package/src/lib/debug/pipeline-trace-bus.ts +424 -0
- package/src/lib/debug/prompt-forge.ts +678 -0
- package/src/lib/debug/repair-sites.ts +513 -0
- package/src/lib/debug/research-phase.ts +266 -0
- package/src/lib/debug/retrieval-context.ts +76 -0
- package/src/lib/debug/retrieval-hardener.ts +142 -0
- package/src/lib/debug/scraper-debug-runner.ts +576 -0
- package/src/lib/debug/scraper-forensics.ts +418 -0
- package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
- package/src/lib/debug/self-test.ts +415 -0
- package/src/lib/debug/step-attribution.ts +211 -0
- package/src/lib/debug/template-rubric.ts +301 -0
- package/src/lib/debug/unified-innovation.ts +126 -0
- package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
- package/src/lib/defense-registry.ts +104 -0
- package/src/lib/defenses.ts +162 -0
- package/src/lib/deterministic-citation-ledger.ts +174 -0
- package/src/lib/elo-registry.base.ts +117 -0
- package/src/lib/elo-registry.ts +1 -0
- package/src/lib/entity-resolver.ts +291 -0
- package/src/lib/failure-modes.ts +267 -0
- package/src/lib/feature-registry.ts +69 -0
- package/src/lib/flaw-registry.ts +143 -0
- package/src/lib/flaws/_template.ts +52 -0
- package/src/lib/flaws/builtins.ts +63 -0
- package/src/lib/flaws/finance.ts +169 -0
- package/src/lib/flaws/fixers.ts +10 -0
- package/src/lib/flaws/index.ts +32 -0
- package/src/lib/flaws/legal.ts +157 -0
- package/src/lib/flaws/medical.ts +188 -0
- package/src/lib/flaws/original-defenses-pack.ts +81 -0
- package/src/lib/flaws/sample-declarative-pack.json +38 -0
- package/src/lib/flaws/selftest.ts +201 -0
- package/src/lib/flaws/software-extended.ts +125 -0
- package/src/lib/flaws/software-rn-webgl.ts +182 -0
- package/src/lib/flaws/statistics-advanced.ts +46 -0
- package/src/lib/flaws/statistics.ts +61 -0
- package/src/lib/gbse/config.ts +20 -0
- package/src/lib/gbse/engine.ts +226 -0
- package/src/lib/gbse/graph.ts +118 -0
- package/src/lib/gbse/graph.worker.ts +21 -0
- package/src/lib/gbse/tests.ts +117 -0
- package/src/lib/gbse/tiers.ts +65 -0
- package/src/lib/gbse/types.ts +94 -0
- package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
- package/src/lib/innovation-genome-engine-v2.ts +3 -0
- package/src/lib/innovation-genome-engine.orig.ts +330 -0
- package/src/lib/innovation-genome-engine.ts +3 -0
- package/src/lib/innovation-genome-v10.orig.ts +314 -0
- package/src/lib/innovation-genome-v10.ts +2 -0
- package/src/lib/innovation-genome-v3.orig.ts +2015 -0
- package/src/lib/innovation-genome-v3.ts +2 -0
- package/src/lib/innovation-genome-v4.orig.ts +1959 -0
- package/src/lib/innovation-genome-v4.ts +2 -0
- package/src/lib/innovation-genome-v5.orig.ts +1210 -0
- package/src/lib/innovation-genome-v5.ts +2 -0
- package/src/lib/innovation-genome-v7.orig.ts +2549 -0
- package/src/lib/innovation-genome-v7.ts +2 -0
- package/src/lib/innovation-genome-v8.orig.ts +1485 -0
- package/src/lib/innovation-genome-v8.ts +2 -0
- package/src/lib/innovation-genome-v9.orig.ts +643 -0
- package/src/lib/innovation-genome-v9.ts +2 -0
- package/src/lib/jina.base.ts +293 -0
- package/src/lib/jina.ts +1 -0
- package/src/lib/live-telemetry.ts +61 -0
- package/src/lib/longwriter.ts +84 -0
- package/src/lib/manuscript-grade-v2.ts +298 -0
- package/src/lib/memory-governor.ts +173 -0
- package/src/lib/memory-stress-tests.ts +248 -0
- package/src/lib/model-intelligence.ts +48 -0
- package/src/lib/model-rotator.ts +75 -0
- package/src/lib/models.orig.ts +513 -0
- package/src/lib/models.ts +151 -0
- package/src/lib/n-deep.base.ts +592 -0
- package/src/lib/n-deep.ts +51 -0
- package/src/lib/nih-simulator.ts +56 -0
- package/src/lib/nih-vulnerability-fixes.ts +460 -0
- package/src/lib/omega-templates.base.ts +294 -0
- package/src/lib/omega-templates.ts +225 -0
- package/src/lib/omni-nexus.ts +64 -0
- package/src/lib/oracle-adapters.ts +67 -0
- package/src/lib/oracle-registry.ts +163 -0
- package/src/lib/orchestrator.ts +357 -0
- package/src/lib/output-boundary.ts +235 -0
- package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
- package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
- package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
- package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
- package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
- package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
- package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
- package/src/lib/overrides/vite-native-scraper.ts +848 -0
- package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
- package/src/lib/overrides/vite-native-selftest.ts +183 -0
- package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
- package/src/lib/philosophy-toolkit.ts +75 -0
- package/src/lib/pipeline.orig.ts +659 -0
- package/src/lib/pipeline.ts +42 -0
- package/src/lib/precache.ts +128 -0
- package/src/lib/py-sandbox.ts +205 -0
- package/src/lib/quality-score.ts +96 -0
- package/src/lib/quant-engine.ts +231 -0
- package/src/lib/quant-lib.ts +80 -0
- package/src/lib/reportos.ts +175 -0
- package/src/lib/research-os.ts +306 -0
- package/src/lib/resource-estimator.ts +114 -0
- package/src/lib/rpm-governor.ts +93 -0
- package/src/lib/runtime-estimator.ts +135 -0
- package/src/lib/sage/empty-space-v6.ts +560 -0
- package/src/lib/sage/empty-space-v7.ts +222 -0
- package/src/lib/sage/empty-space.ts +733 -0
- package/src/lib/sage/engine.ts +786 -0
- package/src/lib/sage/index.ts +17 -0
- package/src/lib/sage/judge.ts +246 -0
- package/src/lib/sage/prng.ts +53 -0
- package/src/lib/sage/reality-grounding.ts +380 -0
- package/src/lib/sage/repetition.ts +151 -0
- package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
- package/src/lib/sage/saga-os-v2.ts +274 -0
- package/src/lib/sage/saga-os-v3-core.ts +1491 -0
- package/src/lib/sage/saga-os-v3-release.ts +739 -0
- package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
- package/src/lib/sage/saga-os-v4.ts +212 -0
- package/src/lib/sage/saga-os-v5.ts +205 -0
- package/src/lib/sage/saga-os-v6.ts +346 -0
- package/src/lib/sage/saga-os-v7.ts +314 -0
- package/src/lib/sage/saga-os-v8.ts +505 -0
- package/src/lib/sage/saga-os.ts +329 -0
- package/src/lib/sage/taxonomy.ts +403 -0
- package/src/lib/sage/unified-roll.ts +524 -0
- package/src/lib/sage/worked-sketches.ts +44 -0
- package/src/lib/sage.ts +166 -0
- package/src/lib/scraper-debug-bus.ts +53 -0
- package/src/lib/scraper-enhanced.orig.ts +153 -0
- package/src/lib/scraper-enhanced.ts +10 -0
- package/src/lib/scraper-hardener.base.orig.ts +155 -0
- package/src/lib/scraper-hardener.base.ts +317 -0
- package/src/lib/scraper-hardener.ts +1 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
- package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
- package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
- package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
- package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
- package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
- package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
- package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
- package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
- package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
- package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
- package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
- package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
- package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
- package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
- package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
- package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
- package/src/lib/scraper-vnext/query-strategist.ts +423 -0
- package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
- package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
- package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
- package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
- package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
- package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
- package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
- package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
- package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
- package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
- package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
- package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
- package/src/lib/scraper-vnext/strata-engine.ts +13 -0
- package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
- package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
- package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
- package/src/lib/scraper-vnext/terminal-final.ts +277 -0
- package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
- package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
- package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
- package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
- package/src/lib/search-cache.ts +138 -0
- package/src/lib/sloop-runner.base.ts +223 -0
- package/src/lib/sloop-runner.ts +104 -0
- package/src/lib/sloop.ts +114 -0
- package/src/lib/small-model-adapters.ts +82 -0
- package/src/lib/sscp.ts +86 -0
- package/src/lib/system-map.ts +54 -0
- package/src/lib/uni-node.ts +86 -0
- package/src/lib/universal-rigor-guard.ts +433 -0
- package/src/lib/v15-gate-testbed.ts +274 -0
- package/src/lib/v15-gemini.base.ts +89 -0
- package/src/lib/v15-gemini.ts +1 -0
- package/src/lib/v15-grounding.orig.ts +241 -0
- package/src/lib/v15-grounding.ts +79 -0
- package/src/lib/v15-pipeline.base.ts +769 -0
- package/src/lib/v15-pipeline.orig.ts +1154 -0
- package/src/lib/v15-pipeline.ts +674 -0
- package/src/lib/v15-questions.ts +64 -0
- package/src/lib/v15-rate-limiter.orig.ts +91 -0
- package/src/lib/v15-rate-limiter.ts +48 -0
- package/src/lib/v15-state.base.ts +87 -0
- package/src/lib/v15-state.ts +28 -0
- package/src/lib/visual-table-generator.tsx +207 -0
- package/src/lib/wasm-runtime.ts +133 -0
- package/src/lib/williams-persona-guide.ts +265 -0
- package/src/lib/williams-style.base.ts +464 -0
- package/src/lib/williams-style.ts +59 -0
- package/src/lib/worker-pool.ts +96 -0
- package/src/lib/writing-tiers.ts +125 -0
- package/src/main.orig.tsx +10 -0
- package/src/main.tsx +10 -0
- package/src/utils/cn.orig.ts +6 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/unify.mjs +537 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI REVISION v3 — Pareto-Superior Targeted Recursive Revision Loop.
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* Solves the core pathology where scores previously fell or stagnated:
|
|
5
|
+
* 1. WEIGHTED-DEFICIT TARGETING:
|
|
6
|
+
* Plot (20%) + Character (20%) + Cleanliness (15%) = 55% of the rubric and
|
|
7
|
+
* CANNOT move under word-level cosmetic substitutions. We compute exact
|
|
8
|
+
* weighted deficits per category: Deficit_k = (Target - Score_k) * Weight_k.
|
|
9
|
+
* 2. L0 -> L1 -> L2 ESCALATION WITH INSERTS & ROTATING MECHANISMS:
|
|
10
|
+
* - L0 (Micro-edit): Line-level prose polish and cosmetic tuning.
|
|
11
|
+
* - L1 (Scene-level insert): Missing dramatic confrontation, character agency,
|
|
12
|
+
* and direct dialogue subtext inserts.
|
|
13
|
+
* - L2 (Macro-structural reversal insert): Irreversible sacrifice, antagonist
|
|
14
|
+
* active intervention, true climax staging, and elimination of administrative
|
|
15
|
+
* anti-climaxes.
|
|
16
|
+
* 3. TWO-MODEL NOISE CALIBRATION (Promotion Margin):
|
|
17
|
+
* Judge LLMs have inherent stochastic variance (~1.0 pt). We enforce a
|
|
18
|
+
* calibrated promotion threshold (+0.5 pt margin) so stochastic noise does
|
|
19
|
+
* not promote degenerate or disguised regressions.
|
|
20
|
+
* 4. PER-CRITERION REGRESSION GUARD:
|
|
21
|
+
* If an iteration gains on Prose Style but regresses on Plot Architecture or
|
|
22
|
+
* Character Voice by more than the tolerance, it is rejected and rolled back.
|
|
23
|
+
* 5. DIAGNOSE STAGNATION (Plateau vs Decline):
|
|
24
|
+
* Detects plateau (score delta within +/- 0.5 for 2 turns) vs decline (score drop),
|
|
25
|
+
* surfaces exact diagnostic explanations, and triggers immediate escalation to
|
|
26
|
+
* higher-level insertion mechanisms.
|
|
27
|
+
* 6. 15 NEGATIVE-SPACE SHAPES & 6-MECHANISM PORTFOLIO:
|
|
28
|
+
* Explicitly embedded into the prompt, forcing the LLM to verify and avoid all
|
|
29
|
+
* 15 failure modes (FM-02 to FM-16) while executing distinct micro-architectures.
|
|
30
|
+
*/
|
|
31
|
+
import { gradeManuscriptV2, type ManuscriptGradeReportV2 } from "./manuscript-grade-v2";
|
|
32
|
+
|
|
33
|
+
export interface RevisionModel {
|
|
34
|
+
provider: string;
|
|
35
|
+
modelId: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type EscalationLevel = "L0-micro" | "L1-scene-insert" | "L2-macro-reversal";
|
|
39
|
+
|
|
40
|
+
export type RevisionProgressEvent =
|
|
41
|
+
| { phase: "grading-initial" }
|
|
42
|
+
| { phase: "grading-initial-done"; report: ManuscriptGradeReportV2 }
|
|
43
|
+
| { phase: "revising"; iteration: number; model: RevisionModel; escalation: EscalationLevel; targetCategory: string }
|
|
44
|
+
| { phase: "revising-done"; iteration: number; changedRatioPct: number; anomaly?: string; stagnationDiagnosis?: string }
|
|
45
|
+
| { phase: "grading"; iteration: number; model: RevisionModel }
|
|
46
|
+
| { phase: "grading-done"; iteration: number; report: ManuscriptGradeReportV2; accepted: boolean; rejectionReason?: string }
|
|
47
|
+
| { phase: "error"; message: string }
|
|
48
|
+
| { phase: "done"; result: RevisionLoopResult };
|
|
49
|
+
|
|
50
|
+
export interface RevisionIteration {
|
|
51
|
+
iteration: number;
|
|
52
|
+
beforeScore: number;
|
|
53
|
+
afterScore: number;
|
|
54
|
+
reviseModel: RevisionModel;
|
|
55
|
+
gradeModel: RevisionModel;
|
|
56
|
+
changedRatioPct: number;
|
|
57
|
+
escalation: EscalationLevel;
|
|
58
|
+
accepted: boolean;
|
|
59
|
+
rejectionReason?: string;
|
|
60
|
+
anomaly?: string;
|
|
61
|
+
stagnationDiagnosis?: string;
|
|
62
|
+
unitCounts?: UnitCounts;
|
|
63
|
+
budgetUsedPct?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface RevisionLoopResult {
|
|
67
|
+
status: "target-met" | "budget-exhausted" | "error";
|
|
68
|
+
initialReport: ManuscriptGradeReportV2 | null;
|
|
69
|
+
finalReport: ManuscriptGradeReportV2 | null;
|
|
70
|
+
iterations: RevisionIteration[];
|
|
71
|
+
finalText: string;
|
|
72
|
+
error?: string;
|
|
73
|
+
plateauDetected?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Approximate changed ratio via common prefix/suffix. */
|
|
77
|
+
export function changedRatioPct(a: string, b: string): number {
|
|
78
|
+
if (a === b) return 0;
|
|
79
|
+
const maxLen = Math.max(a.length, b.length, 1);
|
|
80
|
+
let prefix = 0;
|
|
81
|
+
const minLen = Math.min(a.length, b.length);
|
|
82
|
+
while (prefix < minLen && a.charCodeAt(prefix) === b.charCodeAt(prefix)) prefix++;
|
|
83
|
+
let suffix = 0;
|
|
84
|
+
while (
|
|
85
|
+
suffix < minLen - prefix &&
|
|
86
|
+
a.charCodeAt(a.length - 1 - suffix) === b.charCodeAt(b.length - 1 - suffix)
|
|
87
|
+
)
|
|
88
|
+
suffix++;
|
|
89
|
+
const unchanged = prefix + suffix;
|
|
90
|
+
return Math.round(Math.min(100, Math.max(0, (1 - unchanged / maxLen) * 100)));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function extractChapterHeadings(text: string): string[] {
|
|
94
|
+
const heads: string[] = [];
|
|
95
|
+
for (const line of text.split("\n")) {
|
|
96
|
+
const t = line.trim();
|
|
97
|
+
if (/^(#{1,3}\s+|chapter\s+\d+|part\s+\d+)/i.test(t) && t.length < 120) heads.push(t.slice(0, 100));
|
|
98
|
+
if (heads.length >= 40) break;
|
|
99
|
+
}
|
|
100
|
+
return heads;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface CategoryDeficit {
|
|
104
|
+
key: string;
|
|
105
|
+
label: string;
|
|
106
|
+
weightPct: number;
|
|
107
|
+
currentScore: number;
|
|
108
|
+
weightedDeficit: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function computeCategoryDeficits(report: ManuscriptGradeReportV2, targetScore: number): CategoryDeficit[] {
|
|
112
|
+
return report.categories
|
|
113
|
+
.map((c) => {
|
|
114
|
+
const deficit = Math.max(0, targetScore - c.score);
|
|
115
|
+
const weightedDeficit = Math.round(deficit * (c.weightPct / 100) * 100) / 100;
|
|
116
|
+
return {
|
|
117
|
+
key: c.key,
|
|
118
|
+
label: c.label,
|
|
119
|
+
weightPct: c.weightPct,
|
|
120
|
+
currentScore: c.score,
|
|
121
|
+
weightedDeficit,
|
|
122
|
+
};
|
|
123
|
+
})
|
|
124
|
+
.sort((a, b) => b.weightedDeficit - a.weightedDeficit);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Determine escalation level based on iteration history and largest deficit category. */
|
|
128
|
+
export function determineEscalation(
|
|
129
|
+
iteration: number,
|
|
130
|
+
consecutivePlateaus: number,
|
|
131
|
+
topDeficit: CategoryDeficit,
|
|
132
|
+
): EscalationLevel {
|
|
133
|
+
// If the top deficit is Plot or Character and we have stagnated, or iteration >= 2, escalate to inserts
|
|
134
|
+
if (["plot", "character", "cleanliness"].includes(topDeficit.key)) {
|
|
135
|
+
if (iteration >= 3 || consecutivePlateaus >= 1) return "L2-macro-reversal";
|
|
136
|
+
if (iteration >= 2) return "L1-scene-insert";
|
|
137
|
+
}
|
|
138
|
+
if (consecutivePlateaus >= 2) return "L2-macro-reversal";
|
|
139
|
+
if (consecutivePlateaus >= 1 || iteration >= 2) return "L1-scene-insert";
|
|
140
|
+
return "L0-micro";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Diagnosis of stagnation separating plateau from decline. */
|
|
144
|
+
export function diagnoseStagnation(history: RevisionIteration[]): { isStagnant: boolean; isDecline: boolean; diagnosis: string } {
|
|
145
|
+
if (history.length < 2) return { isStagnant: false, isDecline: false, diagnosis: "Initial trajectory establishing." };
|
|
146
|
+
const last = history[history.length - 1];
|
|
147
|
+
const prev = history[history.length - 2];
|
|
148
|
+
const delta = last.afterScore - prev.afterScore;
|
|
149
|
+
|
|
150
|
+
if (delta < -1.5) {
|
|
151
|
+
return {
|
|
152
|
+
isStagnant: true,
|
|
153
|
+
isDecline: true,
|
|
154
|
+
diagnosis: `Score dropped by ${Math.abs(delta).toFixed(2)} pts. Cause: cosmetic edits disturbed rhythm or caused pronoun/POV drift without adding narrative substance.`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (Math.abs(delta) <= 0.6) {
|
|
158
|
+
return {
|
|
159
|
+
isStagnant: true,
|
|
160
|
+
isDecline: false,
|
|
161
|
+
diagnosis: `Score plateaued (Δ ${delta > 0 ? "+" : ""}${delta.toFixed(2)} pts). Cause: Plot & Character deficits cannot move via word-level substitution. Escalation to scene/reversal insertion required.`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { isStagnant: false, isDecline: false, diagnosis: `Active progress: +${delta.toFixed(2)} pts.` };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** 15 Negative-space shapes to avoid (derived from FM-02 through FM-16). */
|
|
168
|
+
export const FIFTEEN_NEGATIVE_PATTERNS = [
|
|
169
|
+
"1. Re-staging the opening (FM-02): Do not rewrite Chapter 1's Call to Adventure when revising later chapters.",
|
|
170
|
+
"2. Procedural Deadlock (FM-03): Do not have characters enter a room, take measurements, and cancel threats with a form.",
|
|
171
|
+
"3. Severe Truncation (FM-04): Do not truncate chapters into brief 300-word summaries; preserve fully fleshed-out scenes.",
|
|
172
|
+
"4. Wound Tag Fatigue (FM-05): Do not repeat literal story-bible wound tags ('survivor guilt was a cloak', 'love's trap'); show them via actions.",
|
|
173
|
+
"5. Double Article Glitch (FM-06): Do not output 'The The' or 'A A' in chapter titles.",
|
|
174
|
+
"6. Citation Leakage (FM-07): Never emit bracketed metadata ([Source 1], [CHECKSUM], [VERIFIED]).",
|
|
175
|
+
"7. POV Slippage (FM-08): Do not flip second-person ('You') to third-person ('He/Him') during line-edits.",
|
|
176
|
+
"8. Climax Evasion (FM-09): Do not resolve the central confrontation via an administrative decree, petition, or audit filing.",
|
|
177
|
+
"9. Spatial Whiplash (FM-10): Do not jump characters between distant incompatible locations without transit logistics.",
|
|
178
|
+
"10. Epistemic Concept Overload (FM-11): Do not dump 5 theoretical doctrines into dialogue in one scene.",
|
|
179
|
+
"11. Handoff Duplication (FM-12): Do not duplicate the exact same cognitive transfer/revelation in multiple chapters.",
|
|
180
|
+
"12. Temporal Claustrophobia (FM-13): Do not compress an epic journey into a 3-hour morning commute.",
|
|
181
|
+
"13. Instrument/Sensor Fetishism (FM-14): Do not pad prose with lists of hertz, decibels, millibars, and picoseconds.",
|
|
182
|
+
"14. Currency Bookkeeping (FM-15): Do not reduce character interactions to ledgers, coin debits, and fines.",
|
|
183
|
+
"15. Consequence Annihilation (FM-16): When a character makes a sacrifice, do not undo or ignore it in the next chapter.",
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
/** 6 Mechanism Portfolio for targeted resolution. */
|
|
187
|
+
export const SIX_MECHANISM_PORTFOLIO = [
|
|
188
|
+
"M1 (Irreversible Choice): Protagonist must destroy an asset or sever an alliance to escape, permanently closing an exit.",
|
|
189
|
+
"M2 (Antagonist Coercion): Antagonist physically enforces their deadline or seizes territory on the page, forcing real stakes.",
|
|
190
|
+
"M3 (Subtext Dialogue): Replace exposition with dialogue where characters talk about one thing while meaning another.",
|
|
191
|
+
"M4 (Active Transit): Bridge any location shift with sensory transit (distance, mode, physical fatigue, cost of passage).",
|
|
192
|
+
"M5 (Dramatized Climax): Stage the final confrontation face-to-face with dialogue and high-stakes choice, not a legal memo.",
|
|
193
|
+
"M6 (Physicalized Consequence): Show character wounds through bodily reflexes, hesitation, and tactical mistakes rather than internal monologue.",
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
// ── v4: rewrite budget, edit units, exponential decay ────────────────────────
|
|
197
|
+
export type EditUnit = "word" | "phrase" | "sentence" | "paragraph" | "page" | "chapter";
|
|
198
|
+
export const EDIT_UNIT_ORDER: EditUnit[] = ["word", "phrase", "sentence", "paragraph", "page", "chapter"];
|
|
199
|
+
|
|
200
|
+
export interface RevisionBudgetOptions {
|
|
201
|
+
/** Max fraction of the manuscript that may change, % (1-50). Default 25. */
|
|
202
|
+
maxRewritePct: number;
|
|
203
|
+
/** Max contiguous replacement length in paragraphs (1-30). Default 10. */
|
|
204
|
+
maxReplacementParagraphs: number;
|
|
205
|
+
/** When true, one full-chapter replacement is permitted as the absolute max. Default false. */
|
|
206
|
+
allowChapterReplacement: boolean;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export const DEFAULT_REVISION_BUDGET: RevisionBudgetOptions = {
|
|
210
|
+
maxRewritePct: 25,
|
|
211
|
+
maxReplacementParagraphs: 10,
|
|
212
|
+
allowChapterReplacement: false,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export function normalizeBudget(b?: Partial<RevisionBudgetOptions>): RevisionBudgetOptions {
|
|
216
|
+
const n = (v: unknown, lo: number, hi: number, fb: number) => {
|
|
217
|
+
const x = typeof v === "number" ? v : Number(v);
|
|
218
|
+
return Number.isFinite(x) ? Math.min(hi, Math.max(lo, Math.round(x))) : fb;
|
|
219
|
+
};
|
|
220
|
+
return {
|
|
221
|
+
maxRewritePct: n(b?.maxRewritePct, 1, 50, DEFAULT_REVISION_BUDGET.maxRewritePct),
|
|
222
|
+
maxReplacementParagraphs: n(b?.maxReplacementParagraphs, 1, 30, DEFAULT_REVISION_BUDGET.maxReplacementParagraphs),
|
|
223
|
+
allowChapterReplacement: b?.allowChapterReplacement === true,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface UnitBudget { unit: EditUnit; sharePct: number; maxEdits: number }
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Exponential-decay edit budget: smallest units get the most edits.
|
|
231
|
+
* weight(level) = exp(-0.9 * level); shares normalized to 100%.
|
|
232
|
+
* A "page" = 3 consecutive paragraphs; "chapter" is only budgeted when allowed.
|
|
233
|
+
*/
|
|
234
|
+
export function decayEditBudget(totalEdits: number, allowChapter: boolean): UnitBudget[] {
|
|
235
|
+
const levels = allowChapter ? EDIT_UNIT_ORDER : EDIT_UNIT_ORDER.slice(0, 5);
|
|
236
|
+
const weights = levels.map((_, i) => Math.exp(-0.9 * i));
|
|
237
|
+
const sum = weights.reduce((a, b) => a + b, 0);
|
|
238
|
+
return levels.map((unit, i) => ({
|
|
239
|
+
unit,
|
|
240
|
+
sharePct: Math.round((weights[i] / sum) * 100),
|
|
241
|
+
maxEdits: Math.max(unit === "word" ? 1 : 0, Math.round((totalEdits * weights[i]) / sum)),
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export type UnitCounts = Record<EditUnit, number>;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Honest heuristic classification of what changed between drafts.
|
|
249
|
+
* word: isolated single-word substitution; phrase: a changed run of 2-3 words
|
|
250
|
+
* (a word change recurring 2x within 3 words is a phrase, per spec);
|
|
251
|
+
* sentence/paragraph: changed sentences/paragraphs; page: 3+ consecutive
|
|
252
|
+
* changed paragraphs; chapter: >60% of paragraphs changed.
|
|
253
|
+
*/
|
|
254
|
+
export function classifyRevisionUnits(before: string, after: string): UnitCounts {
|
|
255
|
+
const counts: UnitCounts = { word: 0, phrase: 0, sentence: 0, paragraph: 0, page: 0, chapter: 0 };
|
|
256
|
+
if (before === after) return counts;
|
|
257
|
+
const bParas = before.split(/\n{2,}/);
|
|
258
|
+
const aParas = after.split(/\n{2,}/);
|
|
259
|
+
const maxLen = Math.max(bParas.length, aParas.length, 1);
|
|
260
|
+
let changedParas = 0;
|
|
261
|
+
let run = 0;
|
|
262
|
+
const n = Math.max(bParas.length, aParas.length);
|
|
263
|
+
for (let i = 0; i < n; i++) {
|
|
264
|
+
const b = (bParas[i] ?? "").trim();
|
|
265
|
+
const a = (aParas[i] ?? "").trim();
|
|
266
|
+
if (b === a) {
|
|
267
|
+
if (run >= 3) counts.page += Math.floor(run / 3);
|
|
268
|
+
run = 0;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
changedParas++;
|
|
272
|
+
run++;
|
|
273
|
+
counts.paragraph++;
|
|
274
|
+
// sentence-level diff within the paragraph
|
|
275
|
+
const bS = b.split(/(?<=[.!?])\s+/).filter(Boolean);
|
|
276
|
+
const aS = a.split(/(?<=[.!?])\s+/).filter(Boolean);
|
|
277
|
+
const m = Math.max(bS.length, aS.length);
|
|
278
|
+
for (let s = 0; s < m; s++) {
|
|
279
|
+
const bs = (bS[s] ?? "").trim();
|
|
280
|
+
const as = (aS[s] ?? "").trim();
|
|
281
|
+
if (bs === as || !bs || !as) continue;
|
|
282
|
+
counts.sentence++;
|
|
283
|
+
// word-run analysis: changed runs of 1 word = word edit, 2-3 = phrase, longer = sentence-level (already counted)
|
|
284
|
+
const bw = bs.split(/\s+/);
|
|
285
|
+
const aw = as.split(/\s+/);
|
|
286
|
+
let runLen = 0;
|
|
287
|
+
const flush = () => {
|
|
288
|
+
if (runLen === 1) counts.word++;
|
|
289
|
+
else if (runLen >= 2 && runLen <= 3) counts.phrase++;
|
|
290
|
+
runLen = 0;
|
|
291
|
+
};
|
|
292
|
+
const wmax = Math.max(bw.length, aw.length);
|
|
293
|
+
for (let w = 0; w < wmax; w++) {
|
|
294
|
+
if ((bw[w] ?? "") === (aw[w] ?? "")) flush();
|
|
295
|
+
else runLen++;
|
|
296
|
+
}
|
|
297
|
+
flush();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (run >= 3) counts.page += Math.floor(run / 3);
|
|
301
|
+
if (changedParas / maxLen > 0.6) counts.chapter = 1;
|
|
302
|
+
return counts;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Per-category delta directives: NEW instructions targeting THIS draft's movement. */
|
|
306
|
+
export function buildDynamicDirectives(
|
|
307
|
+
prevReport: ManuscriptGradeReportV2 | null,
|
|
308
|
+
currentReport: ManuscriptGradeReportV2,
|
|
309
|
+
stallCounts: Record<string, number>,
|
|
310
|
+
): string[] {
|
|
311
|
+
const out: string[] = [];
|
|
312
|
+
for (const c of currentReport.categories) {
|
|
313
|
+
const prev = prevReport?.categories.find((p) => p.key === c.key)?.score;
|
|
314
|
+
const delta = prev == null ? null : Math.round((c.score - prev) * 100) / 100;
|
|
315
|
+
const stalls = stallCounts[c.key] ?? 0;
|
|
316
|
+
if (delta == null) {
|
|
317
|
+
out.push(`NEW INSTRUCTION [${c.label} ${c.score}/100]: first scored draft — attack its weakest quoted evidence first (see rewrite pairs).`);
|
|
318
|
+
} else if (delta <= -1) {
|
|
319
|
+
out.push(`NEW INSTRUCTION [${c.label} ${prev}→${c.score} REGRESSED]: revert the approach that caused this drop; make ZERO ${c.label.toLowerCase()} edits except one minimal repair to the single worst passage quoted in the latest report.`);
|
|
320
|
+
} else if (stalls >= 2 || (Math.abs(delta) < 0.6 && stalls >= 1)) {
|
|
321
|
+
out.push(`NEW INSTRUCTION [${c.label} STALLED ${stalls + 1} rounds at ~${c.score}]: escalate one unit level (word→sentence→paragraph) and change tactic entirely — use a different mechanism from the portfolio than prior rounds.`);
|
|
322
|
+
} else if (delta > 0) {
|
|
323
|
+
out.push(`NEW INSTRUCTION [${c.label} improving +${delta}]: continue the same tactic on the NEXT-weakest passage only; do not revisit already-fixed text.`);
|
|
324
|
+
} else {
|
|
325
|
+
out.push(`NEW INSTRUCTION [${c.label} flat at ${c.score}]: apply exactly one focused edit to its weakest quoted passage; leave the rest byte-identical.`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function buildTargetedRevisionPrompt(args: {
|
|
332
|
+
originalText: string;
|
|
333
|
+
currentText: string;
|
|
334
|
+
currentReport: ManuscriptGradeReportV2;
|
|
335
|
+
priorReports: ManuscriptGradeReportV2[];
|
|
336
|
+
targetScore: number;
|
|
337
|
+
iteration: number;
|
|
338
|
+
maxIterations: number;
|
|
339
|
+
escalation: EscalationLevel;
|
|
340
|
+
topDeficits: CategoryDeficit[];
|
|
341
|
+
budget?: RevisionBudgetOptions;
|
|
342
|
+
stallCounts?: Record<string, number>;
|
|
343
|
+
dynamicDirectives?: string[];
|
|
344
|
+
}): string {
|
|
345
|
+
const { originalText, currentText, currentReport, priorReports, targetScore, iteration, maxIterations, escalation, topDeficits } = args;
|
|
346
|
+
const budget = normalizeBudget(args.budget);
|
|
347
|
+
const prevReport = priorReports.length ? priorReports[priorReports.length - 1] : null;
|
|
348
|
+
const directives = args.dynamicDirectives ?? buildDynamicDirectives(prevReport, currentReport, args.stallCounts ?? {});
|
|
349
|
+
|
|
350
|
+
const deficitList = topDeficits
|
|
351
|
+
.slice(0, 3)
|
|
352
|
+
.map((d) => `- ${d.label} (${d.weightPct}% weight): current score ${d.currentScore}/100 -> weighted deficit ${d.weightedDeficit.toFixed(2)} pts`)
|
|
353
|
+
.join("\n");
|
|
354
|
+
const unitTable = decayEditBudget(20, budget.allowChapterReplacement)
|
|
355
|
+
.map((u) => `- ${u.unit}: up to ${u.maxEdits} edits (${u.sharePct}% of edit budget)`)
|
|
356
|
+
.join("\n");
|
|
357
|
+
|
|
358
|
+
const escalationInstructions: Record<EscalationLevel, string> = {
|
|
359
|
+
"L0-micro": "LEVEL: L0 (Micro Line-Level Polish). Target stylistic flow, word choice, and dialogue cadence. Fix typos and eliminate repetitive words without altering scenes.",
|
|
360
|
+
"L1-scene-insert": "LEVEL: L1 (Scene-Level Dynamic Insertion). Add 1-2 new high-tension beats or dialogue exchanges into the weakest chapters. Deepen character agency, force an unscripted choice, and reveal subtext.",
|
|
361
|
+
"L2-macro-reversal": "LEVEL: L2 (Macro-Structural Resolution Insertion). Replace any administrative/anti-climactic resolution with a DRAMATIZED face-to-face confrontation with the antagonist. Introduce an irreversible sacrifice (asset lost, alliance broken, wound paid for) so the climax is real and earned.",
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const rewrites = currentReport.rewriteSuggestions.slice(0, Math.min(12, 4 + iteration * 2));
|
|
365
|
+
const history = priorReports
|
|
366
|
+
.map((r, i) => ` Draft #${i}: Composite ${r.weightedComposite.toFixed(2)} (${r.compositeLetterBand}) [Plot: ${r.categories.find(c => c.key === "plot")?.score ?? "—"}, Char: ${r.categories.find(c => c.key === "character")?.score ?? "—"}, Prose: ${r.categories.find(c => c.key === "prose")?.score ?? "—"}]`)
|
|
367
|
+
.join("\n");
|
|
368
|
+
const anchors = extractChapterHeadings(originalText).slice(0, 24).join(" | ") || "(no chapter headings detected)";
|
|
369
|
+
|
|
370
|
+
return [
|
|
371
|
+
"MINIMAL SURGICAL REVISION PASS — PARETO-SUPERIOR TARGETED REVISION",
|
|
372
|
+
`Target composite: ${targetScore}/100. Current composite: ${currentReport.weightedComposite.toFixed(2)}/100 (${currentReport.compositeLetterBand}). Iteration ${iteration} of ${maxIterations}.`,
|
|
373
|
+
"",
|
|
374
|
+
"=== TARGETED DEFICIT DIRECTIVE ===",
|
|
375
|
+
`You are tasked with closing the score gap by focusing primarily on these highest-deficit categories:`,
|
|
376
|
+
deficitList,
|
|
377
|
+
"",
|
|
378
|
+
`=== ESCALATION OPERATING MODE ===`,
|
|
379
|
+
escalationInstructions[escalation],
|
|
380
|
+
"",
|
|
381
|
+
"=== MANDATORY 15 NEGATIVE PATTERNS (AVOID ALL 15) ===",
|
|
382
|
+
FIFTEEN_NEGATIVE_PATTERNS.join("\n"),
|
|
383
|
+
"",
|
|
384
|
+
"=== 6-MECHANISM PORTFOLIO (EXECUTE APPLICABLE SUBSET) ===",
|
|
385
|
+
SIX_MECHANISM_PORTFOLIO.join("\n"),
|
|
386
|
+
"",
|
|
387
|
+
"=== REVISION RULES ===",
|
|
388
|
+
"1. Keep all existing chapters in order. Do not drop chapters.",
|
|
389
|
+
"2. If an edit does not improve narrative tension or structural logic, do not make it.",
|
|
390
|
+
"3. Never output commentary, JSON, or code fences. Output ONLY the complete revised story text.",
|
|
391
|
+
"",
|
|
392
|
+
`=== DYNAMIC DIRECTIVES FOR THIS DRAFT (iteration ${iteration} — these are NEW, follow them over any prior-round habit) ===`,
|
|
393
|
+
...directives,
|
|
394
|
+
"",
|
|
395
|
+
`=== REWRITE BUDGET (hard limits for this pass) ===`,
|
|
396
|
+
`- Total changed text must not exceed ${budget.maxRewritePct}% of the manuscript.`,
|
|
397
|
+
`- No single contiguous replacement may exceed ${budget.maxReplacementParagraphs} paragraph(s)${budget.allowChapterReplacement ? "; ONE full-chapter replacement is permitted as the absolute max" : " (full-chapter replacement FORBIDDEN this pass)"}.`,
|
|
398
|
+
"- Prefer the SMALLEST unit that fixes the defect. Edit budget follows exponential decay (most edits at word level, fewest at page/chapter):",
|
|
399
|
+
unitTable,
|
|
400
|
+
"- Unit honesty rule: an isolated 1-word substitution = word edit; a changed run of 2-3 words = phrase edit; anything longer counts at sentence level or above.",
|
|
401
|
+
"",
|
|
402
|
+
"VERIFIED LINE-LEVEL REWRITE PAIRS:",
|
|
403
|
+
rewrites.length
|
|
404
|
+
? rewrites.map((r) => `- [${r.category}] "${r.originalQuote.slice(0, 140)}" => "${r.rewrittenQuote.slice(0, 140)}"${r.verified ? "" : " (verify before using)"}`).join("\n")
|
|
405
|
+
: "- (none available)",
|
|
406
|
+
"",
|
|
407
|
+
"GRADING HISTORY & TRAJECTORY:",
|
|
408
|
+
history || " (initial grade only)",
|
|
409
|
+
"",
|
|
410
|
+
"ORIGINAL-STORY ANCHORS:",
|
|
411
|
+
` Chapters: ${anchors}`,
|
|
412
|
+
"",
|
|
413
|
+
"CURRENT MANUSCRIPT TO REVISE:",
|
|
414
|
+
"<<<BEGIN_MANUSCRIPT>>>",
|
|
415
|
+
currentText,
|
|
416
|
+
"<<<END_MANUSCRIPT>>>",
|
|
417
|
+
].join("\n");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function detectAnomaly(source: string, revised: string, beforeScore: number, afterScore: number, ratio: number, maxRewritePct = 50): string | undefined {
|
|
421
|
+
const flags: string[] = [];
|
|
422
|
+
if (revised.length < source.length * 0.45) flags.push("severe truncation (output <45% of input length)");
|
|
423
|
+
if (ratio > maxRewritePct) flags.push(`rewrite budget exceeded (${ratio}% > ${maxRewritePct}% cap)`);
|
|
424
|
+
if (afterScore < beforeScore - 1.5) flags.push(`score regression ${beforeScore.toFixed(1)} -> ${afterScore.toFixed(1)}`);
|
|
425
|
+
const srcCh = (source.match(/chapter|##\s+/gi) || []).length;
|
|
426
|
+
const revCh = (revised.match(/chapter|##\s+/gi) || []).length;
|
|
427
|
+
if (srcCh >= 2 && revCh < Math.floor(srcCh * 0.6)) flags.push("chapter omission (chapter markers lost)");
|
|
428
|
+
if (!revised.trim()) flags.push("empty revision output");
|
|
429
|
+
return flags.length ? flags.join("; ") : undefined;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export async function runRecursiveRevisionLoop(args: {
|
|
433
|
+
manuscriptText: string;
|
|
434
|
+
targetScore: number;
|
|
435
|
+
maxIterations: number;
|
|
436
|
+
pickModel: (exclude: string[]) => RevisionModel | null;
|
|
437
|
+
generate: (userMessage: string, choice: RevisionModel) => Promise<string>;
|
|
438
|
+
onProgress?: (e: RevisionProgressEvent) => void;
|
|
439
|
+
promotionMargin?: number;
|
|
440
|
+
budget?: Partial<RevisionBudgetOptions>;
|
|
441
|
+
}): Promise<RevisionLoopResult> {
|
|
442
|
+
const { manuscriptText, targetScore, maxIterations, pickModel, generate, onProgress, promotionMargin = 0.5 } = args;
|
|
443
|
+
const budget = normalizeBudget(args.budget);
|
|
444
|
+
const stallCounts: Record<string, number> = {};
|
|
445
|
+
const emit = (e: RevisionProgressEvent) => {
|
|
446
|
+
try {
|
|
447
|
+
onProgress?.(e);
|
|
448
|
+
} catch {
|
|
449
|
+
/* fail-open */
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const iterations: RevisionIteration[] = [];
|
|
454
|
+
const priorReports: ManuscriptGradeReportV2[] = [];
|
|
455
|
+
let consecutivePlateaus = 0;
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
emit({ phase: "grading-initial" });
|
|
459
|
+
const g0choice = pickModel([]);
|
|
460
|
+
if (!g0choice) throw new Error("No model with a configured API key is available.");
|
|
461
|
+
const initialReport = await gradeManuscriptV2(manuscriptText, { provider: g0choice.provider, modelId: g0choice.modelId }, (msg) =>
|
|
462
|
+
generate(msg, g0choice),
|
|
463
|
+
);
|
|
464
|
+
emit({ phase: "grading-initial-done", report: initialReport });
|
|
465
|
+
priorReports.push(initialReport);
|
|
466
|
+
|
|
467
|
+
let bestText = manuscriptText;
|
|
468
|
+
let bestReport: ManuscriptGradeReportV2 = initialReport;
|
|
469
|
+
let lastModelId = g0choice.modelId;
|
|
470
|
+
|
|
471
|
+
if (initialReport.weightedComposite >= targetScore) {
|
|
472
|
+
const result: RevisionLoopResult = { status: "target-met", initialReport, finalReport: initialReport, iterations, finalText: bestText };
|
|
473
|
+
emit({ phase: "done", result });
|
|
474
|
+
return result;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const maxLoops = Math.max(1, Math.min(8, maxIterations));
|
|
478
|
+
for (let i = 1; i <= maxLoops; i++) {
|
|
479
|
+
const deficits = computeCategoryDeficits(bestReport, targetScore);
|
|
480
|
+
const topDeficit = deficits[0] ?? { key: "plot", label: "Plot", weightPct: 20, currentScore: 50, weightedDeficit: 8 };
|
|
481
|
+
const escalation = determineEscalation(i, consecutivePlateaus, topDeficit);
|
|
482
|
+
|
|
483
|
+
const reviseChoice = pickModel([lastModelId]);
|
|
484
|
+
if (!reviseChoice) throw new Error("No model available for revision step.");
|
|
485
|
+
emit({ phase: "revising", iteration: i, model: reviseChoice, escalation, targetCategory: topDeficit.label });
|
|
486
|
+
|
|
487
|
+
const prompt = buildTargetedRevisionPrompt({
|
|
488
|
+
originalText: manuscriptText,
|
|
489
|
+
currentText: bestText,
|
|
490
|
+
currentReport: bestReport,
|
|
491
|
+
priorReports: priorReports.slice(),
|
|
492
|
+
targetScore,
|
|
493
|
+
iteration: i,
|
|
494
|
+
maxIterations: maxLoops,
|
|
495
|
+
escalation,
|
|
496
|
+
topDeficits: deficits,
|
|
497
|
+
budget,
|
|
498
|
+
stallCounts,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const revised = (await generate(prompt, reviseChoice)).trim();
|
|
502
|
+
lastModelId = reviseChoice.modelId;
|
|
503
|
+
const ratio = changedRatioPct(bestText, revised);
|
|
504
|
+
const unitCounts = classifyRevisionUnits(bestText, revised);
|
|
505
|
+
const beforeScore = bestReport.weightedComposite;
|
|
506
|
+
|
|
507
|
+
// Grade with fresh random model
|
|
508
|
+
const gradeChoice = pickModel([reviseChoice.modelId]);
|
|
509
|
+
if (!gradeChoice) throw new Error("No model available for grading step.");
|
|
510
|
+
emit({ phase: "grading", iteration: i, model: gradeChoice });
|
|
511
|
+
const afterReport = await gradeManuscriptV2(revised, { provider: gradeChoice.provider, modelId: gradeChoice.modelId }, (msg) =>
|
|
512
|
+
generate(msg, gradeChoice),
|
|
513
|
+
);
|
|
514
|
+
lastModelId = gradeChoice.modelId;
|
|
515
|
+
|
|
516
|
+
// Update per-category stall counters from THIS draft's movement (drives next round's dynamic directives).
|
|
517
|
+
for (const c of afterReport.categories) {
|
|
518
|
+
const prev = bestReport.categories.find((p) => p.key === c.key)?.score ?? c.score;
|
|
519
|
+
const moved = Math.abs(c.score - prev) >= 0.6;
|
|
520
|
+
stallCounts[c.key] = moved ? 0 : (stallCounts[c.key] ?? 0) + 1;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const anomaly = detectAnomaly(bestText, revised, beforeScore, afterReport.weightedComposite, ratio, budget.maxRewritePct);
|
|
524
|
+
|
|
525
|
+
// Noise calibration & per-criterion regression guard + rewrite-budget enforcement.
|
|
526
|
+
const scoreDelta = afterReport.weightedComposite - beforeScore;
|
|
527
|
+
const plotBefore = bestReport.categories.find((c) => c.key === "plot")?.score ?? 50;
|
|
528
|
+
const plotAfter = afterReport.categories.find((c) => c.key === "plot")?.score ?? 50;
|
|
529
|
+
const charBefore = bestReport.categories.find((c) => c.key === "character")?.score ?? 50;
|
|
530
|
+
const charAfter = afterReport.categories.find((c) => c.key === "character")?.score ?? 50;
|
|
531
|
+
|
|
532
|
+
let accepted = false;
|
|
533
|
+
let rejectionReason: string | undefined;
|
|
534
|
+
|
|
535
|
+
// Hard reject: empty / catastrophic truncation / chapter loss / over-budget rewrite.
|
|
536
|
+
// Budget reject is honest power: a 90% rewrite claiming to be "minimal" is not promoted.
|
|
537
|
+
const hardReject =
|
|
538
|
+
!revised.trim() ||
|
|
539
|
+
revised.length < bestText.length * 0.45 ||
|
|
540
|
+
ratio > budget.maxRewritePct + 0.5 ||
|
|
541
|
+
(anomaly || "").includes("chapter omission");
|
|
542
|
+
|
|
543
|
+
if (hardReject) {
|
|
544
|
+
accepted = false;
|
|
545
|
+
rejectionReason = anomaly
|
|
546
|
+
? `Hard reject: ${anomaly}. Prior best retained.`
|
|
547
|
+
: `Hard reject: empty/truncated/over-budget revision. Prior best retained.`;
|
|
548
|
+
consecutivePlateaus++;
|
|
549
|
+
} else if (scoreDelta >= promotionMargin) {
|
|
550
|
+
if (plotAfter < plotBefore - 4 || charAfter < charBefore - 4) {
|
|
551
|
+
accepted = false;
|
|
552
|
+
rejectionReason = `Score increased (+${scoreDelta.toFixed(2)}) but core plot/character regressed (plot: ${plotBefore}->${plotAfter}, char: ${charBefore}->${charAfter}). Retaining prior best.`;
|
|
553
|
+
consecutivePlateaus++;
|
|
554
|
+
} else {
|
|
555
|
+
accepted = true;
|
|
556
|
+
bestText = revised;
|
|
557
|
+
bestReport = afterReport;
|
|
558
|
+
consecutivePlateaus = 0;
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
561
|
+
accepted = false;
|
|
562
|
+
rejectionReason = scoreDelta < 0
|
|
563
|
+
? `Score declined (${beforeScore.toFixed(2)} -> ${afterReport.weightedComposite.toFixed(2)}). Best-kept rule preserved prior best draft.`
|
|
564
|
+
: `Improvement (+${scoreDelta.toFixed(2)}) below calibrated noise margin (+${promotionMargin}). Prior best retained.`;
|
|
565
|
+
consecutivePlateaus++;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
emit({ phase: "grading-done", iteration: i, report: afterReport, accepted, rejectionReason });
|
|
569
|
+
|
|
570
|
+
const currentIterationRecord: RevisionIteration = {
|
|
571
|
+
iteration: i,
|
|
572
|
+
beforeScore,
|
|
573
|
+
afterScore: afterReport.weightedComposite,
|
|
574
|
+
reviseModel: reviseChoice,
|
|
575
|
+
gradeModel: gradeChoice,
|
|
576
|
+
changedRatioPct: ratio,
|
|
577
|
+
escalation,
|
|
578
|
+
accepted,
|
|
579
|
+
rejectionReason,
|
|
580
|
+
anomaly,
|
|
581
|
+
unitCounts,
|
|
582
|
+
budgetUsedPct: ratio,
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
const stag = diagnoseStagnation([...iterations, currentIterationRecord]);
|
|
586
|
+
currentIterationRecord.stagnationDiagnosis = stag.diagnosis;
|
|
587
|
+
|
|
588
|
+
emit({ phase: "revising-done", iteration: i, changedRatioPct: ratio, anomaly, stagnationDiagnosis: stag.diagnosis });
|
|
589
|
+
iterations.push(currentIterationRecord);
|
|
590
|
+
priorReports.push(afterReport);
|
|
591
|
+
|
|
592
|
+
if (bestReport.weightedComposite >= targetScore) {
|
|
593
|
+
const result: RevisionLoopResult = { status: "target-met", initialReport, finalReport: bestReport, iterations, finalText: bestText };
|
|
594
|
+
emit({ phase: "done", result });
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const result: RevisionLoopResult = {
|
|
600
|
+
status: "budget-exhausted",
|
|
601
|
+
initialReport,
|
|
602
|
+
finalReport: bestReport,
|
|
603
|
+
iterations,
|
|
604
|
+
finalText: bestText,
|
|
605
|
+
plateauDetected: consecutivePlateaus >= 2,
|
|
606
|
+
};
|
|
607
|
+
emit({ phase: "done", result });
|
|
608
|
+
return result;
|
|
609
|
+
} catch (err) {
|
|
610
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
611
|
+
emit({ phase: "error", message });
|
|
612
|
+
const result: RevisionLoopResult = {
|
|
613
|
+
status: "error",
|
|
614
|
+
initialReport: priorReports[0] ?? null,
|
|
615
|
+
finalReport: priorReports[priorReports.length - 1] ?? null,
|
|
616
|
+
iterations,
|
|
617
|
+
finalText: manuscriptText,
|
|
618
|
+
error: message,
|
|
619
|
+
};
|
|
620
|
+
emit({ phase: "done", result });
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
}
|