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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in detector pack — Universal, domain-agnostic failure modes.
|
|
3
|
+
* V15.1 — deduplicated (removed duplicate epistemic.refusal-mimicry).
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
type FlawDetector, type FlawIssue,
|
|
7
|
+
isNumericPrompt, hasUnits, numberAppearsIn, isFactualPrompt,
|
|
8
|
+
} from "../flaw-registry";
|
|
9
|
+
|
|
10
|
+
const mk = (severity: FlawIssue["severity"], code: string, message: string, autoFixable = false, remediation?: string): FlawIssue =>
|
|
11
|
+
({ severity, code, message, autoFixable, remediation });
|
|
12
|
+
|
|
13
|
+
function numericLeaves(v: any, p = "", d = 0, out: { key: string; val: number }[] = []): { key: string; val: number }[] {
|
|
14
|
+
if (d > 6 || v == null) return out;
|
|
15
|
+
if (typeof v === "number") { if (Number.isFinite(v)) out.push({ key: p || "value", val: v }); return out; }
|
|
16
|
+
if (Array.isArray(v)) { v.forEach((x, i) => numericLeaves(x, `${p}[${i}]`, d + 1, out)); return out; }
|
|
17
|
+
if (typeof v === "object") for (const [k, x] of Object.entries(v)) numericLeaves(x, p ? `${p}.${k}` : k, d + 1, out);
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const BUILTIN_DETECTORS: FlawDetector[] = [
|
|
22
|
+
// ── Numeric & Compute ─────────────────────────────────────────────────────
|
|
23
|
+
{ id: "numeric.missing-compute", domain: "numeric", appliesTo: (c) => isNumericPrompt(c.prompt), scan: (c) => c.computeRecords.length === 0 ? [mk("warning", "MISSING_COMPUTE_RECORDS", "Numeric prompt detected but no deterministic compute records were generated.", false, "Route the numeric question through the compute sandbox or explicitly disclose the calculation was not performed.")] : [] },
|
|
24
|
+
{ id: "numeric.missing-units", domain: "numeric", appliesTo: (c) => isNumericPrompt(c.prompt), scan: (c) => !hasUnits(c.answer) ? [mk("major", "MISSING_UNITS", "Numeric prompt answered without explicit units.", false, "Append the correct unit (e.g. $, %, kg, days) to every numeric value.")] : [] },
|
|
25
|
+
{ id: "numeric.compute-consistency", domain: "numeric", scan: (c) => { const out: FlawIssue[] = []; for (const rec of c.computeRecords) { if (!rec.ok) out.push(mk("critical", "COMPUTE_FAILED", `Deterministic calculation failed for ${rec.label}.`, false, `State that the ${rec.label} calculation failed rather than substituting a guessed value.`)); else if (rec.result) for (const { key, val } of numericLeaves(rec.result)) if (!numberAppearsIn(val, c.answer)) out.push(mk("major", "COMPUTE_VALUE_MISSING", `Computed value ${val} for ${key} (${rec.label}) is missing.`, false, `Insert the exact computed value ${val} for ${key} into the relevant sentence.`)); } return out; } },
|
|
26
|
+
{ id: "numeric.serialization-leak", domain: "numeric", scan: (c) => /\bNaN\b|\bInfinity\b|\[object Object\]/i.test(c.answer) ? [mk("major", "SERIALIZATION_LEAK", "Answer contains NaN/Infinity/[object Object] — stringified computation error leaked.", false, "Replace the leaked artifact with a valid computed value, or state the calculation failed.")] : [] },
|
|
27
|
+
|
|
28
|
+
// ── Constraint compliance ─────────────────────────────────────────────────
|
|
29
|
+
{ id: "constraint.time-horizon", domain: "temporal", appliesTo: (c) => !!c.constraints.timeHorizon, scan: (c) => { const ok = /\b(window|horizon|days?|weeks?|months?|years?|quarter|q[1-4]\b|by \w+ \d{4}|\d{4}-\d{2}-\d{2}|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\b/i.test(c.answer); return ok ? [] : [mk("major", "CONSTRAINT_TIME_IGNORED", `User specified a time horizon of ${c.constraints.timeHorizon?.days} days, but the answer does not acknowledge this temporal boundary.`, false, "Explicitly state the time window and evaluate recommendations within it.")]; } },
|
|
30
|
+
{ id: "constraint.entity-missing", domain: "structural", scan: (c) => c.constraints.explicitComparisonTargets.filter((e) => e && !c.answer.includes(e)).map((e) => mk("critical", "CONSTRAINT_ENTITY_MISSING", `User explicitly requested comparison target '${e}', but it is missing from the answer.`, false, `Add a dedicated section addressing '${e}'.`)) },
|
|
31
|
+
{ id: "constraint.exclusion-violated", domain: "structural", scan: (c) => c.constraints.exclusions.filter((ex) => ex && new RegExp(`\\b${ex.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}\\b`, "i").test(c.answer)).map((ex) => mk("critical", "CONSTRAINT_EXCLUSION_VIOLATED", `User explicitly excluded '${ex}', but it appears in the answer.`, false, `Remove every reference to '${ex}'.`)) },
|
|
32
|
+
|
|
33
|
+
// ── Epistemic ─────────────────────────────────────────────────────────────
|
|
34
|
+
{ id: "epistemic.overconfident", domain: "epistemic", scan: (c) => { const a = /\b(exactly|precisely|guaranteed|100% accurate|always|never|definitively|categorically|without question|undoubtedly|indisputably|unquestionably|proven fact)\b/i.test(c.answer); const h = /\b(assum|default|input|estimate|range|uncertain|requires|approximately|likely|roughly|confidence|caveat|depends)\b/i.test(c.answer); return a && !h ? [mk("warning", "OVERCONFIDENT_CERTAINTY", "Absolute certainty expressed without declared assumptions or confidence intervals.", false, "Replace absolute language with hedged phrasing and state key assumptions.")] : []; } },
|
|
35
|
+
{ id: "epistemic.refusal-mimicry", domain: "epistemic", appliesTo: (c) => (c.sources?.length ?? 0) >= 8, scan: (c) => /\b(provided data|retrieved sources|source context|available evidence)\b[\s\S]{0,160}\b(does not contain|do not contain|lacks?|insufficient|no direct mention|no information regarding|cannot propose|unable to propose)\b/i.test(c.answer) ? [mk("major", "REFUSAL_MIMICRY", "Source-rich retrieval but answer claims insufficient evidence.", false, "Use the retrieved sources to produce a concrete evidence-bounded synthesis; only name narrow fields that are genuinely missing.")] : [] },
|
|
36
|
+
{ id: "epistemic.base-rate-neglect", domain: "epistemic", appliesTo: (c) => isFactualPrompt(c.prompt), scan: (c) => /\b(100%|always|never|guaranteed|everyone|no one)\b[\s\S]{0,90}\b(success|failure|works|fails|wins|loses|survives|dies)/i.test(c.answer) && !/\b(base.?rate|prior|prevalence|background|reference class)\b/i.test(c.answer) ? [mk("major", "BASE_RATE_NEGLECT", "Base-rate neglect: absolute claim without reference to prevalence or prior probability.", false, "Explicitly state base rate or reference class used.")] : [] },
|
|
37
|
+
{ id: "epistemic.sycophancy-output", domain: "epistemic", appliesTo: (c) => isFactualPrompt(c.prompt), scan: (c) => /^(great|excellent|fantastic|outstanding|brilliant) (question|prompt|ask|inquiry)/i.test(c.answer.trim()) ? [mk("warning", "SYCOPHANCY_OUTPUT", "Sycophantic opener leaked into final answer.", true, "Remove the opener; begin with substantive verdict.")] : [] },
|
|
38
|
+
{ id: "scientific.p-hacking-lang", domain: "domain", appliesTo: (c) => /scientific|nih|study|p.?value|hypothesis|statistical/i.test(c.prompt), scan: (c) => /\b(p.?value|statistical significance|significant(ly)?)\b[\s\S]{0,120}\b(exploratory|post.?hoc|we tried|multiple comparisons|cherry.?picked|selected|best.?performing)/i.test(c.answer) ? [mk("major", "P_HACKING_LANG", "Language suggestive of p-hacking or post-hoc specification without correction.", false, "Declare comparisons planned a-priori or apply multiplicity correction.")] : [] },
|
|
39
|
+
|
|
40
|
+
// ── Citation ──────────────────────────────────────────────────────────────
|
|
41
|
+
{ id: "citation.index-out-of-range", domain: "citation", appliesTo: (c) => Array.isArray(c.sources) && c.sources.length > 0, scan: (c) => { const n = c.sources!.length; const out: FlawIssue[] = []; const seen = new Set<number>(); for (const m of c.answer.matchAll(/\[(?:S|Source\s*)(\d+)\]/gi)) { const idx = parseInt(m[1], 10); if (idx > n && !seen.has(idx)) { seen.add(idx); out.push(mk("critical", "CITATION_INDEX_OOR", `Answer cites [S${idx}] but only ${n} source(s) exist.`, false, `Remove or replace [S${idx}] with a citation to a real source (S1–S${n}).`)); } } return out; } },
|
|
42
|
+
{ id: "citation.cites-with-no-sources", domain: "citation", appliesTo: (c) => !Array.isArray(c.sources) || c.sources.length === 0, scan: (c) => { const refs = [...c.answer.matchAll(/\[(?:S|Source\s*)(\d+)\]/gi)].map(m => m[0]); return refs.length > 0 ? [mk("critical", "CITES_WITH_NO_SOURCES", `Answer cites ${refs.slice(0, 3).join(", ")} but no source list was provided.`, false, "Remove source markers or rerun with retrieved sources available.")] : []; } },
|
|
43
|
+
|
|
44
|
+
// ── Format leaks (most auto-fixable) ──────────────────────────────────────
|
|
45
|
+
{ id: "format.critique-json-leak", domain: "format", scan: (c) => /\{\s*"(?:clear|errors|corrections|combinedScore|topicMatch|numbersMatchOverlay)"\s*:/i.test(c.answer) ? [mk("major", "CRITIQUE_JSON_LEAK", "Internal critique/judge JSON leaked into reader-facing output.", true, "Remove the JSON block; it is internal scaffolding.")] : [] },
|
|
46
|
+
{ id: "format.system-marker-leak", domain: "format", scan: (c) => /(^|\n)\s*(REFERENCE OVERLAY|ISSUES DETECTED|CRITIQUE:|DRAFT:|MANDATORY VERIFICATION PROTOCOL|Deterministic Audit Overlay)\b/i.test(c.answer) ? [mk("major", "SYSTEM_MARKER_LEAK", "System/overlay scaffolding markers leaked into output.", true, "Delete every scaffolding marker line.")] : [] },
|
|
47
|
+
{ id: "format.scratchpad-leak", domain: "format", scan: (c) => /(^|\n)\s*(Self-correction:|Let me think|As an AI[,.]|Scratchpad:|Thinking:|Step \d+ \()/i.test(c.answer) ? [mk("warning", "SCRATCHPAD_LEAK", "Chain-of-thought / scratchpad text leaked into output.", true, "Remove scratchpad lines; emit only the final polished answer.")] : [] },
|
|
48
|
+
{ id: "format.xml-tag-leak", domain: "format", scan: (c) => /<\/?(final_answer|paragraph|section|div|p|answer)>/i.test(c.answer) ? [mk("warning", "XML_TAG_LEAK", "Raw XML/HTML scaffolding tags present; use clean Markdown.", true, "Strip scaffolding tags; preserve content as clean Markdown.")] : [] },
|
|
49
|
+
{ id: "format.placeholder-leak", domain: "format", scan: (c) => /\[(?:list of|description of|insert|tbd|placeholder|fill in|todo)\b[^\]]*\]/i.test(c.answer) ? [mk("major", "PLACEHOLDER_LEAK", "Unfilled bracketed placeholder remains in the answer.", false, "Replace every bracketed placeholder with specific content or remove the slot.")] : [] },
|
|
50
|
+
{ id: "format.persona-leak", domain: "format", scan: (c) => /\b(as an? AI(?:\s+language\s+model|\s+assistant)?[,.]|I am an? AI(?:\s+language\s+model)?\b|I(?:'m| am)\s+(?:just\s+)?an?\s+AI\b|my training data|my knowledge cutoff)\b/i.test(c.answer) ? [mk("warning", "PERSONA_LEAKAGE", "AI/system persona meta-discourse leaked into the answer.", true, "Remove every AI self-referential statement.")] : [] },
|
|
51
|
+
{ id: "format.verification-plan-leak", domain: "format", scan: (c) => /\bVERIFICATION PLAN MODE\b|\bHypotheses:\s*\n|\bSearch Queries:\s*\n|I am now proceeding to verify/i.test(c.answer) ? [mk("critical", "VERIFICATION_PLAN_LEAKAGE", "Internal verification-plan / workflow text leaked into the final answer.", false, "Delete verification-plan scaffolding; ship only the finished answer.")] : [] },
|
|
52
|
+
{ id: "format.lazy-truncation", domain: "format", scan: (c) => /\b(rest of the code|omitted for brevity|snipped|left as an exercise|fill in the rest|continued in next|to be continued)\b/i.test(c.answer) ? [mk("critical", "LAZY_TRUNCATION", "Output indicates lazy truncation or omitted required content.", false, "Expand every omitted section with complete content.")] : [] },
|
|
53
|
+
{ id: "format.truncation", domain: "format", appliesTo: (c) => c.answer.trim().length > 200, scan: (c) => { const trimmed = c.answer.trim(); const lastLine = trimmed.slice(trimmed.lastIndexOf("\n") + 1).trim(); if (/^([*\-+]\s|\d+[.)]\s|#{1,6}\s|>|\||```|\$\$)/.test(lastLine)) return []; if (/\|\s*$/.test(lastLine) || /```/.test(lastLine)) return []; const dangling = /[,;:]\s*$|\b(and|or|but|the|a|an|to|of|with|for|in|on|as|by|that|which|because|however|therefore|thus|since|while|where|when)\s*$/i.test(lastLine); const noTerminal = !/[.!?)"'»"\]\}]\s*$/.test(lastLine); return (dangling || (noTerminal && /\w$/.test(lastLine))) ? [mk("major", "TRUNCATED_OUTPUT", "Answer appears truncated mid-sentence.", false, "Complete the unfinished sentence with terminal punctuation.")] : []; } },
|
|
54
|
+
|
|
55
|
+
// ── Markup integrity ──────────────────────────────────────────────────────
|
|
56
|
+
{ id: "markup.code-fence-unclosed", domain: "format", appliesTo: c => c.answer.includes("```"), scan: c => (c.answer.match(/```/g) ?? []).length % 2 !== 0 ? [mk("major", "CODE_FENCE_UNCLOSED", "Odd number of code fences; block is unclosed.", false, "Add the closing ```.")] : [] },
|
|
57
|
+
{ id: "markup.math-fence-unclosed", domain: "format", appliesTo: c => c.answer.includes("$$"), scan: c => (c.answer.match(/\$\$/g) ?? []).length % 2 !== 0 ? [mk("major", "MATH_FENCE_UNCLOSED", "Odd number of $$ math delimiters; block is unclosed.", false, "Add the missing closing $$.")] : [] },
|
|
58
|
+
{ id: "markup.empty-link", domain: "format", scan: c => /\[[^\]\n]+\]\(\s*\)/i.test(c.answer) ? [mk("warning", "MARKDOWN_LINK_EMPTY", "Markdown link with empty href detected.", false, "Provide a valid URL or remove the link syntax.")] : [] },
|
|
59
|
+
|
|
60
|
+
// ── Structural ────────────────────────────────────────────────────────────
|
|
61
|
+
{ id: "structural.table-column-mismatch", domain: "structural", appliesTo: c => /\|[-:\s|]+\|/.test(c.answer) && (c.answer.match(/^\s*\|.+\|\s*$/gm) ?? []).length >= 3, scan: c => { const rows = c.answer.split("\n").filter(l => /^\s*\|.+\|\s*$/.test(l)); if (rows.length < 3) return []; const exp = (rows[0].match(/\|/g) ?? []).length; let errs = 0; for (const r of rows) if ((r.match(/\|/g) ?? []).length !== exp) errs++; return errs > 0 ? [mk("warning", "TABLE_COL_MISMATCH", `Markdown table has ${errs} row(s) with inconsistent column count.`, false, "Ensure every table row has the same number of | delimiters as the header.")] : []; } },
|
|
62
|
+
{ id: "logical.negation-contradiction-lite", domain: "logical", appliesTo: c => c.answer.length > 150, scan: c => { const positives = [...c.answer.matchAll(/\b([A-Z][a-z]{3,30}(?:\s+[a-z]+){0,2})\s+(?:is|are)\s+([a-z]{4,30})\b/g)]; const seen = new Map<string, string>(); for (const m of positives) seen.set(`${m[1].toLowerCase()}|${m[2].toLowerCase()}`, m[0]); for (const m of c.answer.matchAll(/\b([A-Z][a-z]{3,30}(?:\s+[a-z]+){0,2})\s+(?:is|are)\s+not\s+([a-z]{4,30})\b/g)) { const key = `${m[1].toLowerCase()}|${m[2].toLowerCase()}`; if (seen.has(key)) return [mk("major", "INTERNAL_NEGATION_CONTRADICTION", `Contradicting assertions for "${m[1]}": both affirming and negating "${m[2]}".`, false, "Reconcile the contradiction; pick one position.")]; } return []; } },
|
|
63
|
+
];
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finance Domain Pack v1
|
|
3
|
+
*
|
|
4
|
+
* Targets text-detectable failure modes in financial/investment content:
|
|
5
|
+
* - Unsolicited investment advice without registration/suitability caveat
|
|
6
|
+
* - "Guaranteed returns" / "no risk"
|
|
7
|
+
* - Past-performance-as-future-results
|
|
8
|
+
* - Crypto/security misclassification under Howey
|
|
9
|
+
* - Tax advice without CPA/EA caveat
|
|
10
|
+
* - GAAP/IFRS conflation
|
|
11
|
+
* - Backtest without out-of-sample
|
|
12
|
+
* - Sharpe ratio / VaR misuse
|
|
13
|
+
* - "Risk-free" attached to non-Treasury instruments
|
|
14
|
+
* - Insider information indicators
|
|
15
|
+
* - SEC Rule 10b-5 risk language
|
|
16
|
+
*/
|
|
17
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
18
|
+
|
|
19
|
+
const mk = (
|
|
20
|
+
severity: FlawIssue["severity"],
|
|
21
|
+
code: string,
|
|
22
|
+
message: string,
|
|
23
|
+
remediation: string,
|
|
24
|
+
): FlawIssue => ({ severity, code, message, remediation });
|
|
25
|
+
|
|
26
|
+
function T(c: ScanContext): string { return `${c.prompt}\n${c.answer}`; }
|
|
27
|
+
function isFinance(c: ScanContext): boolean {
|
|
28
|
+
return /\b(stock|bond|equity|portfolio|invest|investment|trading|trader|broker|asset|securit|fund|ETF|mutual fund|hedge fund|options|futures|derivat|crypto|bitcoin|ethereum|token|ICO|IPO|SEC|FINRA|GAAP|IFRS|EBITDA|P\/E|ROI|NPV|IRR|Sharpe|alpha|beta|volatility|drawdown|VaR|capital|dividend|yield|interest rate|fed|treasury|tax|IRS|401k|IRA|Roth|capital gain|short sale|margin)\b/i.test(T(c));
|
|
29
|
+
}
|
|
30
|
+
function isAdvisoryFinance(c: ScanContext): boolean {
|
|
31
|
+
return isFinance(c) && /\b(you should|recommend|buy|sell|invest in|allocate|put your money|portfolio.*allocation|best stock|best investment)\b/i.test(c.answer);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const FINANCE_FLAWS: FlawDetector[] = [
|
|
35
|
+
// ── Investment-adviser registration / suitability ─────────────────────────
|
|
36
|
+
{
|
|
37
|
+
id: "fin.no-adviser-disclaimer",
|
|
38
|
+
domain: "domain",
|
|
39
|
+
description: "Direct investment advice without RIA / fiduciary / suitability caveat.",
|
|
40
|
+
appliesTo: isAdvisoryFinance,
|
|
41
|
+
scan: c => /\b(you should buy|you should sell|you should invest|recommend buying|recommend selling|put your money in|buy [A-Z]{1,5} now)\b/i.test(c.answer)
|
|
42
|
+
&& !/\b(not\s+(?:financial|investment)\s+advice|consult\s+(?:a\s+)?(?:licensed|registered|financial)\s+(?:adviser|advisor|RIA|CFP)|registered\s+investment\s+adviser|fiduciary|suitabili|for\s+informational\s+purposes|do\s+your\s+own\s+research)\b/i.test(c.answer)
|
|
43
|
+
? [mk("major", "FIN_NO_ADVISER_DISCLAIMER", "Direct investment advice without 'not financial advice / consult registered adviser' caveat.", "Add: 'This is for informational purposes only and not financial advice. Consult a licensed/registered investment adviser (RIA, CFP) about your specific situation before making investment decisions.' Required by SEC/FINRA rules and to limit liability.")]
|
|
44
|
+
: [],
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// ── Guaranteed-returns / risk-free fallacies ──────────────────────────────
|
|
48
|
+
{
|
|
49
|
+
id: "fin.guaranteed-returns",
|
|
50
|
+
domain: "domain",
|
|
51
|
+
description: "'Guaranteed returns' or 'no risk' language outside Treasury/FDIC context.",
|
|
52
|
+
appliesTo: isFinance,
|
|
53
|
+
scan: c => /\b(guaranteed\s+(?:returns?|profits?|gains?)|risk[-\s]free\s+(?:returns?|profits?|gains?)|zero\s+risk|no\s+risk\s+of\s+loss|can[']?t\s+lose|cannot\s+lose)\b/i.test(c.answer)
|
|
54
|
+
&& !/\b(U\.S\.\s+Treasur|T-?bill|FDIC[-\s]?insured|government[-\s]?backed)\b/i.test(c.answer)
|
|
55
|
+
? [mk("critical", "FIN_GUARANTEED_RETURNS", "'Guaranteed returns' / 'no risk' / 'risk-free' language used outside Treasury/FDIC context — Rule 10b-5 / FTC misrepresentation risk.", "No equity, crypto, hedge-fund, or private-investment product is 'risk-free' or 'guaranteed'. SEC Rule 10b-5 prohibits material misrepresentation. Reserve 'risk-free' for U.S. Treasuries and FDIC-insured deposits up to limit.")]
|
|
56
|
+
: [],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "fin.risk-free-misuse",
|
|
60
|
+
domain: "domain",
|
|
61
|
+
description: "'Risk-free rate' applied to non-Treasury / non-sovereign instrument.",
|
|
62
|
+
appliesTo: isFinance,
|
|
63
|
+
scan: c => /\brisk[-\s]?free\s+rate\b/i.test(c.answer)
|
|
64
|
+
&& /\b(corporate bond|junk bond|high[-\s]yield|stock|equity|crypto|emerging market)\b/i.test(c.answer)
|
|
65
|
+
&& !/\b(spread\s+over|excess\s+(?:return|yield)|treasury\s+yield)\b/i.test(c.answer)
|
|
66
|
+
? [mk("major", "FIN_RISK_FREE_MISUSE", "'Risk-free rate' applied to risk-bearing instrument.", "Risk-free rate refers to U.S. Treasury (or equivalent sovereign) yield only. Other instruments have credit/default risk and are priced as spread over the risk-free rate.")]
|
|
67
|
+
: [],
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// ── Past performance / regulatory boilerplate ─────────────────────────────
|
|
71
|
+
{
|
|
72
|
+
id: "fin.past-performance",
|
|
73
|
+
domain: "domain",
|
|
74
|
+
description: "Citing past returns as predictive of future without disclaimer.",
|
|
75
|
+
appliesTo: isFinance,
|
|
76
|
+
scan: c => /\b(returned|gained|posted|achieved)\s+\d+\s*%(?:\s+(?:over|in)\s+the\s+(?:past|last)\s+\d+\s+years?)?[\s\S]{0,80}\b(will|expect|likely)\s+(?:to\s+)?(?:gain|return|earn|continue)/i.test(c.answer)
|
|
77
|
+
&& !/\b(past\s+performance\s+(?:is\s+not|does\s+not\s+guarantee))/i.test(c.answer)
|
|
78
|
+
? [mk("major", "FIN_PAST_PERFORMANCE", "Past returns presented as predictive of future returns without disclaimer.", "Per SEC and FINRA: past performance is not indicative of future results. Add disclaimer or reframe historical data as illustrative only.")]
|
|
79
|
+
: [],
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// ── Tax / accounting precision ────────────────────────────────────────────
|
|
83
|
+
{
|
|
84
|
+
id: "fin.tax-no-jurisdiction",
|
|
85
|
+
domain: "domain",
|
|
86
|
+
description: "Tax statements without jurisdiction (federal vs state, U.S. vs foreign).",
|
|
87
|
+
appliesTo: c => /\b(tax|IRS|capital gain|deduction|withholding|1099|W-2|filing|return)\b/i.test(T(c)),
|
|
88
|
+
scan: c => /\b(tax\s+rate\s+is|you\s+(?:owe|pay)|deduct(?:ible)?|tax[-\s]?free|exempt|capital\s+gains\s+tax)\b/i.test(c.answer)
|
|
89
|
+
&& /\b(is|are|equals?|\d+\s*%)\b/i.test(c.answer)
|
|
90
|
+
&& !/\b(U\.S\.\s+federal|federal\s+income|state\s+tax|California|New York|Texas|varies\s+by\s+state|foreign|jurisdiction|consult\s+a\s+(?:CPA|tax\s+(?:adviser|professional))|Enrolled\s+Agent)\b/i.test(c.answer)
|
|
91
|
+
? [mk("major", "FIN_TAX_NO_JURISDICTION", "Tax rate / treatment stated without jurisdiction or 'consult a CPA' caveat.", "U.S. federal vs state vs foreign tax treatment varies materially. Specify jurisdiction and add: 'Consult a CPA or Enrolled Agent for your specific situation.'")]
|
|
92
|
+
: [],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "fin.gaap-ifrs-conflation",
|
|
96
|
+
domain: "domain",
|
|
97
|
+
description: "GAAP and IFRS conflated as equivalent.",
|
|
98
|
+
appliesTo: c => /\b(GAAP|IFRS|accounting standards?|financial statements?)\b/i.test(T(c)),
|
|
99
|
+
scan: c => /\b(GAAP\s+and\s+IFRS\s+(?:are|both)\s+(?:the\s+same|equivalent|interchangeable)|GAAP\s+is\s+(?:basically\s+)?IFRS)\b/i.test(c.answer)
|
|
100
|
+
? [mk("major", "FIN_GAAP_IFRS_CONFLATION", "GAAP and IFRS conflated as equivalent — they differ materially in inventory, R&D, lease accounting, etc.", "Differentiate: U.S. GAAP (FASB, rules-based) vs IFRS (IASB, principles-based). Key differences: LIFO permitted under GAAP but not IFRS; R&D capitalization rules differ; lease accounting (ASC 842 vs IFRS 16) differs.")]
|
|
101
|
+
: [],
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
// ── Quantitative finance hygiene ──────────────────────────────────────────
|
|
105
|
+
{
|
|
106
|
+
id: "fin.backtest-no-oos",
|
|
107
|
+
domain: "domain",
|
|
108
|
+
description: "Backtest result cited without out-of-sample / look-ahead disclosure.",
|
|
109
|
+
appliesTo: c => /\b(backtest|backtesting|strategy returned|simulated returns)\b/i.test(T(c)),
|
|
110
|
+
scan: c => /\bbacktest(?:ed|ing)?\b[\s\S]{0,80}\b(?:returned|achieved|earned|gained)\s+\d+\s*%/i.test(c.answer)
|
|
111
|
+
&& !/\b(out[-\s]?of[-\s]?sample|walk[-\s]?forward|look[-\s]?ahead|survivorship|overfit|in[-\s]?sample\s+only)\b/i.test(c.answer)
|
|
112
|
+
? [mk("major", "FIN_BACKTEST_NO_OOS", "Backtest performance cited without out-of-sample / look-ahead / survivorship-bias disclosure.", "Always disclose: in-sample vs out-of-sample period, walk-forward validation, look-ahead-bias controls, survivorship-bias correction (delisted tickers included), and transaction cost / slippage assumptions.")]
|
|
113
|
+
: [],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "fin.sharpe-no-period",
|
|
117
|
+
domain: "domain",
|
|
118
|
+
description: "Sharpe ratio cited without time period / risk-free benchmark.",
|
|
119
|
+
appliesTo: c => /\bSharpe\b/i.test(T(c)),
|
|
120
|
+
scan: c => /\bSharpe\s+(?:ratio\s+)?(?:of|=)\s*\d+(?:\.\d+)?\b/i.test(c.answer)
|
|
121
|
+
&& !/\b(annuali[sz]ed|period|risk[-\s]?free|monthly|daily|over\s+\d+\s+(?:year|month))/i.test(c.answer)
|
|
122
|
+
? [mk("warning", "FIN_SHARPE_NO_PERIOD", "Sharpe ratio cited without specifying annualization / period / risk-free benchmark.", "Sharpe = (R_p - R_f) / σ_p. Always state: time period, annualization basis (e.g. √252 for daily, √12 for monthly), and risk-free rate used.")]
|
|
123
|
+
: [],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "fin.var-no-confidence",
|
|
127
|
+
domain: "domain",
|
|
128
|
+
description: "Value-at-Risk cited without confidence level / horizon.",
|
|
129
|
+
appliesTo: c => /\bVaR\b/i.test(T(c)),
|
|
130
|
+
scan: c => /\bVaR\s+(?:of|=|is)\s*\$?\d/i.test(c.answer)
|
|
131
|
+
&& !/\b(\d{2}\s*%\s+confidence|95\s*%|99\s*%|1[-\s]?day|10[-\s]?day|horizon)\b/i.test(c.answer)
|
|
132
|
+
? [mk("warning", "FIN_VAR_NO_CONFIDENCE", "Value-at-Risk cited without confidence level or time horizon.", "VaR is always conditioned on confidence (e.g. 95% or 99%) AND horizon (e.g. 1-day, 10-day). Specify both. Also note VaR's known limitations (does not bound tail loss; consider CVaR/Expected Shortfall).")]
|
|
133
|
+
: [],
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// ── Securities law triggers ───────────────────────────────────────────────
|
|
137
|
+
{
|
|
138
|
+
id: "fin.crypto-howey",
|
|
139
|
+
domain: "domain",
|
|
140
|
+
description: "Crypto token classified as 'not a security' without Howey test discussion.",
|
|
141
|
+
appliesTo: c => /\b(crypto|token|ICO|altcoin|bitcoin|ethereum|coin)\b/i.test(T(c)),
|
|
142
|
+
scan: c => /\b(?:is\s+not\s+a\s+security|won[']?t\s+be\s+regulated\s+by\s+SEC|outside\s+SEC\s+jurisdiction)\b/i.test(c.answer)
|
|
143
|
+
&& !/\b(Howey\s+test|investment\s+contract|expectation\s+of\s+profits|common\s+enterprise)\b/i.test(c.answer)
|
|
144
|
+
? [mk("major", "FIN_CRYPTO_HOWEY", "Crypto token classified as 'not a security' without Howey test analysis.", "U.S. SEC applies the Howey test (1946): an investment of money in a common enterprise with expectation of profits from the efforts of others = security. Discuss the four prongs explicitly before claiming non-security status.")]
|
|
145
|
+
: [],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "fin.insider-trading-marker",
|
|
149
|
+
domain: "domain",
|
|
150
|
+
description: "Language consistent with material non-public information trading advice.",
|
|
151
|
+
appliesTo: isFinance,
|
|
152
|
+
scan: c => /\b(insider\s+(?:info|information|knowledge|tip)|material\s+non[-\s]?public|MNPI|before\s+the\s+announcement|before\s+earnings\s+(?:are\s+)?released|got\s+a\s+tip\s+from)\b/i.test(c.answer)
|
|
153
|
+
&& /\b(buy|sell|trade|short|long|position)\b/i.test(c.answer)
|
|
154
|
+
? [mk("critical", "FIN_INSIDER_TRADING_MARKER", "Content implies trading on material non-public information (MNPI) — securities-fraud / SEC Rule 10b-5 / insider-trading exposure.", "Refuse to advise on trading based on MNPI. Direct the user to disclosure obligations and counsel. Insider trading is a federal crime under SEC Rule 10b-5 and the Securities Exchange Act of 1934.")]
|
|
155
|
+
: [],
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// ── Crypto / DeFi safety ──────────────────────────────────────────────────
|
|
159
|
+
{
|
|
160
|
+
id: "fin.defi-no-impermanent-loss",
|
|
161
|
+
domain: "domain",
|
|
162
|
+
description: "DeFi liquidity-pool returns cited without impermanent-loss disclosure.",
|
|
163
|
+
appliesTo: c => /\b(DeFi|liquidity\s+pool|LP\s+token|AMM|Uniswap|Curve|Balancer|yield\s+farm)\b/i.test(T(c)),
|
|
164
|
+
scan: c => /\b(?:APY|APR|yield)\s+of\s+\d+\s*%/i.test(c.answer)
|
|
165
|
+
&& !/\b(impermanent\s+loss|IL|divergence\s+loss|smart\s+contract\s+risk|rug\s+pull)\b/i.test(c.answer)
|
|
166
|
+
? [mk("major", "FIN_DEFI_NO_IL", "DeFi LP / yield-farm APY cited without impermanent-loss and smart-contract-risk disclosure.", "DeFi LP returns can be wiped out by impermanent loss when token prices diverge, plus smart-contract bugs, oracle manipulation, and rug-pull risk. Disclose all three explicitly.")]
|
|
167
|
+
: [],
|
|
168
|
+
},
|
|
169
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic Auto-Fixers (CRITIQUE_FIXERS).
|
|
3
|
+
* These run on the raw model output before it reaches the user.
|
|
4
|
+
*/
|
|
5
|
+
import type { ScanContext } from "../flaw-registry";
|
|
6
|
+
|
|
7
|
+
export function CRITIQUE_FIXERS(): void {
|
|
8
|
+
// Registration is handled by the main flaw index; this file exists to satisfy the import contract.
|
|
9
|
+
// If specific fixers need to register themselves as detectors, they would do so here.
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { registerFlaws, registerFlawPack } from "../flaw-registry";
|
|
2
|
+
import { BUILTIN_DETECTORS } from "./builtins";
|
|
3
|
+
import { CRITIQUE_FIXERS } from "./fixers";
|
|
4
|
+
import { STATISTICS_FLAWS } from "./statistics";
|
|
5
|
+
import { STATISTICS_ADVANCED_FLAWS } from "./statistics-advanced";
|
|
6
|
+
import { SOFTWARE_EXTENDED_FLAWS } from "./software-extended";
|
|
7
|
+
import { SOFTWARE_RN_WEBGL_FLAWS } from "./software-rn-webgl";
|
|
8
|
+
import { MEDICAL_FLAWS } from "./medical";
|
|
9
|
+
import { LEGAL_FLAWS } from "./legal";
|
|
10
|
+
import { FINANCE_FLAWS } from "./finance";
|
|
11
|
+
|
|
12
|
+
let loaded = false;
|
|
13
|
+
export function ensureFlawsLoaded(): void {
|
|
14
|
+
if (loaded) return;
|
|
15
|
+
loaded = true;
|
|
16
|
+
|
|
17
|
+
registerFlawPack("builtin", BUILTIN_DETECTORS);
|
|
18
|
+
CRITIQUE_FIXERS();
|
|
19
|
+
registerFlawPack("statistics", STATISTICS_FLAWS);
|
|
20
|
+
registerFlawPack("statistics-advanced", STATISTICS_ADVANCED_FLAWS);
|
|
21
|
+
registerFlawPack("software-extended", SOFTWARE_EXTENDED_FLAWS);
|
|
22
|
+
registerFlawPack("software-rn-webgl", SOFTWARE_RN_WEBGL_FLAWS);
|
|
23
|
+
registerFlawPack("medical", MEDICAL_FLAWS);
|
|
24
|
+
registerFlawPack("legal", LEGAL_FLAWS);
|
|
25
|
+
registerFlawPack("finance", FINANCE_FLAWS);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
registerFlaw, registerFlaws, registerFlawPack,
|
|
30
|
+
setFlawEnabled, setPackEnabled, listFlaws, listPacks,
|
|
31
|
+
loadDeclarativePack, registryHealthCheck,
|
|
32
|
+
} from "../flaw-registry";
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legal Domain Pack v1
|
|
3
|
+
*
|
|
4
|
+
* Targets text-detectable failure modes in legal/regulatory content:
|
|
5
|
+
* - "This is legal advice" without attorney-disclaimer
|
|
6
|
+
* - Jurisdiction-agnostic statutory citation
|
|
7
|
+
* - Citation to fabricated case law (Mata v. Avianca pattern)
|
|
8
|
+
* - Statute citation without title/section/year
|
|
9
|
+
* - Contract clauses without governing-law/venue/severability
|
|
10
|
+
* - Privilege/confidentiality leakage indicators
|
|
11
|
+
* - "Always" / "Never" legal absolutes
|
|
12
|
+
* - GDPR/CCPA conflation
|
|
13
|
+
* - Misstatement of statutes of limitations
|
|
14
|
+
* - UPL (Unauthorized Practice of Law) markers
|
|
15
|
+
*/
|
|
16
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
17
|
+
|
|
18
|
+
const mk = (
|
|
19
|
+
severity: FlawIssue["severity"],
|
|
20
|
+
code: string,
|
|
21
|
+
message: string,
|
|
22
|
+
remediation: string,
|
|
23
|
+
): FlawIssue => ({ severity, code, message, remediation });
|
|
24
|
+
|
|
25
|
+
function T(c: ScanContext): string { return `${c.prompt}\n${c.answer}`; }
|
|
26
|
+
function isLegal(c: ScanContext): boolean {
|
|
27
|
+
return /\b(law|legal|statute|regulation|attorney|lawyer|counsel|court|judge|plaintiff|defendant|contract|clause|agreement|liability|damages|tort|criminal|civil|jurisdiction|appellate|supreme court|circuit|district|prosecut|sue|lawsuit|judgment|sentenc|verdict|breach|negligen|due process|GDPR|CCPA|HIPAA|SEC|FTC|DOJ|EEOC|OSHA|U\.S\.C\.|C\.F\.R\.|§)\b/i.test(T(c));
|
|
28
|
+
}
|
|
29
|
+
function isAdvisoryLegal(c: ScanContext): boolean {
|
|
30
|
+
return isLegal(c) && /\b(you should|you can|you must|your rights|you are entitled|file a|sue for|recover|claim|enforce)\b/i.test(c.answer);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const LEGAL_FLAWS: FlawDetector[] = [
|
|
34
|
+
// ── UPL / disclaimer ──────────────────────────────────────────────────────
|
|
35
|
+
{
|
|
36
|
+
id: "legal.no-attorney-disclaimer",
|
|
37
|
+
domain: "domain",
|
|
38
|
+
description: "Direct legal advisory content without 'not legal advice / consult attorney' disclaimer.",
|
|
39
|
+
appliesTo: isAdvisoryLegal,
|
|
40
|
+
scan: c => /\b(you should|you can|you must|you are entitled|file a|sue for|recover damages)\b/i.test(c.answer)
|
|
41
|
+
&& !/\b(not\s+legal\s+advice|consult\s+(?:an?\s+)?(?:attorney|lawyer)|seek\s+legal\s+counsel|licensed\s+attorney|jurisdiction[-\s]?specific|this\s+is\s+general\s+information)\b/i.test(c.answer)
|
|
42
|
+
? [mk("major", "LEGAL_NO_ATTORNEY_DISCLAIMER", "Direct legal advisory content without 'not legal advice / consult licensed attorney' disclaimer.", "Add: 'This is general information and not legal advice. Consult a licensed attorney in your jurisdiction for advice on your specific situation.' Failing to do so risks Unauthorized Practice of Law (UPL) exposure.")]
|
|
43
|
+
: [],
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// ── Jurisdiction precision ────────────────────────────────────────────────
|
|
47
|
+
{
|
|
48
|
+
id: "legal.jurisdiction-agnostic",
|
|
49
|
+
domain: "domain",
|
|
50
|
+
description: "Statute/rule cited without jurisdiction (federal vs state, which state, which country).",
|
|
51
|
+
appliesTo: isLegal,
|
|
52
|
+
scan: c => /\b(statute of limitations|filing deadline|burden of proof|sentencing|damages cap|minimum wage|right to)\b/i.test(c.answer)
|
|
53
|
+
&& /\b(is|are|requires|allows|prohibits)\b\s+\d/i.test(c.answer)
|
|
54
|
+
&& !/\b(federal|state\s+of|under\s+(?:U\.S\.\s+law|California|New York|Texas|EU\s+law|GDPR|UK\s+law|Canadian|Australian)|in\s+(?:most|all|some)\s+(?:states|jurisdictions)|varies\s+by\s+(?:state|jurisdiction))\b/i.test(c.answer)
|
|
55
|
+
? [mk("major", "LEGAL_JURISDICTION_AGNOSTIC", "Statute / legal rule cited as universal without naming a jurisdiction.", "Specify jurisdiction (federal U.S., specific state, EU GDPR, UK, etc.). Statutes of limitations, damages caps, and procedural rules vary materially across jurisdictions.")]
|
|
56
|
+
: [],
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ── Case citation integrity ───────────────────────────────────────────────
|
|
60
|
+
{
|
|
61
|
+
id: "legal.citation-no-reporter",
|
|
62
|
+
domain: "domain",
|
|
63
|
+
description: "Case cited without reporter / year — likely hallucinated (Mata v. Avianca pattern).",
|
|
64
|
+
appliesTo: isLegal,
|
|
65
|
+
scan: c => {
|
|
66
|
+
// Case name followed by v., but NO reporter cite within 80 chars
|
|
67
|
+
const cases = [...c.answer.matchAll(/\b([A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*)\s+v\.\s+([A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*)/g)];
|
|
68
|
+
for (const m of cases) {
|
|
69
|
+
const tail = c.answer.slice(m.index! + m[0].length, m.index! + m[0].length + 120);
|
|
70
|
+
const hasReporter = /\b\d+\s+(?:U\.S\.|F\.\s?\d?d?|F\.\s?Supp\.?\s?\d?d?|S\.\s?Ct\.|L\.\s?Ed\.\s?\d?d?|Cal\.|N\.Y\.|N\.E\.\s?\d?d?|S\.W\.\s?\d?d?|P\.\s?\d?d?|A\.\s?\d?d?|So\.\s?\d?d?)\s+\d/i.test(tail);
|
|
71
|
+
const hasYear = /\(\s*(?:1[89]\d{2}|20\d{2})\s*\)/.test(tail);
|
|
72
|
+
if (!hasReporter && !hasYear) {
|
|
73
|
+
return [mk("critical", "LEGAL_CITATION_NO_REPORTER", `Case citation "${m[0]}" lacks reporter and year — likely hallucinated (Mata v. Avianca pattern).`, "Provide full Bluebook citation: case name, reporter volume, reporter abbreviation, page, court, year. Verify the case exists in Westlaw/LexisNexis before citing.")];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "legal.statute-no-section",
|
|
81
|
+
domain: "domain",
|
|
82
|
+
description: "Statute referenced (e.g. U.S.C., C.F.R.) without title and section.",
|
|
83
|
+
appliesTo: isLegal,
|
|
84
|
+
scan: c => /\b(?:U\.S\.\s+Code|United\s+States\s+Code|Code\s+of\s+Federal\s+Regulations|federal\s+statute|federal\s+regulation)\b/i.test(c.answer)
|
|
85
|
+
&& !/\b\d{1,2}\s+(?:U\.S\.C\.|C\.F\.R\.)\s+§?\s*\d+/i.test(c.answer)
|
|
86
|
+
? [mk("major", "LEGAL_STATUTE_NO_SECTION", "Statute referenced without Title and §section — citation is unverifiable.", "Use Bluebook format: `<title> U.S.C. § <section>` (e.g. `42 U.S.C. § 1983`). For regulations: `<title> C.F.R. § <section>`.")]
|
|
87
|
+
: [],
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// ── Contract drafting completeness ────────────────────────────────────────
|
|
91
|
+
{
|
|
92
|
+
id: "legal.contract-no-governing-law",
|
|
93
|
+
domain: "domain",
|
|
94
|
+
description: "Drafted contract / clause without governing-law clause.",
|
|
95
|
+
appliesTo: c => /\b(contract|agreement|MOU|terms of service|TOS|EULA|NDA|MSA)\b/i.test(T(c)) && /\b(draft|write|generate|create|sample)\b/i.test(c.prompt),
|
|
96
|
+
scan: c => /\b(this\s+agreement|the\s+parties|whereas|hereby\s+agree)\b/i.test(c.answer)
|
|
97
|
+
&& !/\b(governing\s+law|governed\s+by\s+the\s+laws?\s+of|venue|choice\s+of\s+law|jurisdiction)\b/i.test(c.answer)
|
|
98
|
+
? [mk("major", "LEGAL_CONTRACT_NO_GOVERNING_LAW", "Drafted contract lacks a governing-law / venue clause.", "Always include a governing-law clause (e.g. 'This Agreement shall be governed by the laws of [State], without regard to its conflict-of-laws principles.') and a forum-selection / venue clause.")]
|
|
99
|
+
: [],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "legal.contract-no-severability",
|
|
103
|
+
domain: "domain",
|
|
104
|
+
description: "Drafted contract without severability clause.",
|
|
105
|
+
appliesTo: c => /\b(contract|agreement|MOU|terms|EULA|NDA|MSA)\b/i.test(T(c)) && /\b(draft|write|generate|create|sample)\b/i.test(c.prompt),
|
|
106
|
+
scan: c => /\b(this\s+agreement|the\s+parties|whereas|hereby\s+agree)\b/i.test(c.answer)
|
|
107
|
+
&& !/\b(severability|severable|unenforceable[\s\S]{0,80}remaining\s+provisions)\b/i.test(c.answer)
|
|
108
|
+
? [mk("warning", "LEGAL_CONTRACT_NO_SEVERABILITY", "Drafted contract lacks a severability clause.", "Add a severability provision so that invalidation of one clause does not invalidate the entire agreement.")]
|
|
109
|
+
: [],
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// ── Privacy law confusion ─────────────────────────────────────────────────
|
|
113
|
+
{
|
|
114
|
+
id: "legal.gdpr-ccpa-conflation",
|
|
115
|
+
domain: "domain",
|
|
116
|
+
description: "GDPR and CCPA conflated as equivalent (they are not).",
|
|
117
|
+
appliesTo: c => /\b(GDPR|CCPA|CPRA|privacy\s+law|data\s+protection)\b/i.test(T(c)),
|
|
118
|
+
scan: c => /\b(GDPR\s+and\s+CCPA\s+(?:are|both)\s+(?:the\s+same|equivalent|similar)|like\s+GDPR,\s+CCPA|CCPA\s+is\s+the\s+(?:US|American)\s+GDPR)\b/i.test(c.answer)
|
|
119
|
+
? [mk("major", "LEGAL_GDPR_CCPA_CONFLATION", "GDPR and CCPA conflated as equivalent — they differ materially in scope, basis, and remedies.", "GDPR uses lawful-basis framework with broad data-subject rights; CCPA/CPRA is consumer-rights opt-out model with narrow scope. Differentiate explicitly: definitions of 'personal data', territorial scope, consent vs opt-out, fines, private rights of action.")]
|
|
120
|
+
: [],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "legal.privacy-no-dpia",
|
|
124
|
+
domain: "domain",
|
|
125
|
+
description: "GDPR high-risk processing without DPIA mention.",
|
|
126
|
+
appliesTo: c => /\b(GDPR)\b/i.test(T(c)),
|
|
127
|
+
scan: c => /\b(profil|automated decision|large[-\s]scale|systematic monitoring|special category|biometric|health data)\b/i.test(c.answer)
|
|
128
|
+
&& /\b(GDPR)\b/i.test(c.answer)
|
|
129
|
+
&& !/\b(DPIA|Data\s+Protection\s+Impact\s+Assessment|Article\s+35)\b/i.test(c.answer)
|
|
130
|
+
? [mk("warning", "LEGAL_GDPR_NO_DPIA", "GDPR high-risk processing discussed without Data Protection Impact Assessment (Article 35) requirement.", "Article 35 GDPR requires a DPIA for processing likely to result in a high risk (profiling, large-scale special-category data, systematic monitoring). Disclose this obligation.")]
|
|
131
|
+
: [],
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// ── Absolutes / overstatement ─────────────────────────────────────────────
|
|
135
|
+
{
|
|
136
|
+
id: "legal.absolute-rules",
|
|
137
|
+
domain: "domain",
|
|
138
|
+
description: "Absolute legal statements ('always', 'never') without exceptions.",
|
|
139
|
+
appliesTo: isLegal,
|
|
140
|
+
scan: c => /\b(always|never|in all cases|in every state|without exception)\b[\s\S]{0,80}\b(is\s+illegal|is\s+legal|is\s+required|is\s+prohibited|wins|loses)\b/i.test(c.answer)
|
|
141
|
+
&& !/\b(generally|typically|usually|in most|exception|except\s+for|caveat|varies)\b/i.test(c.answer)
|
|
142
|
+
? [mk("warning", "LEGAL_ABSOLUTE_RULES", "Absolute legal statement without acknowledging jurisdictional or factual exceptions.", "Hedge with 'generally', 'typically', or 'in most jurisdictions'. Legal rules almost always have exceptions, defenses, or jurisdictional carve-outs.")]
|
|
143
|
+
: [],
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
// ── Statute of limitations precision ──────────────────────────────────────
|
|
147
|
+
{
|
|
148
|
+
id: "legal.sol-no-jurisdiction",
|
|
149
|
+
domain: "domain",
|
|
150
|
+
description: "Statute of limitations stated without naming jurisdiction.",
|
|
151
|
+
appliesTo: c => /\b(statute of limitations|SOL|time[-\s]?bar|filing deadline)\b/i.test(T(c)),
|
|
152
|
+
scan: c => /\bstatute\s+of\s+limitations\s+(?:is|of)\s+\d+\s+(?:year|month|day)\b/i.test(c.answer)
|
|
153
|
+
&& !/\b(under\s+(?:U\.S\.|federal|California|New York|Texas|Florida)\s+law|federal\s+statute|state\s+law|jurisdiction[-\s]?specific|varies\s+by)/i.test(c.answer)
|
|
154
|
+
? [mk("major", "LEGAL_SOL_NO_JURISDICTION", "Statute of limitations stated without naming the jurisdiction and cause of action.", "SOL varies by cause of action (personal injury, contract, fraud, etc.) AND by jurisdiction. Always specify both.")]
|
|
155
|
+
: [],
|
|
156
|
+
},
|
|
157
|
+
];
|