Surfaces fired patterns whose zone names terms the machine's narrative never
mentions — foreign framing that leaks through terms not yet in domainGateTerms
(once a term is a gate term, the ghost-pattern invariant already fences it out).
- FindFramingCandidates (proposer_framing.go): per fired pattern, zone terms with
no narrative echo (minus a generic hazard-location stoplist). Echo matching is
bidirectional to survive German compounding (narrative "Steuerung" echoes zone
"Steuerungssystem"). Heuristic verdict foreign (fully orphan) / plausible
(partial). Over-surfaces by design — human/LLM is the precision filter.
- Wired into iace-audit propose -> audit-reports/framing.{md,json}, threshold via
IACE_FRAMING_MIN_ORPHAN (default 0.6).
Honest finding: genuine wrong-MACHINE framing (Walzen, Transportbaender) no longer
fires thanks to the machine-type gate; the residual is mostly cyber/control
patterns with generic-industrial zone vocabulary, candidates for re-framing.
Proposal types 3-4 (vocab->tag, coverage blind spots) remain for slice 5.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Makes the offline proposer runnable end-to-end.
- BuildProposerInput (proposer_input.go): non-test engine->hazards path. The
PatternMatch->Hazard converter is lifted out of the GT test files into
production scope so both the tests and the CLI share one pipeline.
- iace-audit propose <narrative.json> [<ground-truth.json>]: detect candidates ->
GT-screen survivors (when a ground truth is given) -> judge (HeuristicJudge by
default, LLMJudge over ollama when IACE_PROPOSE_LLM=1) -> write the human-review
queue to audit-reports/proposals.{md,json}. Propose-only.
Smoke run on a dishwasher narrative: 32 fired -> 3 candidates -> queue with a
confident duplicate, a confident distinct, and one punted to the LLM judge; GT
wall recall-safe. Live qwen is opt-in via env; the heuristic default keeps the
tool runnable (and CI deterministic) without a model. Proposal types 2-4
(foreign-framing gates, vocab->tag, coverage blind spots) remain for slice 4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the semantic judgement layer on top of the slice-1 detector + GT wall.
DEV-TIME, propose-only — nothing mutates the library or runtime.
- CandidateJudge interface with two implementations: HeuristicJudge
(deterministic default/fallback, used in tests) and LLMJudge (offline, over the
shared llm.ProviderRegistry via the LLMCompleter adapter). LLMJudge degrades to
"uncertain" on any transport/parse error — it can never break a run.
- BuildJudgePrompt: the ISO 12100 same-vs-distinct prompt, unit-tested
deterministically even though the call is not.
- RenderProposalQueue: markdown human-review queue with a suggested action per
candidate (supersede / keep both / needs review).
On real warewashing output the heuristic punts to "uncertain — needs the LLM
judge" for exactly the two recall-safe near-dupes (HP807/HP033 update,
HP101/HP096 winding-vs-friction), making the LLM's role explicit. All 3 GTs
unaffected (read-only). Live qwen wiring + a CLI/file queue are slice 3.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First thin slice of the offline library-improvement proposer. DEV-TIME ONLY,
propose-only — it never mutates the pattern library or the runtime.
- FindDedupCandidates (proposer_dedup.go): structural near-duplicate detection
over the fired patterns (category + measure/zone/scenario overlap). Bakes in
the P1 lesson: only same-category pairs compare, and pairs with different
operational states are never proposed (normal-operation vs maintenance are
legitimately distinct, e.g. HP011 vs HP077).
- ScreenSupersession (proposer_screen.go): the wall. A proposal is safe only if
(1) dropping the hazard does not reduce GT recall AND (2) keep/drop do not
credit DIFFERENT GT entries. Check 2 catches distinct hazards that merely share
measures (HP2201 hot surface GT 1.3 vs HP2202 hot ware GT 1.4) which recall
alone would wave through.
On real warewashing output: 3 candidates -> 1 BLOCKED (distinct GT), 2
RECALL-SAFE for human/LLM review (the update + winding/friction near-dupes).
Nothing auto-applied. All 3 GTs unaffected (read-only). The LLM judgement and a
CLI/file queue are slice 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HP013 (stored electrical energy) fires for dishwashers via the broad stored_energy
tag but its zone is framed for Batteriefaecher/USV-Anlagen, which a dishwasher does
not have. The precise residual-voltage pattern HP144 (Frequenzumrichter/Zwischenkreis,
Priority 90) already fires and covers the same hazard. Add HP013 to the
warewashing-scoped supersession set so the duplicate is dropped only when
dom_warewashing is present.
Warewashing recall stays 100% (25/25), precision 92.6% -> 96.2%. Kistenhub/Bremse
keep HP013 (no dom_warewashing); 26 Bremse pins + benchmark unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The generic hot-surface patterns HP016 (high_temperature) and HP018 (actuator
burn) fire for dishwashers via broad tags and duplicate the precise warewashing
pattern HP2201 (Boiler/Tank/Spuelkammer). Suppress HP016/HP018 only when
dom_warewashing is present, so the specific pattern wins and the duplicate is
dropped. Scoped to the domain tag -> Kistenhub/Bremse and every non-warewashing
machine keep the generic patterns unchanged.
Warewashing recall stays 100% (25/25), precision 90% -> 92.6% (2 dupes removed).
Bremse 26 pins and Kistenhub benchmark unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ListHazards returned hazards in pattern-firing order, which reads as a jumble.
Sort by EN ISO 12100 hazard group (A. Mechanisch, B. Elektrisch, C. Thermisch,
D. Pneumatik/Hydraulik, E. Laerm, F. Ergonomie, G. Stoffe, H. Software/Steuerung,
I. Cyber, J. KI), stable within a group. Matches the frontend CATEGORY_LABELS.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HP1717 was gated on the generic stored_energy tag (carried by a frequency
converter's DC link) + pneumatic_pressure (emitted by "Boiler unter Druck"),
so it leaked into the dishwasher despite the absence of any pneumatics. Require
pneumatic_part instead. The Bremse pin is a static pattern->measure check
(unaffected); full suite incl. Bremse coverage and Kistenhub 97.1% unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HP753 (lithium thermal runaway), HP754 (battery off-gassing) and HP755 (HV
battery shock) were gated on stored_energy, which a frequency converter (C034,
DC-link capacitors) legitimately carries — so they leaked into any machine with
a VFD (surfaced by the dishwasher after the Frequenzumrichter narrative). Now
require the "battery" tag; add lithium/batteriespeicher synonyms so real
battery-storage machines still emit it.
GT #3 100% recall unchanged, battery themes gone from the dishwasher log;
Kistenhub 97.1% and Bremse pinned mappings unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
evidence_required lists only required:true rows; repo_scan was required:false so
attack_surface_minimization surfaced config_export alone. An attack-surface scan
IS required to evidence a minimized attack surface. Adds a test pinning the curated
evidence_required set per NIST obligation (the table test only checked control count).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Registry materialized the generic CORE security objectives (#5b, Modell C), so
the two broad NIST controls now point at their canonical parents instead of the
domain-scoped matches:
SI-7 -> software_integrity_protection (CORE, Annex I (2)(f))
CM-7 -> attack_surface_minimization (CORE, Annex I (2)(j))
Non-breaking: the domain-scoped obligations stay valid and specialize the CORE.
SI-7 evidence = sbom + config_export (SBOM evidences component/supply-chain
integrity; config = signing/secure-boot). Export proposed_obligation_id + handler
test (2 CORE cases) updated. go test green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Registry grew to 95 (Capability materialization #5b added CORE obligations).
Keep the ai-sdk build-context copy current so obligation-status reflects the
live registry contract.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Prior gitea push's build-ai-sdk failed on a transient registry push (arm64 built
clean on macmini; amd64 cross-compile is green) and last-build/main got poisoned
to that SHA, so a plain re-run scopes to nothing. A real touch in ai-compliance-sdk/
re-scopes the build. Also documents the synced-copy contract for
data/obligations/obligation_join_keys.json.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Aligns provide_security_updates -> SI-2 evidence to the curated acceptance set:
config_export (secure-update mechanism config) + test_report (patch verification).
For "provide updates" the patch-verification test is more on-point than a vuln
scan; repo_scan stays on CM-7 for attack-surface.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Registry filled proposed_obligation_id for the 3 NIST primary_implementation
controls: SI-7->signed_update_integrity, SI-2->provide_security_updates,
CM-7->remote_access_attack_surface_min. Adopted onto cra_nist.jsonl so the join
is now EXACT (obligation_id) instead of the coarse citation_unit fallback.
obligation-status now surfaces SI-2 under provide_security_updates; test extended.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Vertical slice over the Compliance Execution Graph: obligation_id -> accepted
controls -> required evidence -> status. NEVER auto-asserts fulfillment - with
no evidence collection wired (MVP), a mapped obligation is "not_assessed" and
every required evidence is "missing". Fail-closed: no id -> 400; unknown id ->
unknown_obligation; mapped-but-no-control -> unmapped; graph not loaded -> 503.
- ComplianceGraphHandlers (separate from the DB-backed ObligationsHandlers):
loads Registry join keys + accepted control mappings + evidence once at start.
- LoadComplianceGraph: candidate-path resolution across dev/container/test.
- Data plumbing: Dockerfile now COPYs data/{control_mappings,evidence_requirements,
obligations}; data/obligations/obligation_join_keys.json is a SYNCED COPY of the
repo-root Registry contract (re-sync on Registry growth).
- Table-driven handler test (mapped/unmapped/unknown/400 + no-fulfillment-claim).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CRA Annex I Part I (2)(e)/(2)(l)/(2)(i) had no clean OWASP target (rejected:
"Mapping ueber NIST/BSI erforderlich"). Their NIST home, curated + accepted:
(2)(e) Integritaet -> SI-7 (Software/Firmware/Information Integrity)
(2)(l) Sichere Updates -> SI-2 (Flaw Remediation)
(2)(i) Angriffsflaeche -> CM-7 (Least Functionality)
New mapping_type=primary_implementation = the single canonical control per
obligation (stronger than implements/supports); related controls (SC-3(3),
RA-5, AC-6, SI-16, ...) follow later as supports.
Evidence is framework-AGNOSTIC: SI-7/SI-2/CM-7 reuse the shared evidence_type
catalog (config_export/test_report/repo_scan) - same types carry CRA, NIST,
ISO 27001, IEC 62443, BSI. (framework,control) is only the link, not the type.
obligation_id left empty: the Obligation Registry assigns it (exported via
controls_for_obligation_mapping.json), then we adopt. go test ./internal/ucca green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Obligation Registry filled proposed_obligation_id (7/7) + cut the logging
family (obligations 47->66). Adopted obligation_id onto our 7 accepted CRA->OWASP
mappings; the join now prefers the EXACT obligation_id over the coarse
citation_unit (which stays as fallback for not-yet-adopted rows).
Effect: semantic coverage 2->4 (user_authentication_required,
credential_confidentiality_protection, auth_key_management,
event_logging_security_events). Befund 1 resolved: V11.2.1 crypto now sits under
credential_confidentiality_protection, not user_authentication_required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
AssessObligationStatus traverses obligation_id -> (citation_unit) -> accepted
controls -> required evidence -> status (erfuellt|offen|unklar). Evidence
presence is a callback; MVP passes nil (nothing collected yet) -> offen.
citation_spans = "pending" until the Legal-Knowledge-Graph session attaches
them. This is the vertical slice that makes the graph a product feature:
"CRA obligation fulfilled because evidence X/Y/Z is present", not "a doc exists".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Consumes the cross-session contract obligations/obligation_join_keys.json (47
obligation_ids). Interim bridge = citation_unit (our source_norm <-> registry
citation_units), to be hardened to the stable obligation_id (field now optional
on ControlMapping).
ComputeObligationCoverage joins the 47 registry obligations to our accepted
control mappings: covered=2 (user_authentication_required, firmware_software_
authentication), mapped_rejected=3 ((2)(e) -> our OWASP mappings rejected,
route via NIST/BSI), uncovered=42. This coverage signal is the feedback to the
Obligation session for what to cut/refine next.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The last edge of the compliance graph: what concrete, fresh evidence proves a
framework control is met (config_export/test_report/sbom/audit_log/pentest/...
from github/ci/scanner/manual_upload, with a freshness requirement).
Seeded for all 7 accepted CRA->OWASP controls (Auth/Crypto/Logging). A graph
test enforces connectivity: every accepted control must carry >=1 required
evidence — no dangling node in Obligation -> Control -> Evidence.
This is what will let the Advisor state "the CRA requirement is fulfilled" from
present evidence, not from the mere existence of a document.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7 accepted, 13 rejected (reviewed_by=benjamin, 2026-06-25). The accepted set is
the first audited ground truth of the compliance graph:
(2c) Zugriff -> V6.3.1, V6.1.1 (Auth)
(2d) Crypto -> V11.2.1, V11.7.1 (corrected from the retriever's wrong V14)
(2k) Logging -> V16.3.3, V16.3.4, V16.1.1
Rejected stay as audit trail. (2e) integrity, (2l) updates, (2i) attack surface
rejected with reason "OWASP ASVS not the right target standard, map via NIST/BSI"
— architectural proof for the multi-framework framework_* layer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- DROP confidence from the persisted mapping: a curated mapping is a
professional statement, not an AI guess (retriever score -> rationale only).
- ADD mapping_status (candidate|accepted|rejected|superseded) — the review state.
- ADD audit trail (reviewed_by/review_date/review_reason); accepted/rejected
fail-closed without it.
- EXTEND mapping_type: + implements, + contradicts.
- Advisor truth = mapping_status=accepted (acceptedOnly filter).
- migrate the 18 CRA->OWASP rows to mapping_status=candidate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
18 retriever_candidate mappings generated via the sdk-dev control-intent
retriever. All marked retriever_candidate (NOT curated truth) — the review
step turns the good ones into human_curated.
Empirical validation of the A-decision: the retriever proposes, but produces
wrong candidates (e.g. encryption -> V14 Config instead of V11 Crypto;
V14.2.4 over-appears) that only human review catches. Review notes inline.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Versioned JSONL store + Go model for Regulation->Control mappings, per the
A-decision: the retriever only PROPOSES candidates; the curated mapping is the
audited truth the Advisor uses at runtime, never re-invented per query.
- ControlMapping struct (source_norm/source_role/target_framework/target_control/
mapping_type/confidence/provenance/rationale/version)
- enum validation (rule layer), fail-closed loader, forward+reverse index,
curated-only filter (IsCurated)
- seed: 2 retriever_candidate rows CRA Annex I -> OWASP ASVS (not yet curated)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
On an implementation question impl_guidance (ENISA) keeps its earned semantic
Top-1, but the top-K now surfaces the best operational_requirement and
control_standard from the pool (ensureControlDiversity) — so different source
roles are visible instead of one role flooding the list, without forcing the
binding sources to Top-1.
A recognised standard NAME (NIST/OWASP/ISO 27001/CIS/CSA CCM/Grundschutz) now
overrides a mis-applied supervisory_guidance source_class in classifyAuthority,
so those standards classify and rank as technical_standard (control_standard
role). The corpus tags many standards as guidance (weight 70); the name wins.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1 complete. GT #3 recall 84% -> 100% (25/25 matched), no regression:
- HP2207 backflow / potable-water contamination (EN 1717) + measure M2209
(Rueckflussverhinderer / Systemtrenner) — the only genuinely new hazard.
- HP2208 cut on sharp edges/screens (new sharp_edge tag from scharfe-Kante/Sieb).
- HP2209 unexpected restart during maintenance (dedicated dom_warewashing pattern;
avoids flooding the log via the broad moving_part tag).
- Spray-arm contact now covered by the enclosure-re-scoped contact patterns.
Kistenhub 97.1% and Bremse pinned mappings unchanged; 0/28 hazards without a
measure. Completes the commercial-dishwasher (white-goods Phase 1) coverage.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1 of the commercial white-goods expansion (EN ISO 10472 family). Extend
GT #3 with 8 completeness hazards a Fachmann expects but that were neither in
the GT nor previously questioned: dry-run boiler overheating, residual/stored
electrical energy, sharp-edge cut, tipping, interlock-failure, unexpected
restart, backflow (EN 1717), microbial/legionella. Enrich the UC-M narrative
with the real features so existing library patterns can fire.
Result: 4/8 auto-covered by existing patterns (dry-run, residual voltage,
tipping, interlock-failure) — recall 84% (21/25). Remaining gaps documented:
spray-arm contact (4.3), sharp-edge cut (4.6), backflow (2.3), restart (6.4).
Gate the re-surfaced CNC leak ("spanende Bearbeitung", high_temperature-only)
via dom_cnc. Kistenhub 97.1% and Bremse pinned mappings unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Class E1. A multi-category pattern (e.g. "Motorueberlast" [electrical, thermal],
"Lagerschaden" [mechanical, thermal]) created one hazard per category, so the
same scenario+zone appeared twice in the CE hazard log under different labels.
InitializeProject now breaks after the primary (first eligible) category — one
hazard per pattern.
This aligns production with the GT benchmark, which already scores one hazard per
matched pattern. Cyber-skip, per-category cap and cross-pattern measure-merge
still use continue (unchanged). Handlers + iace suites green; Kistenhub/Bremse
unchanged.
Note (E2, not fixed): some scenarios exist as TWO separate patterns (e.g.
"Sicherheitssoftware manipuliert" in hazard_patterns_final_c.go and _final_d.go)
— library redundancy that E1's per-pattern break cannot merge. Left for a
separate, GT-guarded library-dedup audit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Class D (generic keyword hygiene, GT-guarded). Two over-broad keyword->component
mappings produced ghost components:
- "kuehl"/"cool" -> Kuehlaggregat (C095) matched product variants
("Cool-Ausfuehrung") and outputs ("kuehle Glaeser"). Narrowed to cooling-UNIT
terms (kuehlaggregat, kuehlanlage, kuehler, kaeltemaschine, chiller, rueckkuehl).
- "filter" -> Absauganlage/Oelnebelabscheider (C124) matched any filter
(Laugen-/Wasser-/Oelfilter). Keep "filteranlage" only.
No pattern or GT test depends on these mappings (Kistenhub/Bremse use hand-crafted
inputs). UC-M now parses 6 plausible components (was 8 incl. the two ghosts).
Warewashing GT recall 82.4% and Kistenhub/Bremse pins unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Class C (phase-aware, generic EN ISO 14120). A contact/entanglement hazard from
a moving part is removed during NORMAL operation when the part is behind an
interlocked guard; it remains only when the guard is open (maintenance/cleaning).
- New HazardPattern.GuardableByEnclosure flag; set on HP096 (friction at
rotating surfaces) and HP101 (entanglement of hair/clothing).
- Narrative emits interlocked_enclosure for an interlocked door/hood.
- pattern_enclosure.go: suppressedByEnclosure (drop in normal-op-only contexts)
+ guardedLifecycles (re-scope to maintenance/cleaning).
- GT #3 gains the maintenance-phase entanglement/friction rows.
Generic + regression-safe: machines that do not emit interlocked_enclosure are
unaffected. GT #3 recall 80% -> 82.4%, one false positive removed (Aufwickeln).
Kistenhub 97.1% and all 26 Bremse pinned mappings unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add ground_truth_warewashing.json + TestWarewashing_GTCoverage. The test runs
the UC-M narrative through the SAME chain as production (ParseNarrative ->
engine -> relevance + cyber filter), so keyword/gating fixes are measured on
the real hazard set, and false positives show up as "extra".
Class A (generic keyword hygiene): spuelarm/spuelfeld no longer map to library
component C004 ("Drehtisch" / rotary table) — that mislabelled the spray arm.
Keep the rotating_part tag. Removes the bogus "Drehtisch" hazard.
GT #3 baseline -> after Class A: recall 80% (unchanged), one false positive
(Drehtisch) removed. Kistenhub 97.1% and Bremse pinned mappings unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
InitializeProject created hazards for every matched pattern, so native
cybersecurity/AI topics (unauthorized access, firmware manipulation, missing
SBOM, ...) mixed into the ISO 12100 hazard log. Route the security categories
(frontend groups I. Cyber/Netzwerk + J. KI) to the CRA module instead —
generically for EVERY project, enforced centrally in InitializeProject.
The split is by the nature of the hazard, not the component: functional-safety
control faults stay in CE (software faults, lost safety functions, config
errors, bus failures, botched updates) — they are random/systematic faults,
not attacks, and feed the CRA safety-function bridge. This holds whether the
controller is a bought-in CE-marked PLC or the manufacturer's own control.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add commercial-dishwasher hazard patterns (HP2200-HP2206): hot-water/steam
scald on door opening, hot surfaces, hot ware, corrosive detergent/rinse-aid
burn, respiratory irritation, door pinch and wet-floor slip — each gated by
dom_warewashing so they never leak into other machine classes. Add the
matching warewashing protective measures (M2200-M2208).
Tighten capability-domain gating: emit dom_flame/dom_glue and add welding
surface-form gate terms (schweissarbeitsplatz, schweissfunke, lichtbogenzone,
...) so the welding/flame/glue burn patterns stop leaking into thermal-capable
machines such as a dishwasher.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
test-go failte seit 2026-06-19: VBR-OBL-001 ("Widerrufsbutton ab 19.06.2026") ist
seit dem Stichtag abgelaufen und faellt aus dem Zukunfts-Horizont von GetRegulatoryNews,
wodurch TestGetRegulatoryNews_FromRealFiles bricht. Fix: now-Referenz injizierbar
(GetRegulatoryNewsAt), Test nutzt fixes Datum -> deterministisch. Produktions-Caller
unveraendert (Wrapper). admin rag-query Marker, damit detect-changes admin mitbaut
(article_label-Rendering). go vet + alle ai-sdk-Tests lokal gruen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Triggert CI + detect-changes fuer ai-compliance-sdk + admin-compliance, nachdem der
vorige Deploy am last-build/main Tag-Bug haengenblieb (Builds uebersprungen). Nur
Doku-Kommentare, Logik unveraendert. Daten-Merge (Qdrant) ist bereits live.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ai-sdk (legal_rag_client/scroll/types) liest die gepinnten Spec-Felder
article_label/regulation_code/article/paragraph/sub/citation_style/is_recital
mit Fallback auf alt-ingestierte Chunks (regulation_id, section); neuer getBool-Helfer.
Advisor + Drafting-Engine bilden die Quellenzeile primaer aus article_label
("BDSG § 38 Abs. 1"), sonst aus den strukturierten Feldern. 17 Tests gruen, tsc sauber.
Vertrag: docs-src/development/rag_reingest_spec.md (§2/§7). Deploy an den Re-Ingest
gekoppelt — neue Felder sind bis dahin leer (graceful Fallback).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Der Floating-Compliance-Advisor war auf prod kaputt (502): RAG ging ueber
rag-service:8097 (auf prod nicht vorhanden) und der Chat ueber
OLLAMA_URL=ollama-embed (embedding-only, kein qwen2.5vl).
- RAG laeuft jetzt ueber die ai-compliance-sdk /sdk/v1/rag/search (bge-m3,
prod-erreichbar) statt rag-service -> profitiert vom reicheren Embedding.
(lib/sdk/agents/advisor-rag.ts)
- LLM-Kaskade: OVH/LiteLLM (gpt-oss-120b) zuerst, Ollama als Dev-Fallback.
(lib/sdk/agents/advisor-llm.ts; OVH-Env via orca-infra admin-Block)
- ai-sdk: bp_compliance_recht in AllowedCollections ergaenzt (Whitelist war
inkonsistent — die Fehlermeldung listete es bereits als erlaubt).
- Route auf die Module umgestellt (duenn); Controls-Augmentation unveraendert.
- Tests: advisor-rag + advisor-llm.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cumulative resume run added 64 new NASA NTRS docs (query/page pool then
exhausted): 164 total, 73 applicable failures, all public-use licensed.
NASA stays a generic component failure-mode library; not scaled further.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Load the prior manifest, seed the seen-set from it + anthropic.jsonl, and add
MAX_DOCS NEW docs per run (100->200->...) instead of re-processing the first
batch and overwriting the register. Widen NTRS paging to page.from 0..400 so
enough fresh usable docs are found after the skip.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>