Der Re-Ingest leitet regulation_short z.T. via title()-Casing aus Dateinamen ab
('dsgvo'->'Dsgvo', 'osha otm'->'Osha Otm') -> falsche Akronyme im Payload UND im
article_label ('Art. 37 Dsgvo'). NEU: normalize_regulation_short() in legal_metadata,
token-basiert mit kuratiertem Akronym-Set -> nur gelistete Akronyme werden gross,
legitimes Mixed-Case (GeschGehG, MuSchG, GoBD, MiCA, eIDAS, EuGH) bleibt unberuehrt.
Angewandt am Ingest-Rand in documents.py (greift fuer Payload-Feld + display_name).
+13 Tests gruen. Bestandsdaten brauchen separaten einmaligen Qdrant-Patch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Streamable-HTTP-MCP des Compliance-Repos (cra_assess_findings) erreichbar als
macmini:8002/mcp. SSE-sicher: proxy_buffering off, http/1.1, read_timeout 3600s,
Authorization (Bearer) wird durchgereicht. Additiv vor location / im 8002-Block.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deletes the night-scheduler (nightly auto-shutdown of services). It stopped
services at 22:00 and killed long-running jobs (e.g. bulk embedding) — net
hindrance. Removed: running container, compose service + hetzner override,
source dir, CI lint entry, rule doc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wandeldarlehen 400k model mapped into the official L-Bank V1.1
Finanzplan template (36 months, Aug 2026 to Jul 2029) for the three
scenarios. Each reconciles to the source liquidity to the cent; grants
booked as cash inflows (out of EBIT); "ohne Pre-Seed" excludes both
tranches; Planungsprämissen and helper tabs filled; Anleitung intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Long-form differentiator page covering the seven sales arguments from
project_marketing_website_3014_themes.md, all anchor-linkable for
sales decks:
#1 engine — Pattern-engine vs Excel-checklist
#2 multi-markt — One risk assessment, all markets (CE+US+CN+JP)
#3 folgegefahren — Operator-to-end-customer harm chain
#4 public-domain — OSHA/NIST/EUR-Lex/BAuA as legal anchor
#5 audit-suite — Engine self-introspection (cmd/iace-audit A-E)
#6 made-in-germany — German export meets US Federal PD
#7 tooling — LLM gap-review as co-pilot, not robo-lawyer
Each section carries a "Belegt durch:" line pointing at the actual
codebase artifact behind the claim, so the page reads as audit-friendly
proof, not marketing fluff.
Below the 7 differentiators a competitor comparison table (BreakPilot
vs DesignSafe vs Pilz PASS vs Sick SD vs Sphera) and a closing block
explaining the R1/R2/R3 license architecture with a pointer to
/sdk/licenses.
Navbar updated to surface the page between Plattform and CE-Prozess.
This closes Task #19. With Task #29 + #7/#8 already in, the roadmap
post-licence-classification work is fully landed.
Adds a "Quellen und Lizenzen der Compliance-Inhalte" section to the
marketing-website Impressum naming the public sources the platform
draws on (EUR-Lex, US Federal Code, ENISA/EDPB/BAuA, OWASP, OECD,
eigene Texte) and pointing to /sdk/licenses for the full per-source
breakdown.
The Datenschutz and Impressum audit (Task #24 in breakpilot-compliance)
confirmed no spurious license claims were buried in these pages.
This change adds explicit transparency rather than removing anything,
and is paired with the explicit disclaimer that the Pauschalvermerk
does NOT replace work-level attribution — that is handled by the
auto-footer in PDFs and the <SourceBadge> in the SDK frontend.
Two idempotent scripts that complete Task #22 (300k atomic_controls
reclassification) across both Postgres DBs and all Qdrant collections
on Mac Mini + Production.
backfill_license_rule.py
- iterative parent_control_uuid inheritance with cycle cap
- dry-run + apply modes, per-iteration row counts
- residual-orphan cluster report for manual review
backfill_qdrant_license_payload.py
- joins canonical_controls.id (or regulation_id) → license_rule
- scrolls + grouped set_payload per rule (3 batches per collection)
- supports both lookup tables (canonical_controls / regulation_registry)
- supports managed Qdrant via --qdrant-api-key (Production)
Backfill bilance:
- Mac Mini canonical_controls: 0 NULL (was 279,384) across 314,811 rows
- Mac Mini Qdrant atomic_controls_dedup: 44,987 points patched
- Mac Mini bp_compliance_gesetze: 37,634 points patched
- Mac Mini bp_compliance_datenschutz: 11,338 points patched
- Production canonical_controls: 0 NULL (was 259,914) across 294,027 rows
- Production Qdrant bp_compliance_gesetze: 55,836 patched
- Production Qdrant bp_compliance_datenschutz: 18,980 patched
- Production Qdrant bp_compliance_ce: 23,239 patched
Schema migration 002_regulation_registry.sql + 252 registry rows were
replicated to Production (was missing — only existed on Mac Mini).
20 BSI/DE-Gesetz entries added to registry to close Qdrant lookup gap.
100% deterministic classification achieved on both DBs via:
- parent_control_uuid inheritance (94% coverage)
- control_parent_links.source_regulation → regulation_registry
- source_citation->>'source' → regulation_registry
- canonical_processed_chunks ground truth (chunk-validated)
- ungrouped LLM-aggregate Vorfahren → own works (Rule 3)
[migration-approved]
Defines the authoritative mapping from license_type to license_rule
in docs/LICENSE_RULES.md, and adds scripts/audit_license_classification.py
to surface classification gaps in registry/canonical_controls/Qdrant.
Key finding from first audit run against bp-core-postgres + Qdrant:
- regulation_registry: 232 rows, 224 rule=1, 8 rule=2, 0 rule=3;
36 rows without license_type (need backfill)
- canonical_controls: 314,811 rows, 279,384 (89%) have NULL
license_rule (target of Task #22 reclassification)
- Qdrant atomic_controls_dedup: 100% of sampled points lack both
license and license_rule payload fields
- Qdrant bp_compliance_gesetze: 80.6% lack both fields
- Qdrant bp_compliance_ce + bp_compliance: nearly clean
Rule definitions clarified (was loosely remembered as
"law / cite / rewrite"):
- Rule 1 = verbatim, sovereign law (EU/DE/AT/CH/US, TRBS/TRGS/ASR,
OSHA, NIST, EU guidelines, DGUV UVV)
- Rule 2 = verbatim with attribution (CC-BY, Apache, OWASP,
OECD AI Principles, ENISA)
- Rule 3 = identifier citation only, no full text (DIN/EN/ISO,
ANSI/UL/IEC, DGUV Regeln/Informationen/Grundsaetze, BSI,
proprietary standards). Pipeline drops chunk_text when rule=3
in pipeline_adapter.py:147.
The 4th category I had proposed ("R1-A") turned out to be already
implemented as rule=2; the mapping doc reflects the actual code
behaviour rather than the original 3-name verbal model.
No schema change. No data migration in this commit — reclassification
of the 279k controls is staged as Task #22 and will be cluster-based
by source/regulation_id.
Per the user's batch review of the rendered PDF. Five subagents ran in parallel,
each owning a different slide file; this is the merged result.
Slide 10 — Regulatory Landscape (PrintProductSlides)
8 regulatory categories now render as a 2×4 icon-tile grid (was a DataTable):
Lock / Shield / Brain / Globe / ShieldCheck / Banknote / Heart / Users.
10 industry profiles now each show an icon next to the name
(Factory for Maschinenbau Kernfokus, plus Heart, Banknote, ShoppingCart, Cpu,
Wifi, Brain, ShieldCheck, BookOpen, Landmark, Building2).
Slide 12 — How It Works (PrintProductSlides)
Step rail and day timeline pulled together (was a big empty middle).
Added a "Was Sie wann bekommen" 4-column benefit block in the bottom third
(Shield/FileText/CheckCircle2/Zap), with mid-page "Median 14 Tage" callout.
Slide 13 — Market TAM / SAM / SOM (PrintMarketSlides)
Dropped MarketFunnel primitive. Left column: SVG nested concentric circles
(TAM r=60 violet, SAM r=36 violet, SOM r=14 amber as Kernmarkt). Right column:
three stacked TAM/SAM/SOM info cards with mono kicker, big EUR value, growth
rate, one-line description; SOM card carries amber accent + "← unser Kernmarkt".
Slide 14 — Pricing green box (PrintProductSlides)
Net-effect callout expanded from 2 lines to a full breakdown:
Pentests +€13k / CE-Risiko +€9k / Compliance-Zeit (−60%) +€15k /
Audit-Vorber. (auto) +€9k / Legal-Stunden (−40%) +€5k / Schulungen +€4k.
Italic footnote: "Plus Vermeidung von Bußgeldern und gewonnene RFQs."
Slide 17 — Competition AppSec title (PrintCompetitionSlides)
Title rewritten to investor-friendly framing — "Cyber-Security: BreakPilot
ersetzt das ganze AppSec-Stack" (was SAST + DAST + SCA + Pentesting).
Slide 18 — Team founder bios (PrintMarketSlides)
Prose paragraphs replaced with 5 icon-bulleted skill/achievement lines per
founder. Benjamin gets violet-50 tiles (Briefcase, RefreshCw, Handshake,
Scale, Lightbulb). Sharang gets amber-50 tiles (Code, TrendingUp, CreditCard,
ShieldCheck, Cpu). Photo + name + role + equity header preserved.
Slide 23 — KPIs trajectory (PrintNewSlides)
Each of the 8 KPI tiles now has a 15mm × 8mm SVG sparkline at the bottom
showing the 5-year progression. Stroke color adapts per metric (violet
default, emerald for cash/margin, red→emerald for EBIT/net-income across
break-even). All-zero series fall back to em-dash. Awkward "0 → 0" prefix
suppressed on missing-data tiles.
Slide 28 — Regulatory Pillars (PrintAnnexSlides)
Rebuilt as 4 actual vertical pillars (was 2×2 box grid). Each pillar has:
capital (top, gradient tint, mono kicker + 01-04 number), shaft (white card
with title + description + 2mm colored left border), base (bottom, darker
tint, mono law citations). A shared horizontal "ground line" below all four
pillars completes the architectural reference.
Slide 29 — Architecture 3D (PrintDiagrams)
Faked 3D depth via staggered right indent (0/2/4mm), inset top highlight
and bottom seam shadows, per-layer drop-shadow with rising opacity. Layer 03
reads as the foundation; layer 01 floats on top. PlaneConnector chevrons
replace the simple SVG down-arrows between tiers. Text stays horizontal.
Slide 31 — Tech Stack (PrintNewSlides)
Cards now have 14mm violet-gradient icon tiles (was 8mm flat), mono kicker
number, 12pt category name, italic one-line blurb, and the techs as rounded
chip tags (violet-50 / violet-200, mono 7.5pt) instead of a flat mono list.
Title cleaned: "100 % " → "100%".
All files under 500 LOC except PrintIntroSlides (515, preexisting issue).
TypeScript clean, next build green, all 38 routes compile.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three universal fixes before per-slide redesigns:
1. Brand: new <ComplAI /> JSX component renders the product name correctly —
'Compl' in inherited text color, 'AI' in violet (#7c3aed), no slashes.
Replaces the previous 'BreakPilot COMPL/AI/' literal in the Executive
Summary p1 title. Page primitive's title prop now accepts ReactNode so
JSX brand wordmarks work anywhere a title would.
2. Em-dash centering: Bullets primitive previously placed each em-dash
marker via absolute positioning with a hardcoded 'top: 4pt', which drifted
relative to font-size and looked off-center in the rendered PDF. Now uses
display:flex on the <li> with a fixed-width column that vertically centers
the 0.5pt rule on the first line height of the text.
3. Funding fallback: cover + The Ask now default to 400_000 (was 1_000_000)
when no funding amount is in the data. New base case is a €400k
Wandeldarlehen, not €1M equity.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the 5 slides flagged as missing vs Claude Design (30 slides). Standard
PDF now matches Claude's slide count and structure.
New slides (PrintNewSlides.tsx):
- TL;DR / 30 SEKUNDEN — 4 quad cards (Scale / Sovereignty / Bidirectional /
Speed) with mono kicker, hero stat, body and ticker line. Slot 3, after the
exec summary.
- Differentiators — 4 under-the-hood cards (Traceability / Engine / Optimizer
/ EU-Trust-Stack) extracted from USP p2. Slot 9, after USP. Each card has
the lucide icon in a violet/amber tile, full body + bullets, and the mono
ticker line.
- KPIs (Trajektorie 2026 → 2030) — 8 hero tiles showing year-1 → year-5
transitions (ARR, customers, ARPU, employees, gross margin, EBIT, net
income, cash). Derived live from computeAnnualKPIs(fmResults). Slot 23.
- Tech Stack — 8-category grid (Frontend / Backend / Storage / AI-RAG /
Code-Scanning / Auth / Comms / DevOps), each with lucide icon tile +
category label + monospaced tech list. Slot 31, after Engineering.
USP p2 redesigned: now hero-sized closing loop only (the 4 cards moved to
Differentiators). Bigger LoopDiagram in a violet-tinted hero panel, 12mm
inner padding, more room for the hub body + bullets.
P&L Detail (PrintFinancialsPage) promoted from financial-only to standard
PDF. Kicker now 21 (was '17b'), subtitle rewritten ('Annualisierte GuV',
no longer 'Investor-only'). Empty-data fallback added so it doesn't crash
if fmResults isn't populated.
Anhang divider moved from PrintAnnexSlides.tsx to PrintNewSlides.tsx (was
pushing PrintAnnexSlides over the 500-LOC cap). Section list inside the
divider updated for the new numbering — now 12 sections from #18 GTM down
to #29 Glossary.
PrintDeck.tsx: BASE_PAGES bumped 30 → 35. Render order updated; hasFinancialDetail
flag removed (P&L always rendered); cap-table is the only remaining
financial-only conditional and stays suppressed for Wandeldarlehen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Investors arriving at slide 16 (Customer Savings) currently jump straight
into annex-strategy without any chapter break — they don't know the main
pitch has ended and the appendix has started.
Adds PrintAnnexDividerPage that sits between customer-savings and
annex-strategy. Layout:
Part II · Anhang BreakPilot · ComplAI
─────────────────────────────────────────────────────────
16 · Kapitelwechsel
Anhang. (giant violet-dotted title, 74pt)
────────
Detail & Belege. (15pt lead)
Auf den folgenden Seiten
17 GTM Strategie 20 Reg. Details 23 KI-Pipeline
18 Finanzplan 21 Architektur 24 Risiken
19 Treibervariablen 22 Engineering 25 Glossar
─────────────────────────────────────────────────────────
BREAKPILOT · COMPLAI WANDELDARLEHEN 16 / 30
Uses .print-page-bg so the violet-tinted dotted background reads as the
same chapter as the rest of the deck. Footer matches the standard Page
primitive.
BASE_PAGES bumped 29 → 30. Bilingual (DE/EN).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The pricing slide previously rendered as a 4-column DataTable buried below
unit economics — the 3 tiers were hard to find. Rebuilt as the Claude Design
PREISE pattern: three prominent product cards side by side.
Each card:
- Mono tier label kicker (STARTER / PROFESSIONAL / ENTERPRISE) at top
- Target audience line ("<25 Mitarbeiter · Basis-Module" etc.)
- Hero price (€3.600 / €18.000 / ab €50.000) + /Jahr unit
- 4–5 feature checkmarks (green ✓)
- Tinted background per tier: violet-50 for Starter, white-gradient for
featured Professional, amber-50 for Enterprise
Professional card carries:
- 2px violet border (vs 1px on others)
- Drop shadow
- "BELIEBT" / "POPULAR" pill badge floating above its top edge in violet
Below the 3 cards, a compact 2-col footer:
- left: 4 Unit Economics tiles (~70% gross margin, ~3.5× LTV/CAC, etc.)
- right: emerald net-effect callout (+€30k per SME / yr)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Infrastructure layer was being cut off (only the chip showed; the 3 inference
service cards never rendered). Root cause: each tier was double-wrapped — an
outer tinted layer card AND inner bordered FlowNode cards — which inflated
the total height past A4 landscape.
Replaces inner FlowNode (border + padding + footer rule) with a new flat
ServiceNode used only inside the tinted layer wrappers:
- no own border / no own padding
- title 11pt → 10pt, kicker 7pt → 6pt
- caps inner items to 4 max
- mono tech footer in 6pt with hairline separator
Also tightened the connectors between tiers: was a 12mm row of three VArrow
SVGs each with its own padding, now a 5mm row of three compact down-arrow
SVGs. Saves ~14mm of vertical space.
Layer chip sizing reduced (7.5pt → 7pt, padding 1.5mm → 1mm) so each chip
takes less of its layer card.
Result: all three layers fit on one A4 landscape page with the LLM
Inference / Embeddings / AI Tools cards visible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five fixes per user review:
1. USP p1 overflow (stats were getting clipped). Tightened card spacing:
- icon tile 12mm → 9mm, moved inline next to title
- mono kicker for "SÄULE · COMPLIANCE" tags
- reduced paddings, title 13pt → 12pt, body 8.5pt → 8pt
- violet replaces indigo (already by alias, but explicit here)
2. USP p2 closing loop: was a plain tinted callout, now a 2-col hero panel
- left: violet circle around ∞, mono "DIE SCHLEIFE · ALWAYS IN SYNC",
bold headline (14pt), body
- right: white card containing the LoopDiagram with violet outline
- gradient violet→white→violet background for the panel
3. How It Works: replaced the floating-arrow StepStrip with a real
horizontal-rail timeline:
- Violet gradient connector line behind 4 numbered circles
- Each circle is a 14mm violet disc with the step number
- Title + body below each circle
Replaced the Time-to-Value callout with a dotted-rail timeline:
- 5 day markers (Tag 0/3/7/14/30) as violet pill chips on a dashed rail
- Stop label below each
- Mono header reads "Time-to-Value · Median 14 Tage · Worst Case 28 Tage"
4. Assumptions slide:
- "Skalare Annahmen" → "Treibervariablen des Finanzplans" (plain language)
- subtitle rewritten to explain the three-scenario sensitivity setup
instead of referencing internal fp_assumptions tables
- each category now a violet-bordered card with mono kicker + variable
count, italic instead of bare table
- sensitivity callout expanded with concrete runway impact numbers
5. Architecture diagram: layer chips per Claude Design pattern.
- Each tier wrapped in a tinted rounded card (violet for product +
inference, amber for gateway)
- "01 · APPLICATION LAYER" mono pill with italic sub-label
("User-facing services") next to it
- Gateway layer carries the LiteLLM Proxy title inline with subtitle
- Connector arrows kept between layers
Also fixes "Kleinstunternehmen" → "Kleinunternehmen" typo in solution
pillar 03 and the product pricing-logic callout.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adapts the visual language from the Claude Design reference (light theme) while
preserving our left-rule Page header and split-block cover.
Color palette: indigo (#4f46e5) → violet (#7c3aed) as primary accent across all
slides. COLORS.indigo* aliases kept so the existing 9 slide files inherit the
new palette without edits. New explicit COLORS.violet50..900 names available
for future code.
Body text shifted from pure slate to deep purple-tinted (#1a0f34) per Claude
tokens.fg.
Typography:
- Body / headings: Inter (was Plus Jakarta Sans)
- Mono utility: JetBrains Mono — applied to kicker tags, page numbers, footer,
the "At a glance" stat block on the cover, and the cover key-term labels
- Mono class .print-mono added to print.css
Background:
- New .print-page-bg utility paints a violet-tinted radial gradient
(white → #f5efff → #ebdfff) with a subtle 24px dotted grid SVG overlay
- Applied to every Page and the cover's right pane
Page chrome:
- Kicker label switched to JetBrains Mono with wider letter-spacing (0.18em)
- Right-of-kicker rule fades violet→transparent (was flat slate)
- New 2px violet gradient bar (700→400→700) below the title/subtitle —
the Claude Design "purple bar" accent, scaled down for print
- Footer restyled: mono caps "BREAKPILOT · COMPLAI" left, version (violet) middle,
page number right
Cover:
- Left block now a violet vertical gradient (was flat indigo)
- All small labels ("Investor Brief", "Auf einen Blick", "Confidential",
"Key Terms", and the term labels) restyled to JetBrains Mono with wider tracking
- Right pane carries the violet-tinted dotted bg, matching the rest of the deck
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per user review of the rendered PDF.
Problem: empty bottom-third on each card → added a bottom stat block per
column showing 3 pulled-out data points (e.g. "64% · 70% · 83%") with red
hero numerals. Description text trimmed since the stats now carry the punch.
Solution: pillar bodies were short, leaving large gaps between description
and the green stat at the bottom. Added 5 detail bullets per pillar (specific
tools, frameworks, behaviours) in the previously empty middle. Stat at the
bottom now reads as a real KPI tile, not a floating value.
Strategy: phase KPI was a tiny corner tag. Promoted it to a bottom
"Outcome" block with side-by-side 14pt numerals matching the phase tone
(2 Kunden / ARR €40k etc.). The bullets get more breathing room above.
The Ask reconciliation (was showing nonsense €4M pre / €5M post / 20%
investor share for a €200k Wandeldarlehen): detect convertible/SAFE/
Wandeldarlehen and swap the tiles to Funding / Discount / Maturity /
INVEST-grant. Equity rounds compute Pre/Post from amount × 20% assumed
investor share. Same conditional applied to the cover key-terms grid.
Pricing label "Was der Kunde zahlt vs. spart (KMU 50 MA, Jahr 1)" was
wrapping "1)" onto its own line — switched to a slash-separated form
("Kunde zahlt vs. spart · KMU 50 MA · Jahr 1") that fits on one line.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three critical fixes after reviewing the rendered PDF:
Cover (was: indigo block collapsed to top, white content stacked below):
- The .print-page class in print.css forces flex-direction: column !important,
which broke the horizontal split. Wrap the cover content in a single grid
container — the column-flex parent then has only one child so direction is
irrelevant. Indigo block now runs full-height on the left.
- Title reduced 88pt -> 60pt so "BreakPilot ComplAI." fits without wrapping.
- Funding amount formatter now handles sub-€1M cases (€200k vs €0.2M).
Finanzplan (was: "nicht verfügbar" on both pages 20-21):
- page.tsx was querying the legacy pitch_fm_results table which isn't populated
by the current pipeline. The interactive deck reads from fp_* tables.
- Wire in lib/finanzplan/adapter.ts (finanzplanToFMResults) which bridges the
live fp_* tables to FMResult[] — same source the interactive deck uses.
- Fall back to live default fp_scenario if the version snapshot's fm_scenarios
is empty.
- adapter.ts: populate total_customers + new_customers from fp_kunden_summary
(was hardcoded 0).
The Ask:
- target_date was rendering as raw ISO timestamp "2026-08-01T00:00:00.000Z";
now formatted as "Aug 2026" (locale-aware).
- Hero funding amount uses same sub-€1M formatter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two bug fixes plus the requested visual rework — the deck now looks like a pitch deck, not a research paper.
Bugs:
- BASE_PAGES corrected from 28 to 29; disclaimer no longer shows "29/28"
- fmResults + fmAssumptions now load for the standard PDF, not only when financial=true; Finanzplan annex + KPI dashboard now render
Visual rework (per user: "graphic elements, not just text"):
- Cover: split layout — indigo block left (tagline + hero stats + version meta), white block right with oversized title and key terms
- Modules: 12 lucide icons in indigo-50 tiles (ScanLine, ShieldCheck, FileText, ClipboardCheck, Users, UserCheck, AlertTriangle, Brain, Target, GraduationCap, TrendingUp, MessageSquare)
- USP cards: icon-led card heads with FileSearch/ArrowLeftRight/Repeat/Layers/etc.; LoopDiagram SVG on the closing "Compliance ↔ Code" hub
- How It Works: StepStrip primitive with visible right-arrows between steps
- Market: nested-rectangle MarketFunnel (TAM > SAM > SOM) replaces three stacked boxes
- Customer Savings: 4 hero KPIs + ComparisonBars (today vs. with BP) per cost item
- The Ask: DonutChart for use-of-funds
- Cap Table: DonutChart for equity distribution
- Finanzplan p2: 2×2 chart grid — Revenue (bars), EBIT (bars, tone by sign), Cash balance (line+area), Headcount (bars)
- Architecture: ArchitectureDiagram primitive (3 tiers, vertical arrows between tiers)
- AI Pipeline: PipelineFlow primitive (4 stages, horizontal arrows)
- Team: founder photos (32×32mm) added; falls back to initials if photo_url missing
New primitives:
- PrintCharts.tsx — BarChart, LineChart, ComparisonBars, DonutChart, ProgressBar, MarketFunnel
- PrintDiagrams.tsx — FlowNode, VArrow, HArrow, StepStrip, ArchitectureDiagram, LoopDiagram, PipelineFlow
All files under 500 LOC cap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Throws away the screen-deck-derived print system. Builds a new institutional-research aesthetic:
- 12-col grid on A4 landscape, hairline rules, no colored bars, no icons
- 3-color discipline: indigo (structural), emerald (positive), red (problem)
- Plus Jakarta Sans 800 for hero numerals + titles; tabular numerals everywhere
- 1-to-1 content parity with the interactive deck: full USP (8 cards), full competition matrix (45 features, 12 AppSec features, 8+6 competitor profiles), Finanzplan P&L grid + KPI dashboard, full glossary
- 2-page slides where content demands (Exec Summary, USP, Competition, Finanzplan)
- 28 base pages; +1 for Financial detail; +1 for Cap Table (suppressed on Wandeldarlehen)
Files:
- New: PrintIntroSlides, PrintProductSlides, PrintMarketSlides, PrintCompetitionSlides
- Rewritten: PrintLayout (new primitives Page/KpiRow/TwoCol/ThreeCol/DataTable/MatrixGlyph/Callout), PrintAnnexSlides, PrintFinancialSlides, PrintDeck
- Removed: PrintCoreSlides.tsx, PrintExtraSlides.tsx (obsolete)
- print.css now sets Plus Jakarta Sans as the print font family
- All files under 500 LOC cap
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clean-Room derivation of 195 controls from BSI QUAIDAL (10 criteria + 15
building blocks + 30 measures + 140 metrics) for EU AI Act Art. 10
training-data quality compliance.
- ingest_bsi_quaidal.py parses YAML frontmatter into a structural index
(no protected prose stored on disk).
- derive_quaidal_mcs.py rewrites each entry via local LLM (qwen3.5:35b-a3b)
with a hard 4-gram plagiarism gate < 20%; achieved mean overlap 0.5%.
- Migration 011 adds compliance.derived_controls table with full source
provenance (framework, section, url, commit SHA, license note).
- apply_quaidal_to_db.py UPSERTs YAML into DB.
- Source repo (legal-sources/bsi-quaidal/) gitignored.
Same pattern as IACE module DIN-reference handling: name the norm and
section, never quote.
Backed by BSI license clarification 2026-05: § 5 UrhG anwendbar,
share:true im Frontmatter; Clean-Room derivation is the safe path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Marketing-Website
- Neue SavingsSection auf Homepage: "Compliance entdeckt sechsstellige
Einsparungen". Pitch-Position der Cookie-Audit-Cost-Optimization-Story
fuer DAX-Konzern-Sales (BMW-Case-Style: 90 Vendors -> 25 nach
Konsolidierung, EUR 500k-3M / Jahr).
- /savings-scan: Kostenloser 5-Min-Saving-Scan-Form (URL + E-Mail).
Form-Submit ist Placeholder, soll an Compliance-Backend gehaengt werden.
- /savings-methodik: 4-Stufen-Erklaerung der Cookie-Tier-Inferenz +
ehrliche Caveats (Listpreise != Vertragspreise, Media-Spend nicht
enthalten) + Datenquellen.
- Content-de + Content-en in content.ts beide um savings-Block ergaenzt
und Section-Numerierung angepasst (03=Savings, 04=Deterministic).
- LOC-Split: savings-Inhalte (DE+EN, ~100 LOC) in content.savings.ts
ausgelagert damit content.ts unter 500-LOC-Hard-Cap bleibt.
Control-Pipeline
- LESSONS-LEARNED-mc-check-types.md fuer die parallele CRA-MC-Generation.
Erklaert die TEXT/PROCESS/REVIEW-Klassifikation die im Compliance-Repo
retrofitted wurde. Verhindert dass CRA-MCs denselben Defekt bekommen.
Mapping-Heuristik fuer verification_method -> check_type, plus
Backfill-Workflow fuer ~62 ambiguous Eintraege.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
globals.css sets html,body { height:100%; overflow:hidden; background:#0a0a1a }
with no media query. In print mode this clips all slides to one viewport
height (explaining the 2-page limit) and renders a black background.
Override with height:auto, overflow:visible, background:white in @media print.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Firefox doesn't honor height on flex containers in print mode — the
container collapses to content height, causing all slides to fit on 2
pages. Moved the authoritative height to the display:block wrapper
(.print-page-break) and changed .print-page to height:100% so it
fills its reliably-sized block parent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
page: slide-page on each block wrapper forces Firefox to allocate a new
physical page per slide — the spec-correct approach. break-before: page
is belt-and-suspenders. Switched from break-after to break-before via
adjacent sibling selector to avoid a blank trailing page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-moz-print-color-adjust: exact ensures Firefox doesn't strip background
colors from headers, badges, and accent elements when printing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chrome's print engine silently ignores break-after/page-break-after on
flex containers. Wrapping each .print-page (flex) in a plain block
.print-page-break element gives Chrome a reliable page break anchor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ConsentBanner: save consent_id to localStorage after successful POST
- Footer: DELETE /api/consent/{id} on consent re-open (Art. 17 DSGVO)
- New proxy route: DELETE /api/consent/[id] → backend withdrawal endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inline <style> tags in React body are unreliable for @media print in
Chrome. Move all print CSS to app/pitch-print/print.css imported via
a layout.tsx — Next.js injects this as a proper <link> in <head>,
which is guaranteed to be applied before print rendering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ScriptManager: two blocking mechanisms — injection of CONSENT_SCRIPTS
after consent + activation of type="text/plain" data-consent scripts.
Standard CMP blocking pattern ready for third-party analytics/marketing.
DSE: add Interessenabwaegung (balancing test) for Art. 6(1)(f) DSGVO
processing: Hosting and Server-Logfiles sections now document why
legitimate interest outweighs data subject rights.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Session ID via sessionStorage UUID
- Self-host Google Fonts (Inter, Plus Jakarta Sans, JetBrains Mono) — eliminates
third-party transfer to Google, no more DSGVO violation
- ScriptManager component: consent-change listener for future analytics/marketing scripts
- GeoIP via browser timezone (Intl.DateTimeFormat) + IP injection in proxy
- Vendor-level consent UI: loads vendor config from backend, shows per-vendor
toggles under each category, sends vendor_consents dict
- DSE updated: Google Fonts section now says "lokal gehostet"
- Config proxy route: GET /api/consent/config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch from inline pageBreakAfter to CSS class `.print-page` with
explicit `page-break-after: always !important` so Chrome print
preview creates a new page per slide (was collapsing to 2 pages)
- Remove margin/box-shadow in @media print so A4 boundaries align
- Content areas now use flex:1 so cards/pillars stretch to fill the
full page height (no more blank void below content)
- Remove conditional rendering on data-dependent slides — always
render all 9 core pages
- Larger font sizes throughout (11px body, 13px card titles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds /pitch-print/[versionId] — a server-rendered, print-CSS-optimized
page that generates investor-ready PDFs via the browser's native print
dialog (Save as PDF). Two variants per version:
- Standard PDF (9 pages): Cover, Problem, Solution, Products, Market,
Team, Milestones, The Ask
- Financial PDF (+4 pages): adds Financials P&L table (aggregated from
pitch_fm_results), Assumptions, Cap Table, Legal Disclaimer
White background with indigo accents, A4 landscape via @page CSS, all
color-rendered in print via print-color-adjust: exact. Auto-triggers
window.print() 900ms after load. Admin toolbar visible on screen only.
Export buttons added to /pitch-admin/versions/[id] detail page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ConsentBanner detects loaded scripts (analytics/marketing) and cookies
after consent, sends them to the CMP backend for transparency tracking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Send consent_method, page_url, referrer, device_type, browser, os,
screen_resolution and consent_scope with each consent record for
vendor-agnostic compliance tracking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 20 manually verified golden controls with expected MC topics
- Structural quality tests: min 10K MCs, max 300/MC, no orphans
- Doc-check controls tests: 8 doc types covered, no empty questions
- Quality thresholds: 90% accuracy, enforced by regression tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OSHA 29 CFR 1910 Subpart O (1910.211-1910.219) — complete machine
guarding requirements. US federal law, public domain.
International norms mapping table: China GB/T, Korea KS, India BIS
equivalents to ISO/EN standards. Unfortunately all countries protect
ISO copyright even for identical national adoptions (IDT).
Only OSHA provides truly free machinery safety content.
EU Excel harmonised standards list included for reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email security gateways follow GET redirects automatically and were
consuming the token before the investor clicked through. The verify page
now shows an 'Access Pitch Deck' button; the token is only consumed on
explicit click, which scanners cannot trigger.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Behind Orca's reverse proxy, request.url resolves to http://127.0.0.1:3000
which causes redirects to go to the internal address instead of the public
domain. Use PITCH_BASE_URL (already set in service.toml) as the base.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New pitch_short_links table stores 6-char alphanumeric codes mapped to magic link tokens
- GET /p/[code] redirects to /auth/verify?token=... (302, validates expiry)
- All magic link generation points (invite, generate-link, resend) now create a short code
- Emails (invite + resend) use the short URL — less token-like, cleaner for spam filters
- Copy-link UI shows short URL prominently with full URL as fallback
- Migration 008 added to /api/admin/migrate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ROW_LABEL_MAP (DE→EN) covering GuV, Liquidität, Kunden, Betriebliche Aufwendungen rows
- Add FORMULA_TOOLTIPS_EN with English tooltip text for all formula-driven rows
- Add MONTH_LABELS_EN (Mrz→Mar, Mai→May, Okt→Oct)
- LabelWithTooltip now accepts `de` flag, translates display text and tooltip accordingly
- Month column headers switch between DE/EN month abbreviations
- Falls back to original German label for any row not in the map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/admin/investors/:id now returns preferred_lang
- PATCH /api/admin/investors/:id accepts preferred_lang (de/en), validates value
- Investor detail page: DE/EN toggle in the Pitch Version card, instant save on click
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Version dropdown on the invite form shows all committed versions
- Selected version is assigned to the investor at creation time (no separate step needed)
- API validates version is committed before upserting
- Leaving the dropdown empty keeps any existing assignment (COALESCE behavior)
- version_id included in audit log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add English email template variants (greeting, message, closing, subject, CTA copy)
- Add `preferred_lang` column to `pitch_investors` — stored per investor, deck opens in that language by default
- Invite form: DE/EN language toggle that switches email defaults and pitch language setting
- Invite form: "Send email" toggle — when off, creates investor + returns magic link without sending email (for cold outreach attachment)
- `app/page.tsx`: initializes pitch language from investor's `preferred_lang` before first render (no flash)
- Migration 007 added to `/api/admin/migrate` route for production rollout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Splits master controls >200 members by re-clustering their object groups
with k=4-20 per group. First round: 38 groups → 325 sub-groups → 253 new MCs.
25 generic MCs remain (monitoring, procedure, etc.) — need regulation-source split.
Session summary: Block F complete, Control Generation (1,599+), Pass 0a/0b,
Production Sync, G-pre1/2/3 Object Clustering + Master Controls + API,
G1-G4 Compliance Execution Layer (Decision Trace, Commit Ledger, Decision Memory,
Pre-Deployment Enforcement).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: deployment_checks (verdict, blocking/warning controls, risk score)
New API:
POST /v1/deployment-checks (SDK asks: "can I deploy?")
GET /v1/deployment-checks/{id} (check result)
POST /v1/deployment-checks/{id}/override (manual override with justification)
GET /v1/deployment-checks/stats (approval/block rate)
Check logic: queries G1 decision_traces + G3 open failures per affected control.
Verdict: approved (0 blocking) or blocked (with fix recommendations).
454 tests pass, 0 regressions.
Block G complete: G1-G4 all implemented.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: decision_events (assessment→decision→fix→verification→failure cycle)
New API:
POST /v1/decision-events (record lifecycle event)
GET /v1/decision-events (list with filters)
GET /v1/decision-events/timeline/{control_id} (full chronological timeline)
GET /v1/decision-events/stats (failure rate, cycle times)
Each event captures input_state, output_state, actor, evidence.
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: compliance_commits (commit hash, affected controls, risk level)
New API:
POST /v1/compliance-commits (SDK registers commit + impact)
GET /v1/compliance-commits (list with filters)
GET /v1/compliance-commits/by-control/{id} (all commits for a control)
GET /v1/compliance-commits/stats (dashboard)
GET /v1/compliance-commits/{id} (detail)
GIN index on affected_control_ids for fast @> containment queries.
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: decision_traces (status, reason, evidence, fix plan per control)
New API:
POST/GET/PUT /v1/decision-traces (CRUD for decisions)
GET /v1/decision-traces/stats (compliance dashboard)
GET /v1/controls/{id}/full-trace (Regulation→Obligation→Control→Decision→Evidence)
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
G-pre1: 144k objects clustered into 7,466 groups via Mini-Batch K-Means
on bge-m3 embeddings. Two-stage: k=5000 base + sub-cluster groups >50.
G-pre2: 5,114 Master Controls from lifecycle phase chains
(define→implement→test→monitor), linking 172,504 atomic controls.
G-pre3: REST API for Master Controls
GET /v1/master-controls (list, search, filter)
GET /v1/master-controls/stats
GET /v1/master-controls/{mc_id} (detail with phase-controls)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8 tests confirm all REGULATION_LICENSE_MAP, ACTION_TYPES, _NEGATIVE_PATTERNS,
_ACTION_SYNONYMS, and _OBJECT_SYNONYMS entries are correctly migrated to DB.
Dicts kept as fallback for DB-unavailability resilience.
Block F complete: F1-F5 all done.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uses Ollama (qwen3.5:35b-a3b, think:false) to generate additional
German synonyms for action types and object tokens. Results stored
with source='llm' in action_synonyms/object_synonyms tables.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: init scripts ran repeatedly (on container restart) and tried
vault secrets enable / vault auth enable for already-existing paths.
Vault logged ERRORs and burned 40-84% CPU in the loop.
Fix:
- Marker file /vault/data/.init-complete skips re-initialization
- vault secrets list / vault auth list checks before enable calls
- No more "path already in use" errors on subsequent runs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The preview-data API was returning `fm_scenarios` but PitchDeck reads
`data.fp_scenarios`, so fpBaseScenarioId was always null and the
Finanzplan slide fell back to the global default scenario (Base Case 200k)
instead of the version's assigned scenario (e.g. 1 Mio. Euro Base).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Registers /generate/run-pass0a and /generate/pass0a-status/{job_id}
on the core control-pipeline (port 8098). Previously Pass 0a was only
available on the compliance backend which connects to Production DB,
causing a split-brain when controls are generated locally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI Q&A: fetch is_showcase from DB; showcase sessions receive no financial/funding
context and have an explicit LLM guard refusing to discuss investment details.
FAQ context and financial slide IDs stripped from system prompt.
FAB: flex layout so Fullscreen button is always visible regardless of panel height.
Presenter: pass activeSlideOrder to usePresenterMode so buildSlideAudioPlan maps
slideIdx → slideId from the filtered list, not the full SLIDE_ORDER. Progress
calculation also filters to active scripts only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NavigationFAB and SlideOverview now accept slideNames prop and render only the
active slide list (filtered for showcase mode). Adds AI presenter start button
to the FAB footer so it's accessible even when intro-presenter slide is hidden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds is_showcase boolean to pitch_investors; when set, filters out financials,
the ask, cap table, assumptions, finanzplan, risks, and intro-presenter slides.
Slide navigation is fully dynamic — progress bar and counts update accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/api/finanzplan now accepts ?scenarioId and uses it for the per-sheet
row counts (the numbers in brackets on the tab bar). FinanzplanSlide
passes fpBaseScenarioId when fetching the sheet list, so Wandeldarlehen
investors see e.g. Personalkosten (9) instead of (35).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The fm_scenarios array in each pitch version snapshot already stores the
fp_scenario IDs directly (same pattern 1 Mio used). Wandeldarlehen snapshots
were missing Bear/Bull entries — updated in DB to add them.
- /api/data: include fp_scenarios in version response (was omitted)
- PitchDeck: derive fpBaseScenarioId from data.fp_scenarios
- useFpKPIs: accept fpBaseScenarioId instead of isWandeldarlehen boolean
- AssumptionsSlide: find Bear/Base/Bull by name from fpScenarios prop
- FinanzplanSlide: initialize from fpBaseScenarioId, use version scenarios for selector
- FinancialsSlide / ExecutiveSummarySlide: pass fpBaseScenarioId to hook
- types: add FpScenarioRef + fp_scenarios field to PitchData
No UUID hardcoded in any component. Adding a new pitch version only
requires setting the correct fp_scenario IDs in its fm_scenarios snapshot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AssumptionsSlide sends ?scenarioId=<uuid> for Bear/Base/Bull cards but
the route was silently dropping it for non-admin requests, making all
three cards return the same default Base Case data. Since fp_ financial
projections are already investor-facing, any valid scenarioId is allowed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrates ACTION_TYPES (26+8 types), _NEGATIVE_PATTERNS (22), _ACTION_SYNONYMS
(65), and _OBJECT_SYNONYMS (75) from hardcoded dicts to DB tables.
- SQL migration: 003_action_object_ontology.sql (3 tables)
- Migration scripts: f2_migrate_actions.py (34 types, 145 synonyms), f3_migrate_objects.py (75 objects)
- OntologyRegistry cache: 5min TTL, raises RuntimeError if empty (safe fallback to dicts)
- control_ontology.classify_action/get_phase delegate to DB with dict fallback
- control_dedup.normalize_action/normalize_object delegate to DB with dict fallback
- 25 new tests, 446 total pass, 0 regressions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Next: F1 Regulation Registry (DB + API + Frontend + Auto-Create)
Frontend at /sdk/regulation-registry in breakpilot-compliance admin
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add case-sensitive _SINGLE_NUM_ALLCAPS_RE for "1. INTRODUCTION" style
headers (ENISA, BSI docs). Cannot use _LEGAL_SECTION_RE for this because
it uses re.IGNORECASE which would false-positive on "1. Erstens" etc.
Also re-downloaded 2 corrupt PDFs from nist.gov (nistir_8259a, nist_ai_rmf)
— originals in MinIO were 263-byte XML error responses, not PDFs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_SECTION_NUMBER_RE only had patterns for §/Art/Section/Kapitel/Annex
but missed NIST-style identifiers (AC-1, GV.OC-01, 3.1, A01:2021).
This caused 0% section rate for all NIST/BSI/ENISA documents even
though sections were correctly detected — the section NUMBER wasn't
extracted from the header.
Also adds:
- reupload_legal_strategy.py: re-upload with legal chunking
- extract_and_upload_nist.py: local PDF extraction workaround
- qdrant-snapshot.sh: backup mechanism for Qdrant collections
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete instructions for next session including:
- Current quality metrics per document type
- Prioritized action items (NIST fix, citation backfill, missing laws)
- Full Block E-G roadmap with details
- All critical files, DB state, test commands
- Known issues (3 lost NIST PDFs, frontend 500s, D5 script safety)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Session achieved: structural metadata end-to-end (D2-D4), overlap bug
fix, HTML stripping with charset detection, 430/436 docs re-ingested.
Remaining: ~40 EU Official Journal PDFs need HTML from EUR-Lex (broken
multi-column PDF extraction), 3 missing EDPB PDFs, 1 corrupt PDF.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default was 'pymupdf' which doesn't exist as a backend, causing
fallthrough to pypdf every time. With 'auto', the priority is:
unstructured > pdfplumber > pypdf.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EU Official Journal PDFs (AI Act, CRA, NIS2, DSGVO, etc.) use
multi-column layouts that pypdf breaks into fragmented words
("Ar tik el" instead of "Artikel"). pdfplumber handles these correctly.
Backend priority: unstructured > pdfplumber > pypdf (auto mode).
Also increases D5 re-ingestion timeout to 3600s for large PDFs.
58 embedding-service tests passing. pdfplumber: MIT license.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed:
1. Opening block tags (<h3>, <div>) now also create newlines, not just
closing tags. Fixes: gesetze-im-internet.de puts § inside <h3> which
followed inline <a> text — § ended up mid-line, not at line start.
2. HTML charset detection from meta tag (charset=iso-8859-1). Files from
gesetze-im-internet.de use ISO-8859-1, not UTF-8. The § byte (0xA7)
was destroyed by UTF-8 decode. Now: try UTF-8 → check meta charset →
fallback ISO-8859-1.
32 rag-service tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HTML files from gesetze-im-internet.de were decoded as raw UTF-8, keeping
<div>/<p> tags intact. The legal chunker regex requires § at line start,
which never matched inside HTML tags → 0% section metadata for HTML docs.
Fix: detect HTML content and strip tags before sending to embedding
service. Block elements become newlines, entities are decoded.
§ signs now appear at line starts → section detection works.
Also adds D5 re-ingestion scripts (reingest_d5.py + config) for
batch re-processing of all documents in Qdrant collections.
27 rag-service tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- lib/translate.ts: LiteLLM DE<>EN translation utility
- Migration 006: description_de/description_en on both dataroom tables
- Admin + investor upload APIs: accept description+lang, auto-translate the other language on save
- PATCH /api/admin/dataroom/documents/[id]: description path in addition to display_name path
- PATCH /api/dataroom/uploads/[id]: investor can edit their own upload descriptions
- PATCH /api/admin/dataroom/investors/[id]/uploads: admin can edit investor upload descriptions
- All GET queries updated to return description fields
- Admin dataroom: drop zone replaces upload button, multi-file, inline description editor per doc and per investor upload
- Investor dataroom: drop zone, multi-file, description+lang textarea before upload, inline description editing on existing uploads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
D2: RAG service stores section/section_title/paragraph/paragraph_num/page
from embedding service chunks_with_metadata into Qdrant payloads.
D3: Control generator prefers section > article > section_title from
Qdrant, adds page to source_citation and generation_metadata.
D4: Validated with real BGB §§ 312-312k text. Found and fixed critical
bug where Phase 3 overlap destroyed the [§ ...] section prefix, causing
only the first chunk per document to have metadata. All subsequent
chunks lost section info.
Also fixes pre-existing lint issues (unused imports, ambiguous variable
names, duplicate dict key, bare except).
456 tests passing (58 embedding + 387 pipeline + 11 rag-service).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dataroom routes were reading x-investor-id from request headers which
the middleware sets as response headers — these don't reach route handlers
when the admin fallback path runs (NextResponse.next() without header).
Switch to getSessionFromCookie() consistent with all other investor routes.
Auth page DSGVO footer switched from absolute bottom-0 to normal flow
so the expanded Art. 13 notice doesn't overlap the login card.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Docker volume inherits directory ownership from the image on first mount.
Without this, the volume mounts as root and the nextjs (uid 1001) process
gets EACCES when trying to write dataroom uploads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 Stunden → 30 Tage, expand scope to include personal contact data,
add Art. 15–21 rights, LfDI BW supervisory authority. Both DE + EN.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- runDataCleanup() replaces maskOverdueInvestors(): now also anonymizes
never-activated invites after 90 days, deletes sessions + magic links
older than 30 days, NULLs IPs in audit logs older than 30 days, and
redacts email from audit log details JSONB for masked investors
- New /api/admin/cleanup POST endpoint for scheduled invocation
- New .gitea/workflows/pitch-cleanup.yml: daily cron at 02:00 UTC calls
the cleanup endpoint so anonymization is genuinely automatic, not lazy
- Switch masking window from first_activity_at to last_login_at (30 days
of inactivity; resets on each login)
- Both auth pages: DSGVO footer now covers all Art. 13 requirements —
data categories, retention cutoffs, Art. 15–21 rights, contact address,
LfDI Baden-Württemberg as supervisory authority
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sends 67k review candidates to Haiku Batch API in pairs.
Each pair gets a DUPLIKAT/VERSCHIEDEN decision with reasoning.
Results stored in control_dedup_reviews.review_status.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dedup is done (162k controls). Re-enable healthcheck with generous
timeouts (10 retries × 30s) and restart: unless-stopped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New POST /api/admin/investors/[id]/generate-link endpoint: creates a
magic link without sending email, returns the URL for the admin to
copy and share manually (for when email is filtered)
- Adds 'Copy Link' button (emerald) to investor list and detail pages;
link is copied to clipboard on click
- New lib/masking.ts: maskOverdueInvestors() UPDATE that anonymizes
email/name/company → revokes sessions 72h after first investor login
- first_activity_at recorded on first verify (COALESCE, set once only)
- migration 004 adds first_activity_at + data_masked_at columns with
partial index; also wired into /api/admin/migrate for one-shot apply
- Admin UI shows 'anonymized' badge, expiry countdown, and masked state;
Copy Link + Resend are disabled for anonymized investors
- verify route returns 410 if data_masked_at is set (belt-and-suspenders
alongside the revoked status check)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tsconfig.json: add mcp-server to exclude list so the standalone MCP
package's imports don't break the Next.js type-check build
- FinanzplanSlide.tsx: resolve merge conflict, keep MonthlyGrid refactor
from upstream (discards superseded inline table from stash)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previously committed version was missing useIsLight hook, all sub-components
(PillarRow, ColHeader, CentralHub, BridgeConnectors, FeatureCard, DetailModal,
StarField, ticker components) and their data/types. Only the main component
shell was present, causing a CI build failure on type-check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scan endpoint needs up to 3-5 min (multi-page crawl + LLM calls).
Without explicit timeout, nginx defaults to 60s → 504 Gateway Timeout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stores last_control_id in canonical_generation_jobs after each page.
On restart, resumes from checkpoint instead of starting over.
Checkpoint is deleted on completion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port 3007 (admin-compliance) had no limit (nginx default 1M) causing
413 on SDK state saves. Port 8093 (SDK) had 10M, now 50M.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Paginated DB queries (100 rows/page) instead of loading all 166k rows
- Individual timeout (30s) per embedding + qdrant call
- Per-control try/except — one failure doesn't kill the job
- Sequential processing (no asyncio.gather) for stability
- Progress logging every 500 controls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dedup job blocks the event loop for extended periods, causing
health checks to fail repeatedly. Even 10 retries × 30s wasn't enough.
Disabled healthcheck and restart policy until dedup is complete.
TEMPORARY — re-enable after dedup is finished.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dedup Phase 2 blocks the event loop for extended periods, causing
health checks to fail. Docker then restarts the container and kills
the job. Increased retries from 3 to 10, timeout from 10s to 30s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153k of 160k merge groups have only 1 control — no intra-group
dedup possible. Skip them in Phase 1, they become masters automatically.
Phase 2 (cross-group) still checks them via Qdrant embeddings.
Reduces Phase 1 from ~96h to ~2h.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents duplicate batch submissions that caused ~$170 in extra costs.
Refuses new submit if a batch was submitted in the last 10 minutes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove applicability/scanner_hint/evidence_type/provides_context from
Pass 0b prompt to reduce output tokens (~40% less). These 6 fields are
added via cheap Haiku backfill afterwards (~$1.50 per 10k controls).
Saves ~$200 over the remaining 160k obligations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prompt v4 adds 6 new fields to Pass 0b output:
- applicability: condition rules (same format as dependency engine)
- check_type: expanded to 10 granular types
- scanner_hint: search_terms + negative_indicators for MCP
- manual_review_required_if: escalation conditions
- evidence_type: code/process/hybrid
- provides_context: context variables this control creates
New endpoint POST /generate/backfill-extended:
- Backfills existing 9k controls via Haiku Batch API (~$1.50)
- Adds all 6 new fields to generation_metadata
- Supports dry_run mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLM merge_key phases (e.g. "submission") don't always match PHASE_ORDER
keys. Derive phase order from action_type via get_phase_order() instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix truncated title detection: only flag near-200-char titles or mid-word cutoffs
- Fix evidence leak detection: check title start patterns, not keyword substring
("nachweisen" verb is valid action, "Nachweis vorliegen" is evidence)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- GET /generate/batch-api-status/{batch_id} — check Anthropic batch status
- POST /generate/process-batch — process completed batch results (background)
- GET /generate/process-batch-status/{job_id} — poll processing progress
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add assertion, pass_criteria, fail_criteria, check_type to AtomicControlCandidate dataclass
- Parse MCP fields from LLM output in _process_pass0b_control
- Store MCP fields in generation_metadata JSON for later use by MCP scanner
- Fields default to empty when not present (backward-compatible with old prompts)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Obligations classified before API call:
- evidence → skipped (saves API cost)
- composite → skipped (not atomic)
- framework_container → skipped (decompose separately)
- atomic → sent to LLM
Filter stats returned in submit response.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes from v2 evaluation (7.9/10 avg, 28 controls):
1. COMPOUND BAN: "durchführen UND Maßnahmen ergreifen" → pick primary action only
2. EVIDENCE-OF-ACTION: "Tests dokumentieren" → evidence field, not own control
3. PFLICHT=PROZESS: "Behörden informieren" + "Verfahren etablieren" = 1 control
4. MERGE-KEY BUG: merge_key from LLM output now stored in generation_metadata
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Key changes to system prompt:
- Evidence/documentation belongs in evidence field, NOT as separate control
- SBOM = 1 control (not "maintain" + "document" separately)
- Security lifecycle phases (identify/assess/remediate/monitor) = separate controls
- Same object + same action + same actor = 1 control (merge, not split)
- Titles must contain the ACTION, not just the subject
WRONG: "Vertraulichkeit Mitarbeiter"
RIGHT: "Mitarbeiter zur Vertraulichkeit verpflichten"
Titles serve as MCP search queries against customer documents/code.
Bad titles = bad search results = unusable product.
All 52,566 old pass0b controls deprecated (not deleted) for full regeneration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents UniqueViolation from blocking entire batch. Each result
is committed individually, errors are rolled back without affecting
subsequent results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previous version searched against atomic_controls_dedup collection which
only contains Pass 0b atomic controls. Now creates a temporary collection
with ALL draft controls as reference, then checks targets against it.
Two phases:
1. Index ~53k reference drafts into temp Qdrant collection (batch 32)
2. Search each of 14k target controls, Embedding + LLM for borderline
3. Cleanup temp collection when done
Status updates every 50 controls (fixed counter bug).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /generate/harmonization-recheck verifies promoted controls
against Qdrant dedup collection via Embedding + LLM. Runs as stable
asyncio background task inside the container (no docker exec issues).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
D1: Remove /api/admin/fp-patch from PUBLIC_PATHS — it was returning live financial
data (fp_liquiditaet rows) to any unauthenticated caller; middleware admin gate now
applies as it does for all /api/admin/* paths.
D2: Add PITCH_ADMIN_SECRET bearer guard to POST /api/financial-model (create scenario)
and PUT /api/financial-model/assumptions (update assumptions) — any authenticated
investor could previously create/modify global financial model data.
D3: Add PITCH_ADMIN_SECRET bearer guard to POST /api/finanzplan/compute — any
investor could trigger a full DB recomputation across all fp_* tables. Also replace
String(error) in error response with a static message.
D4: GET /api/finanzplan/[sheetName] now ignores ?scenarioId= for non-admin callers;
investors always receive the default scenario only. Previously any investor could
enumerate UUIDs and read any scenario's financials including other investors' plans.
D9: Remove `name` from the non-admin /api/finanzplan response — scenario names like
"Wandeldarlehen v2" reveal internal versioning to investors.
D10: Remove hardcoded postgres://breakpilot:breakpilot123@localhost fallback from
lib/db.ts — missing DATABASE_URL now fails loudly instead of silently using stale
credentials that are committed to the repository.
D6: Fix all 4 TypeScript errors that were masked by ignoreBuildErrors:true; bump
tsconfig target to ES2018 (regex s flag in ChatFAB), type lang as 'de'|'en' in
chat route, add 'as string' assertion in adapter.ts. Remove ignoreBuildErrors:true
from next.config.js so future type errors fail the build rather than being silently
shipped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C3: Split SYSTEM_PROMPT into PART1/PART2/PART3 constants; Kernbotschaft #9 and
VERSIONS-ISOLATION now concatenated directly at runtime instead of .replace() — a
whitespace mismatch can no longer cause placeholder text to leak verbatim to the LLM.
I2: Add second liquidity-chain pass (sumAus→ÜBERSCHUSS→rolling balance) after tax rows
(Gewerbesteuer/Körperschaftsteuer) are written to fp_liquiditaet, so first-run LIQUIDITÄT
figures include tax outflows without requiring a second engine invocation.
I6: Warn when loadFpLiquiditaetSummary finds no fp_liquiditaet rows for a named scenario,
surfacing scenario-name mismatches that would otherwise silently return empty context.
I8: Sanitize console.error calls in chat/route.ts (3 sites) and data/route.ts; cap
LiteLLM error body to 200 chars, use (error as Error).message for stream/handler errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Using terms like 'Version X' or 'Szenario Y' in the VERSIONS-ISOLATION
instruction implies other versions exist. Rewritten to never reference
version/scenario names — just 'this pitch deck, created for you, the only one'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes all hardcoded version-specific numbers from SYSTEM_PROMPT (200k,
40k/160k L-Bank split, 195 Kunden, 3.3 Mio, 9 MA). These are now generated
at runtime from the investor's assigned pitch_version_data: funding amount,
instrument, fm_scenarios name, and 2030 financials (customers, revenue,
employees).
loadPitchContext() now returns { contextString, meta } so the POST handler
can build correct isolation and Kernbotschaft strings for any version —
Wandeldarlehen 200k, 1 Mio, or any future scenario.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FAQ entries contain hardcoded financial numbers written for specific scenarios
(e.g. 470k Liquidität 2027, 200k/40k WD amounts). When an investor is on a
different version, those FAQ numbers would override the correct version-specific
context already injected from pitch_version_data.
Added an explicit priority instruction: version-specific Unternehmensdaten
always override FAQ content for any conflicting numbers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
loadPitchContext() now accepts a versionId and loads data from
pitch_version_data instead of hardcoded base table queries, matching
the pattern used by /api/data and /api/financial-model.
Also pulls fp_liquiditaet yearly summaries (LIQUIDITÄT, Summe ERTRÄGE,
etc.) for the matching fp_scenario so the agent quotes the correct
finanzplan numbers. Falls back to base tables when no version is assigned.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Engine now uses dynamic row_type-based summation instead of hardcoded label
strings that differed between scenarios (e.g. 'Summe ERTRÄGE' vs
'Summe EINZAHLUNGEN'), fixing stale 9.2M value in Wandeldarlehen scenarios.
Rolling balance now includes all financing cash flows via ÜBERSCHUSS chain.
MilestonesSlide: widen Theme type to union so t.key comparisons compile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds confidence scoring, escalation detection, and reasoning to the
deterministic filter. All assessment is deterministic (no LLM).
Confidence scoring (0.0-1.0):
- +0.25 industry specified
- +0.15 company size specified
- +0.20-0.30 scope signals provided
- +0.15 controls found
- +0.15 no contradictions
- Capped at 0.75 for escalation cases
Escalation triggers:
- Contradictory signals (holds_client_funds without operates_payment_service)
- Ambiguous signals (provides_embedded_connectivity)
- Financial signals without explicit payment service declaration
- Incomplete profile (no industry, size, or signals)
Reasoning: template-based, includes active signals, control count,
scope-condition descriptions, and warnings.
Response now includes "assessment" field with confidence, escalation_flag,
escalation_reason, inferred_signals, reasoning, and warnings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deleted 3 packages that were copied without validation:
- applicability_demo/ (fictional control IDs, wrong API schema)
- applicability_demo_sdk/ (wrong endpoint URL, fictional request format)
- applicability_demo_ci/ (GitHub Actions instead of Gitea, duplicated code)
Replaced with real integration in test_applicability_use_cases.py:
- TestApplicabilityIntegration calls real get_applicable_controls()
- Checks source_citation->source and control_id domain prefixes
- Runs against actual DB when DATABASE_URL is set
- 128 structure/acceptance tests pass, 24 integration tests skip without DB
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Makefile + pytest + GitHub Actions workflow for automated regression:
- make install / make eval / make test
- pytest integration with demo_cases.yaml
- Golden outputs for 6 priority cases
- Report generation (JSON + Markdown)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Request payloads + response contract + api_runner.py for 6 priority cases.
Can be run directly against /v1/applicability/evaluate endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 4 (Harmonization) now uses two-tier approach:
- Score >= 0.92: auto-duplicate (embedding only, fast)
- Score 0.85-0.92: LLM verification via local qwen3.5 (think=false, ~3s)
- Score < 0.85: not a duplicate
This eliminates ~44% false positives from pure embedding similarity.
LLM_DEDUP_ENABLED env var controls the feature (default: true).
Also adds 10 applicability use case tests (bank+TAN, webshop+Stripe,
SaaS startup, energy provider, health app, automotive, law firm, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /v1/canonical/generate/backfill-applicability enriches controls
with applicable_industries, applicable_company_size, scope_conditions
via Anthropic API. Targets ~26k controls from pipeline version < 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend was recalculating Summe EINZAHLUNGEN including funding (1M),
which made liquidity appear as ~1M throughout. Now all Liquidität
sum/balance rows (Summe, ÜBERSCHUSS, Kontostand, LIQUIDITÄT) come
directly from the engine-computed DB values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ollama API supports "think": false to disable extended thinking mode
on qwen3.5. Reduces response time from 95s to ~3s per comparison.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qwen3.5 uses extended thinking by default which causes 95s+ responses
and 30s timeouts. Add /no_think to system prompt and num_predict=200
to keep responses short.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /v1/canonical/generate/llm-dedup uses local Ollama (qwen3.5:35b-a3b)
to verify borderline duplicate matches (score 0.85-0.91). More accurate
than embedding similarity for compliance controls with subtle scope
differences (e.g. "documented" vs "implemented").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stale UUIDs in the Qdrant dedup collection can reference controls
that were deprecated in earlier batches. Log warning and continue
instead of raising and killing the entire job.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace "Normen" with "Leitlinien", "Regularien", or "Quellen" throughout
the pitch deck and presenter FAQ. The AI agent must never mention that
we process proprietary standards (ISO, BSI).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- System prompt now uses WD numbers (200k, 195 customers, 3.3M revenue, 9 MA)
- Added VERSIONS-ISOLATION rule: agent denies other versions exist
- "Dieses Pitch Deck wurde individuell für Sie erstellt. Es gibt nur diese Version."
- Fixed team scaling (2+7=9, not 5→35)
- Fixed pricing tiers (Starter/Professional/Enterprise)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix: Zurück-Button — onPrev was not passed to PresenterOverlay
- TTS: BreakPilot, Executive Summary etc. pronounced in English
- "Ihre Kunden" → "Unsere Kunden"
- "kein kleine" → "kein kleines und mittleres Unternehmen vorhalten kann"
- Removed all false "lösen/befreien" claims
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Why 2nd round 500k? Optional, depends on traction, discuss with investor
- Flexible convertible: 200k-400k, any amount possible via L-Bank
- Capital discipline: lean approach, open-source, German hosting, no waste
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- KPICard: accept string values (e.g. "380+") without NaN
- Remove cap-table slide from order + sidebar
- Remove Land & Expand arrow from Pricing slide
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add migration script (scripts/migrate_jsonb.py) that converts
89,443 Python dict repr rows to valid JSON via ast.literal_eval
- Column altered from TEXT to native JSONB
- Index created on generation_metadata->>'merge_group_hint'
- Remove unnecessary ::jsonb casts in pipeline_adapter.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /v1/canonical/generate/backfill-repair uses Anthropic API to
generate missing fields from available context (source text, other
fields). Handles 1,470 controls with incomplete data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cloud-Hosting, KI Tools, 3rd Party API → Materialaufwand
- New rows: Datenbank-Hosting, CDN/Storage
- Engine: compute Cloud-Hosting formula in materialaufwand
- Gross Margin now realistic (~82% in 2026)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- All charts now have Y-axis labels with scale
- X-axis with year labels on border lines
- Click any chart → modal with KPI explanation + yearly breakdown
- 8 detail explanations: MRR, EBIT, Headcount, Cash, Rev vs Costs, ACV, Gross Margin, NRR, EBIT Margin
- Unit Economics cards clickable with hover effect
- Compact 2x2 grid for EBIT/Headcount and Cash/RevCost
- ISO 27001 cert moved to Jan 2027 on production
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- USP: "Compliance und Code — in einer Plattform, immer synchron"
- Milestones: "Von der Idee zur GmbH — was wir bereits erreicht haben"
- Finanzplan subtitle: "Alle Werte in EUR"
- 2. Finanzierungsrunde (optional)
- Re-secure fp-patch
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revenue rows without qty/price (e.g. Beratung & Service) were excluded
from total. Now all revenue rows contribute to GESAMTUMSATZ.
Same fix for materialaufwand SUMME.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bulk-imported 67 umsatz + 78 kunden + 28 material rows to production WD
- Fix Bestandskunden annual column: show Dec value (point-in-time), not sum
- Fix tab labels: Umsatzerlöse, Liquidität (umlauts)
- Re-secure fp-patch endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Qdrant collections use regulation_id (not regulation_code), regulation_name_de,
guideline_name, download_url etc. Also search bp_compliance_datenschutz
collection where OWASP/ENISA docs live.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Go SDK RAG proxy returns 401 (Qdrant API key mismatch). Switch
AnchorFinder to use direct Qdrant vector search + embedding service,
same approach as the main pipeline. No dependency on Go SDK anymore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add POST /v1/canonical/generate/backfill-anchors endpoint for batch
populating open_anchors on controls generated with skip_web_search=true
- Uses AnchorFinder Stage A (RAG search) to find OWASP/NIST/ENISA refs
- Background job with progress tracking (same pattern as other backfills)
- Promotes needs_review controls that gain anchors to draft state
- Target audience normalization (enterprise/authority/provider → JSON arrays)
already applied via SQL
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move pill from SVG (behind HTML) to an absolutely-positioned HTML div
with zIndex:10 so it always renders above the milestone cards.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Increase timeline marginTop from 14→68 so it clears the absolute-positioned
Tip and Fortschritt elements at top:36.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keep only the top-level GradientText h2; drop the redundant h1 and kicker
inside each slide canvas.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ArchitectureSlide: full light mode via useIsLight() hook, all inline styles adapt
- USPSlide: full light mode via useIsLight() hook, all inline styles adapt
- MilestonesSlide: new component — horizontal timeline with past/HEUTE/future,
THEMES object (dark + light), clickable milestone nodes and stat cards with
detail modal, bilingual (de/en), scaling via ResizeObserver
- PitchDeck: register new 'milestones' slide case
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port Claude Design's USP v2 exactly: Compliance ↔ Code bridge with a
central glowing orb, two pillar rows per column, animated SVG flow
connectors, and four Under-the-Hood feature cards with live tickers.
Detail modal (Framer Motion AnimatePresence) slides in on click for
all 9 interactive elements. Star field background. All text is our
actual content (RFQ Verification, Process Compliance, Bidirectional
Sync, Continuous, End-to-End Traceability, Compliance Optimizer,
EU Trust Stack, killer quote). Full DE/EN i18n.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port Claude Design's V4 design exactly: three 3D perspective slabs
(Application / Gateway / Infrastructure) with animated data-flow connectors
between them, per-node live activity tickers, and a slide-up detail panel.
Replaces metro map with V4's floating slab aesthetic — dark purple gradient
background, CSS perspective rotateX transforms, JetBrains Mono terminal
tickers, amber LiteLLM hub with pulse indicator. All node data (titles,
tech stacks, services) preserved from previous design.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove flex-1 trailing divider that was pushing pills left
- Increase map height 420→480px to give clearance between app labels and gateway circle
- Update SVG viewBox to 0 0 1100 480 (consistent with CONNS coordinates)
- Update cy% for all nodes to match new 480px coordinate space (app 22.1%, gateway 50%, inference 80%)
- Update SEP1/SEP2 to 36%/65% for new height
- Update all SVG element y-coords: track lines, tick marks, junction dots, gateway stub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New architecture: CERTifAI (GenAI portal), COMPLAI (compliance), Scanner
(code security) — MCP bidirectional connection between COMPLAI and Scanner
- LiteLLM hub: token budget, PII guardrails, anon web search, no US providers
- Inference layer: Qwen3/DeepSeek (local LLM), bge-m3 embeddings, AI Tools
- Fix hover snapping: position wrapper (div) separate from scale (motion.button)
- Always-on data traffic packets on all connections
- Bidirectional MCP packets + MCP badge between COMPLAI and Scanner
- Live token ticker counter on active inference nodes
- BSI/EU sovereign badges on tier labels
- Spinning dashed ring on active LiteLLM hub
- Secondary infra chips in GenAI tier (PostgreSQL, Gitea, Orca, etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge conflict resolution incorrectly added Anhang: Annahmen and
Anhang: Go-to-Market twice. Remove the duplicates, keep only the
renamed Anhang: Systemarchitektur entry in the correct position.
Also move @import rules above @tailwind directives (Turbopack CSS spec).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace grid/SVG-line layout with archipelago map: organic island blobs,
quadratic bezier sea routes, circular map-marker nodes
- Fix SVG distortion: all strokes use vectorEffect=non-scaling-stroke
- No more preserveAspectRatio=none diagonal-line warping
- LiteLLM hub gets spinning ring + ripple pulse on active
- Ocean background with per-tier radial glows, dot grid, zone labels
- Switch dev server to --turbopack for faster HMR
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
One-time endpoint to halve D&O, E&O, Produkthaftpflicht, Cyber, Rechtsschutz
for 2027 and delete Editorial Content row.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Group features into 7 thematic sections with colored headers
- Set isDiff=true (star) for all features where only ComplAI has it
- USP slide: remove "ohne es zu brechen" from quote
- Product slide: Privacy-Hardware card colored (emerald) like Cloud card
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove Use of Funds card from The Ask slide
- Remove Go-to-Market slide from deck
- Move Annahmen & Sensitivität after Finanzplan in slide order
- Update sidebar names in both DE and EN
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Kunden labels: 7px → 11px (matching MRR)
- EBIT negative: moved from inside bar (mt-1) to above bar (-mt-4)
- Liquidität negative: same fix (-mt-4 for all values)
- grossMargin + nrr added to FinanzplanSlide KPIs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Charts tab showed 0 for Gross Margin and NRR because these fields
were not computed in the FinanzplanSlide's own fpKPIs loading
(only existed in the useFpKPIs shared hook).
Added: grossMargin = (revenue - material) / revenue × 100
Added: nrr = revenue / prevYearRevenue × 100
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Removed USP section (GDPR, AI Act, CRA etc.) — these are Compliance, not AppSec
- Removed Compliance-only features from APPSEC_FEATURES array
- Reordered: Competitors → Effizienz-Kennzahlen → AppSec Features → Score Summary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Finanzplan Charts tab: new Unit Economics chart showing ACV, Gross Margin,
NRR, EBIT Margin as mini bar charts per year (2026-2030).
BusinessModelSlide: sub-text under Unit Economics increased from 10px → xs (12px).
DB: Removed "Container in Produktion" metric from pitch_metrics + all versions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
USP Slide:
- 4 MOAT cards (was 3): added "Compliance Optimizer"
"Not just allowed/forbidden but the maximum permissible configuration
of every AI use case. Deterministic constraint optimization."
- Killer quote: "Everyone can say what is forbidden. Almost no one can
say how far you can go without breaking it. That is our product."
- Grid: 3 cols → 2x2 for better readability
Executive Summary:
- Competitors: removed Invest column, larger font (9px → 10px)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug: "SUMME Betriebliche Aufwendungen" excluded Personalkosten and
Abschreibungen because they have is_sum_row=true. Result: both sum
rows showed identical values.
Fix: explicitly include Personalkosten and Abschreibungen rows in
the SUMME Betriebliche calculation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Finanzplan is now read-only for investors:
- Removed "Berechnen" / "Compute" button
- Removed cell double-click editing
- Removed blue edit indicator dots
- All sums computed live in frontend (no manual recompute needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getValues() now reads values_invest for investment rows.
Previously only read values/values_total/values_brutto, missing the
invest-specific field name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Year navigation: "Alle Jahre" shows 5 annual columns, individual years show 12 months
- Default starts at single year view
- Annual view: flow rows show yearly sum, balance rows show Dec value
- Removed [section] labels from row display
- Footer sum only shown in monthly view (not annual)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extended live-compute to ALL tabs:
- Liquidität: "Summe ERTRÄGE" = sum of einzahlung rows,
"Summe AUSZAHLUNGEN" = sum of auszahlung rows
- Kunden: GESAMT rows = sum of tier detail rows
- Umsatz: GESAMTUMSATZ = sum of all revenue rows
- Materialaufwand: SUMME = sum of cost rows
ÜBERSCHUSS rows kept from DB (complex multi-step formula).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sum rows (is_sum_row=true) are now computed live in the frontend from
their detail rows, not read from stale DB values. This means:
- Category sums (Versicherungen, Marketing, Sonstige etc.) always match
- "Summe sonstige" = all non-personal, non-AfA rows
- "SUMME Betriebliche" = all rows including personal + AfA
- No more manual recompute needed after DB changes
Also: chart labels increased from 7-8px to 11px for readability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Charts tab:
- All bar labels increased from 7-8px to 11px (readable)
- New: Liquidität (Jahresende) bar chart — shows cash position per year
- New: Umsatz vs. Gesamtkosten — side-by-side bars per year
- All charts read from fpKPIs (fp_* source of truth)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Churn model: 25% of new customers leave after 3 months (trial period).
Remaining customers have normal monthly churn (3% Starter, 2% Pro, 1% Ent).
Churn label shows "25% Trial + X%/Mon".
DB: section 'unit_cost' renamed to 'einkauf' (removed English label from UI).
Code: unit price detection updated for new section name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Removed 'sonst_ertraege' from SHEET_LIST (empty, irrelevant for pre-seed)
- DB: Added Mac Studio (LLM Training) 13.000 EUR, Jan 2027, AfA 3 Jahre
- DB: Added Software-Lizenzen (GWG) 800 EUR/Jahr (2026-2030)
- DB: Added Domain/SSL/Zertifikate (GWG) 500 EUR at founding
- DB: Removed GPU-Server (wrong assumption — Mac Studio used instead)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Engine: All formula-based rows (F) now start at FOUNDING_MONTH (m8),
not m1. Affects: Fortbildung, Fahrzeug, KFZ, Reise, Bewirtung,
Internet, BG, Marketing, Serverkosten, Gewerbesteuer.
DB: All manual (M) betriebliche rows zeroed for m1-m7 across all
6 scenarios.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Removed auto-generated SUMME footer from umsatzerloese, materialaufwand, kunden tabs
(GESAMTUMSATZ/Bestandskunden gesamt rows already exist in DB data)
- GESAMT/Total rows now have thicker top border (border-t-2 white/20)
- unit_cost rows show unit price instead of annual sum
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Einkaufspreis rows (Mac Mini/Studio) showed sum of 12 months (e.g. 38,400)
instead of the unit price (3,200). Now detected via section='unit_cost'
or label contains 'Einkaufspreis' and shows the price value instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug: Finanzplan data grid always loaded Base Case (is_default=true)
even for Wandeldarlehen version, showing 35 employees + module-based
customers instead of lean 10-person plan.
Fix: isWandeldarlehen prop passed to FinanzplanSlide. On load, picks
Wandeldarlehen scenario by name match instead of is_default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FinancialsSlide:
- Break-Even: shows year (2029) instead of formatted number (2.029)
- GuV tab: replaced AnnualPLTable (useFinancialModel) with fp_guv data table
Shows: Revenue, Personnel, EBIT, Taxes, Net Income per year
- Cashflow tab: replaced AnnualCashflowChart (useFinancialModel) with
fp_liquiditaet bar chart showing cash position + EBIT per year
- Both tabs now show "Quelle: Finanzplan" label
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phases were duplicated between GTM slide and Strategy slide.
GTM now shows only: ICP (Ideal Customer Profile) + Channel Mix.
Phases remain exclusively on Strategy slide (version-aware).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All KPI numbers on the AI Pipeline slide now load from the
pitch_pipeline_stats table via /api/pipeline-stats:
- Legal sources: 380+ (was hardcoded 75+)
- Unique controls: 25k+ (was 70k+)
- Obligations: 47k+ (from DB)
- EU regulations, DACH laws, frameworks: from DB
- Pipeline steps text: all counts dynamic
Numbers can be updated via SQL without code deploy:
UPDATE pitch_pipeline_stats SET value = X WHERE key = 'legal_sources';
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use of Funds pie chart now shows actual spending breakdown from fp_* tables
(months 8-24) instead of manually set percentages:
- Engineering & Personal: from fp_personalkosten
- Vertrieb & Marketing: from fp_betriebliche (marketing category)
- Betrieb & Infrastruktur: from fp_betriebliche (other categories)
- Hardware & Ausstattung: from fp_investitionen
Falls back to funding.use_of_funds if fp_* data not yet loaded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- NRR: Revenue year N / Revenue year N-1 × 100 (no more "target > 120%")
- Payback: CAC / monthly gross profit (no more "target < 3 months")
- Both computed in useFpKPIs hook from fp_guv data
- BusinessModelSlide shows computed values with "(berechnet)" label
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New shared hook: useFpKPIs — loads annual KPIs from fp_guv/liquiditaet/personal/kunden.
Replaces useFinancialModel (simplified model) for KPI display on all slides.
Slides updated:
- CompetitionSlide: "110 Gesetze" → "380+ Regularien & Normen"
- BusinessModelSlide: ACV + Gross Margin from fp_* (was useFinancialModel)
- ExecutiveSummarySlide: Unternehmensentwicklung from fp_* (was useFinancialModel)
- FinancialsSlide: KPI cards from fp_* (ARR, Customers, Break-Even, EBIT 2030)
All slides now show consistent numbers from the same source of truth (fp_* tables).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Base Case KPIs now loaded from fp_guv/fp_liquiditaet/fp_kunden (source of truth)
- Bear/Bull derived from Base with scaling factors
- Assumptions text conditional: Wandeldarlehen shows lean plan (3→10, 8%/15% growth),
1 Mio shows original (5→35, aggressive growth)
- Removed dependency on useFinancialModel (simplified model)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New slide with 6 risks and concrete mitigations:
1. AI Commoditization — Layer 2-6 moat, not Layer 1
2. US Platform Expansion — EU-only infrastructure, CLOUD Act barrier
3. Team/Key-Person Risk — Documentation, ESOP, early legal hire
4. Slow Customer Acquisition — Consulting revenue bridge, channel strategy
5. Regulatory Changes — Enlarges market, RAG indexes in days
6. Liquidity Risk — Organic growth, Pre-Seed BW option
Key quote: "We don't compete with AI. We compete with teams that
use AI better than we do."
Presenter script added for the risks slide.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously KPIs/Charts used useFinancialModel (simplified model) which had
different assumptions than the fp_* tables (source of truth).
Now: KPIs tab loads from fp_guv, fp_liquiditaet, fp_personalkosten, fp_kunden
via API. Charts (MRR, EBIT, Headcount) also use fp_* data.
Removed dependency on useFinancialModel and computeAnnualKPIs for this slide.
Added Liquidität (Dez) row to KPIs table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Engine:
- Gewerbesteuer (F): 12.25% of monthly profit (only when positive)
- Berufsgenossenschaft (F): 2.77% of brutto payroll
- Allgemeine Marketingkosten (F): 10% of revenue
- Internet/Mobilfunk (F): Headcount × 50 EUR/Mon
UI: Tooltip for Gewerbesteuer formula added.
DB changes (production):
- Gewerbesteuer: (M) → (F), auto-calculated
- Rechtsanwalt/Datenschutz: new hire Oct 2026, 7500 EUR brutto
- Beratung & Services: new revenue line (5k→30k/Mon)
- Investitionen: Home Office 2500 EUR per new hire
- Marketing Videos moved to marketing category
- Bank → Bank-/Kreditkartengebühren
- Jahresabschluss costs filled (1000-2000 EUR/year)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `force: true` body param to POST /api/financial-model/compute that
skips the cached results check and recomputes from assumptions directly.
Exposes this via a "Force Recompute" button on the scenario edit admin page,
so updating assumptions directly in the DB can be followed by a cache bust
without touching the UI assumption flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the FM-name-based 'wandeldarlehen' hack with a proper scenario
picker. Scenarios are fetched from /api/finanzplan, default is selected
automatically. Dropdown appears when multiple scenarios exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AdminShell: shows NEXT_PUBLIC_GIT_SHA in sidebar footer
- Dockerfile + build-pitch-deck.yml: pass --build-arg GIT_SHA at build time
- FinanzplanSlide: fetch with cache:no-store to always show current DB values
- finanzplan routes: Cache-Control: no-store to prevent CDN/proxy staling
- CLAUDE.md: remove dead gitea remote (only origin exists)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CI/CD pipeline migrated from Coolify to Orca.
Updated CLAUDE.md, pre-push-checks, docs-src, and pitch-deck scripts/slides.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The admin preview was not returning fm_scenarios/fm_assumptions,
so preferredScenarioId was always null and all financial slides
fell back to Base Case (1M) instead of the version's scenario.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mandatory pre-push gates for all three language stacks with exact
commands, common pitfalls, and architecture rules. CLAUDE.md updated
with quick-reference section linking to the new files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TTS:
- CE → "C. E." for letter-by-letter pronunciation
- SAST → "S. A. S. T.", DAST → "D. A. S. T."
Finanzplan Slide 28:
- Data grid now loads Wandeldarlehen fp_scenario when active FM scenario
contains "wandeldarlehen" (scenarioId=c0000000-...-000000000200)
- Base Case version continues to load default fp_scenario
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Traction slide: "funktionsfähig und deployed" → "Prototyp-Stadium, mit Testkunden validiert"
- "bereit für zahlende Kunden" → "Ab August 2026 produktiver Betrieb"
- SDK Demo: "produktive Plattform" → "funktionierender Prototyp, mit Testkunden validiert"
- USP: "produktive Engine" → "leistungsfähige Engine"
Until founding in August 2026, all references must indicate prototype/test status.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TTS Language Bug:
- ChatFAB: detect response language from text content instead of UI language
- German text with umlauts/ß triggers German TTS even when UI is in English
Presenter Script (German TTS pronunciation):
- Add proper umlauts (ä/ö/ü) throughout German text
- Expand abbreviations for clear pronunciation:
DSGVO → Datenschutz-Grundverordnung
SAST → Static Application Security Testing
DAST → Dynamic Application Security Testing
SBOM → Software Bill of Materials
VVT → Verarbeitungsverzeichnis
TOMs → technisch-organisatorische Maßnahmen
BSI → Bundesamt für Sicherheit in der Informationstechnik
KMU → kleine und mittlere Unternehmen, etc.
Technical FAQ (12 new entries):
- BGE-M3, RAG, Qdrant, Cross-Encoder, Hybrid Search
- SAST/DAST, SBOM, BSI, Cloud Providers (SysEleven/Hetzner)
- Controls/Prüfaspekte, Policy Engine, VVT/TOMs/DSFA
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical fix: All financial slides now use the version's preferred scenario
instead of always defaulting to Base Case (1M). This ensures the
Wandeldarlehen version shows its own lean financial plan.
- useFinancialModel: add preferredScenarioId parameter
- PitchDeck: extract default scenario from previewData.fm_scenarios
- Pass preferredScenarioId to all 5 financial slides
- FinancialsSlide layout: remove empty right column, full-width charts
- Remove ScenarioSwitcher + unused slider from FinancialsSlide
- Fix COMPLEI → COMPLAI in presenter script (only TTS pronunciation differs)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows "Lade Finanzplan..." when annualKPIs is empty (data not yet loaded)
instead of rendering nothing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Investors should not be able to modify business case assumptions.
Questions should be directed to founders via the AI chat agent.
Scenario switcher is kept for viewing different scenarios.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DevSecOps, Onepager, SaaS, deployed, TypeScript, RegTech, OpenAI,
PostgreSQL, NVIDIA, GitLab, Full Compliance GPT, ERPNext — all marked
for English voice synthesis in German presenter script and FAQ.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When jwtVerify fails (JWT expired), decode the token without expiry check
to recover sessionId, validate it against the DB, and reissue a fresh 24h
JWT. Fixes investors with old 1h JWTs being locked out on magic link re-click.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace '25.000+' with 'über 25 Tausend' in DE text so Edge TTS speaks
it correctly instead of 'fünfundzwanzig Punkt null null null plus'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JWT was set to 1h while the session cookie lived 24h. After 1 hour the
cookie persisted but jwtVerify failed, making /api/auth/me return 401
and the re-click redirect fall through to the already-used token error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
German permanently routes to compliance TTS service (Edge TTS neural
voice, Piper fallback). OVH DE path removed — no env var can flip it
back accidentally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without the flag, German routes to the compliance TTS service which uses
Edge TTS (de-DE-ConradNeural) with Piper as fallback — easier to A/B
between OVH and compliance/Edge TTS without code changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OVH honours sample_rate_hz and returns data at exactly the requested
rate, so synthesis and WAV header rates must always match. Decoupled
22050/16000 caused 22050 Hz PCM wrapped in a 16000 Hz header → slow
bloated playback. Both back to 16000 Hz.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OVH uses sample_rate_hz in the request for internal synthesis quality
but always outputs raw PCM at 16000 Hz. Sending 22050 for synthesis
gives better pronunciation; declaring 16000 in the WAV header gives
correct playback speed. Previously both were the same value, forcing
a tradeoff between quality and speed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OVH Riva ignores the sample_rate_hz request param and always returns at
its native 16000 Hz. Declaring a higher rate in the WAV header causes
proportionally slower/deeper playback. 16000 Hz matches the actual output.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
22050 Hz declared in WAV header while Riva returns 44100 Hz native PCM
causes playback at half speed — deep, bloated voice. Aligning the
declared rate with the actual output fixes pitch and speed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If an investor clicks the magic link again after already being logged in,
check /api/auth/me first — valid session → redirect to / immediately
instead of showing the 'link already used' error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Client component (investors/new page) imported DEFAULT_MESSAGE etc. from
lib/email.ts which also top-level initialises nodemailer — webpack tried
to bundle fs/net/dns into the client chunk and failed.
Extract the pure constants + getDefaultGreeting into lib/email-templates.ts
(client-safe), keep nodemailer in lib/email.ts (server-only), update the
page to import from email-templates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add nodemailer to serverExternalPackages so webpack doesn't try to
bundle fs/net/dns built-ins (was fatal build error)
- Import jwtVerify from jose/jwt/verify instead of the full jose index
to avoid pulling in JWE deflate code incompatible with Edge Runtime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ExecutiveSummarySlide:
- Unternehmensentwicklung: hardcoded table → useFinancialModel + computeAnnualKPIs
(MA, Kunden, ARR now computed from finanzplan DB for all versions)
- Pricing: aligned with BusinessModelSlide tiers (Starter/Professional/Enterprise)
Enterprise: 40k → 50k (matching Folie 11)
BusinessModelSlide:
- ACV: hardcoded "15–50k" → computed from summary.final_arr / final_customers
- Gross Margin: hardcoded "> 80%" → computed from lastResult.gross_margin_pct
All financial numbers on all slides now flow from the same compute engine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All financial data now flows from the same compute engine (useFinancialModel).
No more hardcoded numbers in any slide — all values are derived from the
finanzplan database, ensuring consistency across all pitch deck versions.
- FinanzplanSlide: KPI table + charts now use computeAnnualKPIs() from FMResult[]
- BusinessModelSlide: bottom-up calc (customers × ACV = ARR) from compute engine
- AssumptionsSlide: Base case from compute, Bear/Bull scaled from Base
- New helper: lib/finanzplan/annual-kpis.ts for 60-month → 5-year aggregation
- PitchDeck: passes investorId to all financial slides for version-aware data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email template (email.ts):
- Bilingual: German body + DE/EN legal footer
- Customizable greeting, message body, and closing
- Magic Link explanation box (hardcoded)
- Confidentiality & Disclaimer footer (hardcoded, bilingual)
Invite page (investors/new):
- Name is now required, Company is optional
- Editable fields: greeting, message, closing (with defaults)
- Live email preview panel (right side)
- Shows full email content before sending
- German UI labels
API (invite/route.ts):
- Passes greeting, message, closing to email function
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds legal footer to the investor invite email with:
- Confidentiality obligation (3 years, purpose limitation)
- Disclaimer (not an offer, projections only, risk of total loss)
- Jurisdiction: Konstanz, German law
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Conditional sections only shown when instrument is "Wandeldarlehen"
- 200k investor ask + 200k L-Bank = 400k total funding visualization
- 3-step explanation: Investment → Conversion → Investor advantage
- Pre-Seed BW / L-Bank co-financing info box
- Cap Table before/after conversion example
- Use of Funds EUR amounts based on 400k total budget
- "1 Mio." version remains completely unaffected
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace emoji with Landmark icon
- Add JSON.parse fallback for use_of_funds
- Guard pieData labels and amounts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 15% tax-free acquisition grant (corrected from 25%)
- 25% exit grant on capital gains
- Up to 40% effective support (entry + exit combined)
- Program extended until 31.12.2026
- Disclaimer to verify current terms at bafa.de
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Theme toggle button below language toggle
- Uses existing theme-light CSS class from globals.css
- Moon/Sun icons with Nacht/Tag labels (DE) or Dark/Light (EN)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- All text-[10px] → text-xs (12px)
- All text-[9px] → text-[11px]
- All text-[8px] → text-[10px]
- Affected: BusinessModel, Product, Savings, Strategy slides
- Engineering: revert LoC to 481K (compliance SDK only, not all repos)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ext. DSB: 6k→3k (halved, not eliminated)
- Compliance docs: 0→2k (reduced effort, not zero)
- Personnel: "~2/8/40 MA savings" → "50% more productive compliance time"
(realistic productivity gain, not full headcount elimination)
- ROIs now credible: KMU 3.7x, Mittelstand 6.4x, Konzern 15.6x
(was 11x/21x/62x — too aggressive for investors)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 17 more umlaut fixes (Konformitätsbewertung, Löschkonzept,
Portabilität, Regelmäßige, etc.) across 6 files
- ComplAI → COMPLAI in all string contexts for consistency
- BrandName component used for JSX rendering (gradient AI)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Finanzen slide crashed on mount with "a.toFixed is not a function".
Traced to UnitEconomicsCards.tsx:59 calling ltvCacRatio.toFixed(1),
where ltvCacRatio arrives as a string.
Root cause: the cached path in /api/financial-model/compute returns raw
rows from pitch_fm_results. node-postgres returns NUMERIC / DECIMAL
columns as strings by default, so lastResult.ltv_cac_ratio (and every
other *_eur / *_pct / *_ratio field) flows through the app as a string.
Arithmetic-heavy code paths survived on accidental string-coerce (`-`,
`/`, `*`), but direct method calls like .toFixed() don't coerce, which
is why Unit Economics was the visible crash site.
Fix at the boundary: register a single types.setTypeParser(NUMERIC, …)
on the pg Pool so every query returns real numbers. All our NUMERIC
values fit well inside Number.MAX_SAFE_INTEGER, so parseFloat is safe.
One-line change, no component-level defensive coercions needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Regulatory landscape footer: text-xs text-white/50 (was text-[9px] text-white/20)
- New POST /api/admin/import-fp endpoint to import fp_* data from JSON dump
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /api/admin/migrate creates all fp_* tables on production DB.
Admin-only, creates tables with IF NOT EXISTS for safe re-runs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitHub avatar (github.com/mighty840) saved as /team/sharang-parnerkar.jpg.
Team-data JSON for both draft versions (Wandeldarlehen and The Ask 1 Mio)
was updated out-of-band via the admin API:
- Bio lengthened (~640 chars DE/EN) to match Benjamin's depth — now
covers the ETO tenure (3→60 org scale), ETOPay, ViviSwap/MiCA,
enterprise AI on AWS/Azure/SysEleven, embedded Rust work, and the
ferrite-sdk open-source project.
- photo_url switched from empty to /team/sharang-parnerkar.jpg.
- Expertise tags unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Compute endpoint now returns cached results if available (single SELECT
instead of DELETE + 60 INSERTs)
- When recompute is needed, batch all 60 rows into a single INSERT
- Reduces DB calls from 61 to 2 (cached) or 3 (recompute)
- Fixes timeout/blank financial slides for investors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SDK Live Demo was janky: AnimatePresence mode="wait" unmounted the
current Image before mounting the next, so every advance forced a cold
fetch and left an empty black frame until the new image decoded. Only
the first three screenshots had priority; the rest fetched lazily, so
the first pass through the carousel repeatedly stalled.
Replaces the single swap-in/swap-out Image with a stack of 23 images
layered in an aspect-[1920/1080] container. Cross-fades are now pure
CSS opacity on always-mounted nodes, so there is no unmount and no gap.
Key details:
- priority on the first 3 (triggers <link rel="preload">); loading=eager
on the remaining 20 so the browser starts all fetches at mount rather
than deferring via IntersectionObserver.
- sizes="(max-width: 1024px) 100vw, 1024px" lets next/image serve the
actual displayed resolution instead of the 1920 hint — fewer bytes,
faster first paint.
- Load-gated reveal: a new `shown` state trails `current` until the
target image fires onLoadingComplete. If the user clicks ahead of
the network, the previous loaded screenshot stays visible — no more
black flashes before images arrive.
Second pass through the carousel is instant (images are in-cache).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an OVH-backed branch to /api/presenter/tts so the German presenter
narration is synthesized by OVH AI Endpoints' nvr-tts-de-de (NVIDIA Riva)
reached through the LiteLLM passthrough at /tts-ovh/audio/*, which
injects the OVH API token server-side.
- DE requests now hit ${LITELLM_URL}/tts-ovh/audio/v1/tts/text_to_audio
with the documented body shape (encoding=1, language_code=de-DE,
voice_name=German-DE-Male-1, sample_rate_hz=22050) and return the
audio/wav bytes upstream serves (confirmed RIFF-framed in a smoke test).
- EN continues to hit compliance-tts-service until OVH_TTS_URL_EN is set,
making the eventual EN switch a single env flip.
- OVH and voice/url/sample-rate parameters are env-overridable
(OVH_TTS_URL_DE, OVH_TTS_VOICE_DE, OVH_TTS_SAMPLE_RATE,
OVH_TTS_URL_EN, OVH_TTS_VOICE_EN) so retuning doesn't need a redeploy.
- Defensive: OVH failures surface as 502 (no silent fallback) so upstream
issues are visible during this test rollout.
- wrapPcmAsWav() helper is kept as a safety net in case OVH ever returns
bare PCM instead of a full WAV.
Adds X-TTS-Source response header (ovh | compliance) to make
provenance observable from DevTools.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Photo extracted from CV and placed in public/team/
- Team data updated via MCP (both versions):
- Bio: 18+ years industry/strategy, SVP at ETO GRUPPE,
60 employees, M&A, 11 patents, VDMA/CyberLAGO memberships
- Role: CEO & Gründer (was CEO & Co-Founder)
- Expertise tags: Strategie & M&A, DSGVO/AI Act/CRA,
IoT & Embedded, Web3 & Blockchain, 11 Patente
- photo_url set to /team/benjamin-boenisch.png
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SDK Live Demo slide renders screenshots via next/image from
/public/screenshots/*.png. Because /screenshots was not on the
PUBLIC_PATHS list, every request was 307-redirected to /auth, and the
next/image optimizer responded with
HTTP 400 "The requested resource isn't a valid image."
leaving the slide with empty dark frames (surfaced in the pitch preview).
next/image also bypasses middleware itself (see the matcher), but the
server-side fetch it performs for the source URL does hit middleware
and carries no investor cookie, so whitelisting the path is required
even for authenticated viewers.
These PNGs are public marketing assets — there's no reason to gate them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The SDK live-demo slide renders a fake browser URL bar to frame each
screenshot. It used admin.breakpilot.ai, but the actual demo instance
investors should be able to reach lives on admin-dev.breakpilot.ai.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Investors who lost their session or whose invite token was already used
can now enter their email on /auth to receive a fresh access link,
without needing a manual re-invite from an admin.
- New /api/auth/request-link endpoint looks up the investor by email,
issues a new pitch_magic_links row, and emails the link via the
existing sendMagicLinkEmail path. Response is generic regardless of
whether the email exists (enumeration resistance) and silently no-ops
for revoked investors.
- Rate-limited both per-IP (authVerify preset) and per-email (magicLink
preset, 3/hour — same ceiling as admin-invite/resend).
- /auth page now renders an email form; submits to the new endpoint and
shows a generic "if invited, link sent" confirmation.
- Route-level tests cover validation, normalization, unknown email,
revoked investor, and both rate-limit paths.
- End-to-end regression test wires request-link + verify against an
in-memory fake DB and asserts the full flow: original invite used →
replay rejected → email submission → fresh link → verify succeeds.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- USP as slide title (GradientText) above
- Circle doubled to 380px with spinning ring
- Infinity symbol (∞) in center hub instead of text
- Compliance left, Code right inside circle — larger font
- 4 cards in corners (220px wide, larger text, ~5 lines each)
- Cards spread to corners (top/bottom, left/right)
- Dashed SVG lines connecting circle to cards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Large spinning circle (320px) with USP hub in center
- Compliance items left, Code items right inside circle
- 4 arrows pointing outward to capability cards
- 2 cards left (RFQ, Bidirectional), 2 cards right (Process, Continuous)
- Longer descriptions (~5 lines per card)
- Grid layout: cards | circle | cards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace broken absolute positioning with clean grid layout:
- Top: Compliance card | BreakPilot hub (spinning) | Code card
- Arrows + sync labels between cards
- Bottom: 4 capability cards in a row
- No more floating text, no overlapping elements
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cover: remove "für den Maschinenbau" from tagline
- Problem subtitle: Maschinenbauer → Deutsche und europäische Unternehmen
- New USP slide after Solution: bridge between compliance docs/audits
and actual code implementation — RFQ verification, bidirectional sync,
automated process compliance, continuous instead of annual checks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
faqMatch (undefined) → faqMatches[0]. The undefined variable caused
a ReferenceError after streaming completed, which the catch block
turned into "Verbindung fehlgeschlagen" for every subsequent message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admins in preview mode can now use /api/chat and other investor
endpoints without needing a separate investor login.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Regulatorien + Branche moved to top header row
- Branche: white/70 instead of white/30 for readability
- Regulatorien: indigo color instead of grey
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove colored dot legend row (redundant with column headers)
- Stagger column headers on 2 rows (odd/even) to save space
- Last column: Reg. → Regulatorien
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Column headers: centered text labels instead of icons
- Remove colored dots from headers
- Last column: # → Reg. (Regulierungen)
- Consistent column width for last column
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- grid items-stretch so cards match height
- Smaller avatar (16->64px) to free vertical space
- Equity moved to a top-right pill (compact); decimals collapsed via equityDisplay()
- Profile link icon auto-detects GitHub vs LinkedIn vs generic
- Expertise tags get their own divider strip at card bottom — cleaner hierarchy
- Card background lightened from 0.08 to 0.04 with subtle hover border
Bio text itself shortened on the data side (both draft versions via admin API).
build-pitch-deck workflow now posts an HMAC-signed push event to orca's
webhook endpoint after the image is built + pushed. This avoids the race
where orca would otherwise redeploy with the old :latest image before
CI finishes pushing the new one.
Removed the obsolete deploy-coolify.yml (wrong branch, wrong system) and
stripped the deploy-coolify job from ci.yaml.
Requires Gitea Actions secret: ORCA_WEBHOOK_SECRET_PITCH_DECK
- Batch-Postprocessing: Controls mit title/objective = None/null/"" werden
gefiltert und nicht gespeichert. Title wird aus Objective abgeleitet falls
nur Title fehlt.
- _store_control: Pre-store Quality Guard lehnt leere Controls ab
- Verhindert "None"-Controls die durch LLM-Parsing-Fehler entstehen
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root Cause: _generate_control_id erzeugte ID-Kollisionen (String-Sort statt
numeric), ON CONFLICT DO NOTHING verwarf Controls stillschweigend, Chunks
wurden als "processed" markiert obwohl Store fehlschlug → permanent verloren.
Fixes:
1. _generate_control_id: Numeric MAX statt String-Sort, Collision Guard
mit UUID-Suffix Fallback, Exception wird geloggt statt verschluckt
2. _store_control: ON CONFLICT DO UPDATE statt DO NOTHING → ID immer returned
3. Store-Logik: Chunk wird bei store_failed NICHT mehr als processed markiert
→ Retry beim naechsten Lauf moeglich
4. Counter: controls_generated nur bei erfolgreichem Store inkrementiert
Neue Counter: controls_stored + controls_store_failed
5. Anthropic API: HTTP 429/500/502/503/504 werden jetzt retried (2 Versuche)
6. Monitoring: Progress-Log zeigt Store-Rate (%), ALARM bei <80%
7. Post-Job Validierung: Vergleicht Generated vs Stored vs DB-Realitaet
WARNUNG wenn store_failed > 0, KRITISCH wenn Rate < 90%
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Nginx SSL server block for pitch-deck on port 3012
- Route through Nginx instead of direct container port
- Restore secure cookie flag (requires HTTPS)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HTTP access on local network was blocked by secure cookie flag when
NODE_ENV=production. Now requires explicit opt-in via env var.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Anchor-Search (DuckDuckGo + RAG via SDK) verlangsamt Pipeline von
~50 Chunks/min auf ~10 Chunks/min. Anchors (OWASP/NIST-Referenzen)
koennen nachtraeglich in einem Batch-Job befuellt werden.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Setup instructions for the pitch version MCP server.
.mcp.json contains the admin secret and is gitignored.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stdio MCP server that wraps the pitch-deck admin API, exposing 11 tools:
list_versions, create_version, get_version, get_table_data,
update_table_data, commit_version, fork_version, diff_versions,
list_investors, assign_version, invite_investor.
Authenticates via PITCH_ADMIN_SECRET bearer token against the deployed
pitch-deck API. All existing auth, validation, and audit logging is
reused — the MCP server is a thin adapter.
Usage: add to ~/.claude/settings.json mcpServers, set PITCH_API_URL
and PITCH_ADMIN_SECRET env vars. See mcp-server/README.md (to be added).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Testet: minimaler/voller Kontext, verbotene Nutzungen (KI/Standard),
Datenarten-Mapping, TOM bei hohem Konflikt-Score, Speicherfristen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces raw JSON textarea in version editor with proper form UIs:
- Company: single-record form with side-by-side DE/EN tagline + mission
- Team: expandable card list with bilingual role/bio, expertise tags
- Financials: year-by-year table with numeric inputs
- Market: TAM/SAM/SOM row table
- Competitors: card list with strengths/weaknesses tag arrays
- Features: card list with DE/EN names + checkbox matrix
- Milestones: card list with DE/EN title/description + status dropdown
- Metrics: card list with DE/EN labels
- Funding: form + nested use_of_funds table
- Products: card list with DE/EN capabilities + feature tag arrays
- FM Scenarios: card list with color picker
- FM Assumptions: row table
Shared editor primitives (components/pitch-admin/editors/):
BilingualField, FormField, ArrayField, RowTable, CardList
"Edit as JSON" toggle preserved as escape hatch on every tab.
Preview: admin clicks "Preview" on version editor → opens
/pitch-preview/[versionId] in new tab showing the full pitch deck
with that version's data. Admin-cookie gated (no investor auth).
Yellow "PREVIEW MODE" banner at top.
Also fixes the [object Object] inline table type cast in FM editor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Arrays of objects (funding_schedule, founder_salary_schedule, etc.)
now render as editable tables with per-field inputs, add/remove row
buttons, instead of a raw JSON string in a single text input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Builds and pushes to registry.meghsakha.com/breakpilot/pitch-deck
on every push to main that touches pitch-deck/ files. Tags with
:latest and :SHORT_SHA.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Control-Pipeline (Pass 0a/0b, BatchDedup, Generator) als eigenstaendiger
Service in Core, damit Compliance-Repo unabhaengig refakturiert werden kann.
Schreibt weiterhin ins compliance-Schema der shared PostgreSQL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pool-Size von 5 auf 20 erhöht (Connection-Exhaustion bei
parallelen Finanzplan-Queries + Compute + API-Calls)
KPIs/Charts Tabs laden keine DB-Daten (virtual tabs,
Daten sind hardcoded) → sofortiges Rendering
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Summenzeile auch für Umsatzerlöse und Kunden
- Kunden-Sheets: Jahresspalte zeigt Dezember-Wert (Bestand, nicht Summe)
- Bereits existierende Summenzeilen werden nicht doppelt gezählt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Gründer immer sort_order 1+2, dann nach start_date
- Beide Gründer exakt gleiches Gehalt (7.000 EUR/Mo ab Jan 2027)
- Alle Pos-Namen durchnummeriert (Pos 3 bis Pos 35)
Umlaute in DB-Labels (Liquidität, GuV, Betriebliche):
Umsatzerloese→Umsatzerlöse, UEBERSCHUSS→ÜBERSCHUSS,
Koerperschaftsteuer→Körperschaftsteuer, etc.
Engine-Labels synchron aktualisiert.
Summenzeile (SUMME) als tfoot für:
Personalkosten, Materialaufwand, Betriebliche Aufwendungen,
Investitionen, Sonstige Erträge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neue Folie "Investition & Cap Table" nach The Ask:
- Pie Chart: Gründer 75%, Investor 19,6%, ESOP 5,4%
- Pre-Seed Details: 4M Pre-Money, 975k Investment, 4,975M Post-Money
- Gründergehälter: 0 (2026) → 7k (2027) → 8k (2028) → 9,1k (2029+)
- Gewinnverwendung: 100% Reinvestition, kein Dividende bis Series A
- INVEST-Programm (BAFA): 20% Zuschuss = 195.000 EUR zurück
- ESOP: 5,4% für Schlüsselmitarbeiter, 4J Vesting, 1J Cliff
- Series A Ausblick: 15-25M Bewertung bei 3M+ ARR
Finanzplan: Gründer 7.000 EUR/Mo ab Jan 2027, 14% jährl. Erhöhung
FAQs: Cap Table + Gewinnverwendung als Fließtext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Architektur-Umbau: FAQ-Antworten werden NICHT mehr direkt gestreamt.
Stattdessen werden die Top-3 relevanten FAQ-Einträge als Kontext
ans LLM übergeben. Das LLM interpretiert die Frage, kombiniert
mehrere FAQs bei komplexen Fragen und antwortet natürlich.
Vorher: Frage → Keyword-Match → FAQ direkt streamen (LLM umgangen)
Nachher: Frage → Top-3 FAQ-Matches → LLM-Prompt als Kontext → LLM antwortet
Neue Funktionen:
- matchFAQMultiple(): Top-N Matches statt nur bester
- buildFAQContext(): Baut Kontext-String für LLM-Injection
- faqContext statt faqAnswer im Request-Body
- System-Prompt Anweisung: "Kombiniere bei Bedarf, natürlicher Fließtext"
Behebt: Komplexe Fragen mit 2+ Themen werden jetzt korrekt beantwortet
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
System-Prompt: "Antworte wie ein Mensch im Gespräch, keine Bulletlisten,
erkläre das WARUM, TTS-optimiert"
Alle 6 Team-FAQs + Module-FAQ als natürlicher Fließtext umgeschrieben:
- Deutsche Rollennamen (Vertriebsmitarbeiter, Kundenbetreuer, etc.)
- Begründungen eingebettet ("Der Grund ist...", "Das haben wir bewusst...")
- Übergangssätze für natürlichen Redefluss
- 3-5 Absätze pro Antwort statt Aufzählungen
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- prevSlide() in usePresenterMode: springt zur vorherigen Folie,
stoppt aktuelle Audio, startet Präsentation der vorherigen Folie
- SkipBack Button in PresenterOverlay neben SkipForward
- Beide Buttons springen zur korrekten Folie UND starten die Audio
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Überschuss = NUR operativer Cashflow:
Einzahlungen: Umsatz + Sonst.Erträge + Anzahlungen (OHNE EK/FK)
Auszahlungen: Material + Personal + Sonstige + Steuern (OHNE Kredit)
= Operativer Überschuss
Kontostand = Vormonat + Operativer Überschuss + Finanzierung
Finanzierung = EK + FK - Kreditrückzahlungen (separat)
So zeigt der Überschuss die echte operative Performance,
die Kapitaleinzahlung erscheint nur im Kontostand.
Marketing: 5.000€/Mo ab Jul 2027 (statt 20k)
Alle Werte Math.round() — ganzzahlig
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PDF: @page 297mm x 680mm mit 30mm Margins (passt zum getesteten Format)
Personal: 35 Positionen (5/10/17/25/35 MA pro Jahr)
Kunden: ~20/122/379/733/1213 verteilt auf 4 Pricing-Tiers
Startup 60%, KMU klein 25%, KMU mittel 10%, Enterprise 5%
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CE-SW-Risiko: "auf Code-Basis schon in der Entwicklung"
- "Compliance GPT" ohne "Echtzeit"
- Problem +Bullet: "EU-Regulierung unterscheidet nicht klein/groß"
- Sicherheitskontrollen → Prüfaspekte (Hero + KPI-Kachel)
- Pricing: "Startup" ohne "/ <10"
- Markt: SOM mit * "nur Anlagen- und Maschinenbau"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Problem: "Hohe Kosten für Pentests und Audits — nur einmal im Jahr"
Lösung: +CE-SW-Risikobeurteilung Echtzeit, +Compliance GPT,
Pflichten statt CE-Risikobewertungen, Jira entfernt
Spalten: grid-cols-4 / grid-cols-6 gleichmäßig verteilt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vollstaendiger Disclaimer-Text (DE + EN) am Ende der Executive Summary:
- Slide: Dezente Box mit 7px Schrift, vor dem Download-Button
- PDF (DIN A3): Gleicher Text in #94a3b8 vor dem Footer
Inhalt: Keine Anlageberatung, zukunftsgerichtete Aussagen,
Team Breakpilot (noch keine GmbH), Vertraulichkeit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CSS-Variablen-basiertes Theming (globals.css)
- .theme-light Klasse auf html-Element schaltet alles um
- Toggle-Button oben rechts (Sonne/Mond Icon)
- Light Mode: helle Hintergruende, dunkle Texte, gedaempfte Glass-Effekte
- Alle text-white/* Klassen werden per CSS Override umgemapped
- Partikel-Background auf 8% Opacity im Light Mode
- Kein text-shadow-glow im Light Mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GuV hat Jahres-Keys (y2026) statt Monats-Keys (m1-m60).
Eigene Tabelle mit 5 Jahrsspalten, Jahresnavigation ausgeblendet.
Alle Summenzeilen (EBIT, Ergebnis, Jahresueberschuss) hervorgehoben.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nach dem Audit: Haupt-/Nebenabweichungen automatisch abarbeiten —
Rollen zuweisen, Stichtage, Tickets, Nachweise einfordern,
Eskalation an GF. Kein Excel, kein Hinterherlaufen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Echte KMU-Sorgen statt irrelevante 4.1B-Statistik:
1. KI-Dilemma: Wollen KI, aber keinen Copilot/Claude im Code
2. Patriots Act: Selbst EU-Server der US-Player unsicher
3. Regulierungs-Tsunami: 5+ Gesetze, 50k/Jahr Stichproben
Quote: "Maschinenbauer brauchen eine KI-Loesung, die in Deutschland
laeuft, ihren Code schuetzt und Compliance automatisiert."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Maschinenbau als neue Kern-Branche in Matrix (15 Regularien)
- Alle Branchen-Counts aktualisiert (synced mit breakpilot-lehrer)
- 9→10 Branchen ueberall konsistent (i18n, KPIs, Presenter, FAQ)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neue Folie als erste Content-Slide (nach Intro) mit kompakter
Investor-Uebersicht: Problem/Loesung, KPIs, Markt, Team, Funding.
PDF-Download via window.print() ohne zusaetzliche Dependencies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace plain recursive chunker with legal-aware chunking that:
- Detects legal section headers (§, Art., Section, Chapter, Annex)
- Adds section context prefix to every chunk
- Splits on paragraph boundaries then sentence boundaries
- Protects DE + EN abbreviations (80+ patterns) from false splits
- Supports language detection for locale-specific processing
- Force-splits overlong sentences at word boundaries
The old plain_recursive API option is removed — all non-semantic
strategies now route through chunk_text_legal().
Includes 40 tests covering header detection, abbreviation protection,
sentence splitting, and legal chunking behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Screenshots were owned by root but Next.js standalone runs as nextjs user,
causing image optimization to fail with 'not a valid image' error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure all pitch messaging: Cloud-based SDK platform with 65+ modules
is the CORE product. Mac Mini/Studio repositioned as side product for small
firms. Updated presenter scripts (20 slides), FAQ (35 entries), and chat
system prompt with new Kernbotschaften covering company compliance, Code/CE
scanning, EU AI hosting, Jira integration, and additional features.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Chat answers are now read aloud via Edge TTS (auto, with mute toggle)
- FAQ team answer: vague text → Benjamin Boenisch (CEO) + Sharang (CTO)
- System prompt: explicit instruction to always cite team names from DB
- Speaker icon in chat header shows speaking state, click to mute/unmute
- Audio stops on new message, chat close, or mute
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Concatenate all paragraphs + transition hint into one TTS call per slide
→ natural prosody, zero gaps within a slide
- Pre-fetch next slide's audio during current playback → seamless transitions
- Advance slide during transition phrase ("Let us look at...")
- Pause/resume without destroying audio → instant continue
- Subtitle display synced to playback position via timeupdate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
useSlideNavigation.ts has 'use client' — server API routes can't import
from it. Move SLIDE_ORDER to lib/slide-order.ts (no 'use client') and
re-export from useSlideNavigation for backwards compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
stateRef was still 'resuming' when advanceRef.current() ran,
causing it to bail out. Now sync stateRef immediately before advance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser autoplay policy blocks audio.play() outside user gesture.
Use AudioContext to unlock audio immediately in click handler.
Add console logging for TTS debugging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New API route /api/presenter/tts proxies to compliance-tts-service
- usePresenterMode now uses Audio element with Piper-generated MP3
- Client-side audio caching (text hash → blob URL) avoids re-synthesis
- Graceful fallback to word-count timer if TTS service unavailable
- Add TTS_SERVICE_URL env var to pitch-deck Docker config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Integrate Web Speech API into usePresenterMode for text-to-speech
- Speech-driven paragraph advancement (falls back to timer if TTS unavailable)
- TTS toggle button (Volume2/VolumeX) in PresenterOverlay
- Chrome keepAlive workaround for long speeches
- Voice selection: prefers premium/neural voices, falls back to any matching lang
- Fix all German umlauts across presenter-script, presenter-faq, i18n, route.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Migrate chat API from Ollama to LiteLLM (OpenAI-compatible SSE)
- Add 15-min presenter storyline with bilingual scripts for all 20 slides
- Add FAQ system (30 entries) with keyword matching for instant answers
- Add IntroPresenterSlide with avatar placeholder and start button
- Add PresenterOverlay (progress bar, subtitle text, play/pause/stop)
- Add AvatarPlaceholder with pulse animation during speaking
- Add usePresenterMode hook (state machine: idle→presenting→paused→answering→resuming)
- Add 'P' keyboard shortcut to toggle presenter mode
- Support [GOTO:slide-id] markers in chat responses
- Dynamic slide count (was hardcoded 13, now from SLIDE_ORDER)
- TTS stub prepared for future Piper integration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddlePaddle 3.x + PP-OCRv5 requires >6GB RAM and has oneDNN
compatibility issues on CPU. PaddleOCR 2.x with PP-OCRv4 works
reliably with ~2-3GB RAM and has no MKLDNN issues.
- Pin paddlepaddle<3.0.0 and paddleocr<3.0.0
- Simplify main.py — single init strategy, direct 2.x result format
- Re-enable warmup (fits in memory with 2.x)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous FLAGS_use_mkldnn env var was ignored by PaddlePaddle 3.x.
Now using paddle.set_flags() API and PaddleOCR enable_mkldnn param.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set FLAGS_use_mkldnn=0 before paddle import to avoid
ConvertPirAttribute2RuntimeAttribute error
- Support both PaddleOCR 2.x (list) and 3.x (dict) result formats
- Use use_textline_orientation (3.x) instead of use_angle_cls
- Remove latin lang fallback (not supported in 3.x)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The warmup OCR call during startup pushes memory over 6G and causes
OOM kills + restart loops. First real OCR request will be slow
(JIT compilation) but container stays stable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After every push to gitea, Claude automatically polls health endpoints
and notifies the user when deployment is ready for testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add PaddleOCR PP-OCRv5 service with 4G memory limit, model volume,
and health check (5min start period for model loading). Domain routing
(ocr.breakpilot.com) to be configured in Coolify UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Hetzner references with Coolify. Deployment is now:
- Core + Compliance: Push gitea → Coolify auto-deploys
- Lehrer: stays local on Mac Mini
Updated: CLAUDE.md, MkDocs CI/CD pipeline, MkDocs index, environments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddleOCR 3.x removed show_log param and lang='latin'. Try multiple
init strategies in order until one succeeds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Old paddlepaddle==2.6.2 + paddleocr==2.8.1 caused hangs on first OCR
request. Upgrading to paddlepaddle>=3.0.0 + paddleocr>=2.9.0 enables
native PP-OCRv5 support and fixes stability issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The import and model loading can take minutes and was blocking
the startup event, causing health checks to timeout. Now loads
in a background thread — health endpoint returns 200 immediately
with status 'loading' until model is ready.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddleOCR 2.8.1 throws a generic Exception (not ValueError) when
ocr_version='PP-OCRv5' is used. Broadened except clause to catch
any error and fall back to lang='latin' for older versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddleOCR 3.4.0 removed 'latin' language support, causing ValueError
at startup. Now uses lang='en' with ocr_version='PP-OCRv5' and falls
back to lang='latin' for older PaddleOCR versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddlePaddle 3.x hat oneDNN/PIR Executor Bug. Zurueck auf 2.6.2
mit bewaeherter ocr() API statt predict().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaddlePaddle braucht libgomp.so.1 fuer Inferenz.
lang wird ignoriert bei explizitem model_name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Model wird beim Container-Start geladen (nicht erst beim ersten Request).
Health-Check start_period auf 300s erhoeht fuer initialen Download.
/health gibt "loading" zurueck bis Modell bereit ist.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lang="latin" braucht text_recognition_model_name in PP-OCRv5.
Neue API nutzt predict() statt ocr(), Ergebnis-Format angepasst.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Microservice fuer PaddleOCR auf Hetzner. FastAPI mit /ocr und /health
Endpoints, API-Key Auth, 4GB Memory Limit, Modell-Cache Volume.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove backend-core, billing-service, night-scheduler, and admin-core
as they are not used by any compliance/SDK service. Update
health-aggregator CHECK_SERVICES to reference consent-service instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Dockerfile hardcoded TARGETARCH=arm64 for Mac Mini. Coolify server
is x86_64, causing exit code 126 (wrong binary arch). Now uses Docker
BuildKit's auto-detected TARGETARCH with dpkg fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add QDRANT_API_KEY to config.py (empty string = no auth)
- Pass api_key to QdrantClient constructor (None when empty)
- Add QDRANT_API_KEY to coolify compose and env example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add docker-compose.coolify.yml (17 services), .env.coolify.example,
and Gitea Action workflow for Coolify API deployment. Removes nginx,
vault, gitea, woodpecker, mailpit, and dev-only services. Adds Traefik
labels for *.breakpilot.ai domain routing with Let's Encrypt SSL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:43:02 +01:00
712 changed files with 137859 additions and 21956 deletions
Applies to: `pitch-deck/`, `admin-v2/` (Next.js apps in this repo)
---
## NON-NEGOTIABLE: Pre-Push Checklist
**BEFORE every `git push`, run ALL of the following from the Next.js app directory. A single failure blocks the push.**
```bash
# 1. Type check (catches the class of bug that broke ChatFAB.tsx — const inside object)
npx tsc --noEmit
# 2. Lint (ESLint with TypeScript-aware rules)
npm run lint
# 3. Production build (THE most important check — passes lint/types but still fails build)
npm run build
```
**One-liner pre-push gate:**
```bash
npx tsc --noEmit && npm run lint && npm run build
```
> **Why `npm run build` is mandatory:** Next.js performs additional checks during build (server component boundaries, missing env vars referenced in code, RSC/client component violations) that `tsc` and ESLint alone do not catch. The ChatFAB syntax error (`const` inside object literal) is exactly the kind of error caught only by build.
### Why each check matters
| Check | Catches | Time |
|-------|---------|------|
| `tsc --noEmit` | Type errors, wrong prop types, missing members | 5-20s |
**Seit 2026-04-09 liegt die gesamte RAG/Control-Pipeline im Core-Repo** (`control-pipeline/`), NICHT mehr im Compliance-Repo. Alle Arbeiten an der Pipeline (Pass 0a/0b, BatchDedup, Control Generator, Enrichment) finden ausschliesslich hier statt.
- **Port:** 8098
- **Container:** bp-core-control-pipeline
- **DB:** Schreibt ins `compliance`-Schema der shared PostgreSQL
- **Das Compliance-Repo wird NICHT fuer Pipeline-Aenderungen benutzt**
**NEVER push to any remote without first running and confirming ALL checks pass for every changed language stack.**
This rule exists because CI failures break the deploy pipeline for everyone and waste ~5 minutes per failed build. A 60-second local check prevents that.
Blocks on: type errors, lint violations, **build failures**.
> `npm run build` is mandatory — `tsc` passes but `next build` fails more often than you'd expect (server/client boundary violations, env var issues, JSX syntax errors).
---
## What Claude Must Do Before Every Push
1. Identify which services/apps were changed in this task
2. Run the appropriate gate command(s) from the table above
3. If any check fails: fix it, re-run, confirm green
4. Only then run `git push origin main`
**No exceptions.** A push that skips pre-push checks and breaks CI is worse than a delayed push.
---
## CI vs Local Checks
| Stage | Where | What |
|-------|-------|------|
| Pre-push (local) | Claude runs | Lint + type check + unit tests + build |
| CI (Gitea Actions) | Automatic on push | Same + integration tests + contract tests |
| Deploy (Orca) | Automatic after CI | Docker build + health check |
Local checks catch 90% of CI failures in seconds. CI is the safety net, not the first line of defense.
---
## Failures That Were Caused by Skipping Pre-Push Checks
-`ChatFAB.tsx`: `const textLang` inside fetch object literal — caught by `tsc --noEmit` and `npm run build`
-`nodemailer` webpack error: server-only import in client component — caught by `npm run build`
-`jose` Edge Runtime error: full package import — caught by `npm run build`
for svc in bp-core-postgres bp-core-valkey bp-core-qdrant bp-core-ollama bp-core-embedding-service bp-core-rag-service bp-core-backend bp-core-consent-service bp-core-health; do
{type:'library',name:'TextMeshPro',version:'3.2',category:'unity',description:'Advanced Text Rendering',license:'Unity Companion',sourceUrl:'https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2'},
{type:'library',name:'anthropic',version:'latest',category:'python',description:'Anthropic Claude SDK',license:'MIT',sourceUrl:'https://github.com/anthropics/anthropic-sdk-python'},
{type:'library',name:'websockets',version:'14.x',category:'python',description:'WebSocket Support (Voice Streaming)',license:'BSD-3-Clause',sourceUrl:'https://github.com/python-websockets/websockets'},
{type:'service',name:'Go School Service',version:'1.21',category:'application',port:'8084',description:'Klausuren, Noten, Zeugnisse',license:'Proprietary',sourceUrl:'-'},
{type:'service',name:'Gitea',version:'1.21',category:'cicd',port:'3003',description:'Self-hosted Git Service with Actions CI/CD',license:'MIT',sourceUrl:'https://github.com/go-gitea/gitea'},
{type:'service',name:'BQAS Local Scheduler',version:'1.0',category:'qa',port:'-',description:'Lokale GitHub Actions Alternative (launchd)',license:'Proprietary',sourceUrl:'-'},
{type:'service',name:'BQAS Regression Tracker',version:'1.0',category:'qa',port:'-',description:'Score-Historie und Regression-Erkennung',license:'Proprietary',sourceUrl:'-'},
{type:'tool',name:'Gitleaks',version:'latest',category:'security-tool',description:'Secrets Detection in Git',license:'MIT',sourceUrl:'https://github.com/gitleaks/gitleaks'},
`CREATE TABLE IF NOT EXISTS dsr_exception_checks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
request_id UUID NOT NULL REFERENCES data_subject_requests(id) ON DELETE CASCADE,
exception_type VARCHAR(50) NOT NULL,
description TEXT NOT NULL,
applies BOOLEAN,
checked_by UUID REFERENCES users(id),
checked_at TIMESTAMPTZ,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
)`,
// Phase 10 Indexes
`CREATE INDEX IF NOT EXISTS idx_dsr_user ON data_subject_requests(user_id)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_status ON data_subject_requests(status)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_type ON data_subject_requests(request_type)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_deadline ON data_subject_requests(deadline_at)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_assigned ON data_subject_requests(assigned_to)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_request_number ON data_subject_requests(request_number)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_created ON data_subject_requests(created_at)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_status_history_request ON dsr_status_history(request_id)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_communications_request ON dsr_communications(request_id)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_exception_checks_request ON dsr_exception_checks(request_id)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_templates_type ON dsr_templates(template_type)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_template_versions_template ON dsr_template_versions(template_id)`,
`CREATE INDEX IF NOT EXISTS idx_dsr_template_versions_status ON dsr_template_versions(status)`,
// Insert default DSR templates
`INSERT INTO dsr_templates (template_type, name, description, request_types, sort_order)
VALUES
('dsr_receipt_access', 'Eingangsbestätigung Auskunft', 'Bestätigung des Eingangs einer Auskunftsanfrage nach Art. 15 DSGVO', '["access"]', 1),
('dsr_receipt_rectification', 'Eingangsbestätigung Berichtigung', 'Bestätigung des Eingangs einer Berichtigungsanfrage nach Art. 16 DSGVO', '["rectification"]', 2),
('dsr_receipt_erasure', 'Eingangsbestätigung Löschung', 'Bestätigung des Eingangs einer Löschanfrage nach Art. 17 DSGVO', '["erasure"]', 3),
('dsr_receipt_restriction', 'Eingangsbestätigung Einschränkung', 'Bestätigung des Eingangs einer Einschränkungsanfrage nach Art. 18 DSGVO', '["restriction"]', 4),
('dsr_receipt_portability', 'Eingangsbestätigung Datenübertragung', 'Bestätigung des Eingangs einer Datenübertragungsanfrage nach Art. 20 DSGVO', '["portability"]', 5),
('dsr_identity_request', 'Anfrage Identitätsnachweis', 'Aufforderung zur Identitätsverifizierung', '["access","rectification","erasure","restriction","portability"]', 6),
('dsr_processing_started', 'Bearbeitungsbestätigung', 'Bestätigung, dass die Bearbeitung begonnen hat', '["access","rectification","erasure","restriction","portability"]', 7),
('dsr_processing_update', 'Zwischenbericht', 'Zwischenstand zur Bearbeitung', '["access","rectification","erasure","restriction","portability"]', 8),
('dsr_clarification_request', 'Rückfragen', 'Anfrage zur Klärung des Begehrens', '["access","rectification","erasure","restriction","portability"]', 9),
('dsr_completed_access', 'Auskunft erteilt', 'Abschließende Mitteilung mit Datenauskunft', '["access"]', 10),
('dsr_completed_access_negative', 'Negativauskunft', 'Mitteilung dass keine Daten vorhanden sind', '["access"]', 11),
('dsr_completed_rectification', 'Berichtigung durchgeführt', 'Bestätigung der Datenberichtigung', '["rectification"]', 12),
('dsr_completed_erasure', 'Löschung durchgeführt', 'Bestätigung der Datenlöschung', '["erasure"]', 13),
('dsr_completed_restriction', 'Einschränkung aktiviert', 'Bestätigung der Verarbeitungseinschränkung', '["restriction"]', 14),
('dsr_completed_portability', 'Daten bereitgestellt', 'Mitteilung zur Datenübermittlung', '["portability"]', 15),
('dsr_restriction_lifted', 'Einschränkung aufgehoben', 'Vorabbenachrichtigung vor Aufhebung der Einschränkung', '["restriction"]', 16),
// Non-fatal - user can set up 2FA later, but log it
c.JSON(http.StatusCreated,gin.H{
"message":"Registration successful. Please verify your email.",
"user_id":user.ID,
"verification_token":verificationToken,// In production, this would be sent via email
"two_factor_setup":nil,
"two_factor_error":"Failed to initialize 2FA. Please set it up in your account settings.",
})
return
}
c.JSON(http.StatusCreated,gin.H{
"message":"Registration successful. Please verify your email and complete 2FA setup.",
"user_id":user.ID,
"verification_token":verificationToken,// In production, this would be sent via email
"two_factor_setup":map[string]interface{}{
"secret":twoFAResponse.Secret,
"qr_code":twoFAResponse.QRCodeDataURL,
"recovery_codes":twoFAResponse.RecoveryCodes,
"setup_required":true,
"setup_endpoint":"/auth/2fa/verify-setup",
},
})
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.