Skip to content

Implement remaining architecture items: privacy, retry, dispute agent, grounding#13

Merged
strickvl merged 7 commits intomainfrom
feature/enforce-privacy-mode-embeddings
Feb 14, 2026
Merged

Implement remaining architecture items: privacy, retry, dispute agent, grounding#13
strickvl merged 7 commits intomainfrom
feature/enforce-privacy-mode-embeddings

Conversation

@strickvl
Copy link
Copy Markdown
Owner

@strickvl strickvl commented Feb 14, 2026

Summary

Implements the 4 remaining architecture review items from design/remaining-architecture-phases.md (phases 3 and 5), adding verification and safety layers across the entity processing pipeline.

  • Item 9 — Privacy mode enforcement: When --local CLI flag is active, force EMBEDDING_MODE=local so no data leaves the machine, regardless of config.yaml settings. Fail loudly if sentence-transformers is missing.
  • Item 13 — Extraction retry on QC failure: Single retry with repair hint when extraction QC flags zero_entities, high_drop_rate, or many_duplicates. Keeps the better result (v1 vs v2) based on output count and severity.
  • Item 14 — Merge dispute agent: Add confidence field to MatchCheckResult, then route ambiguous cases (gray-band similarity + low confidence) to a second-stage LLM dispute agent that can override the match checker's decision. Optional JSONL review queue for deferred cases.
  • Item 15 — Profile grounding verification: Verify that profile claims are supported by their cited source articles. Citation-anchored claim extraction, per-article LLM batching, SHA-256 hash skip for unchanged profiles, batch post-processing integration.
  • CLAUDE.md: Added pre-push CI-equivalent lint check instructions to prevent ruff version mismatches.

Changes

Commit Item What
e6d500e 9 Enforce local embedding mode with --local flag
b0ea82d 13 Conditional extraction retry on QC failure
7555358 14a Add confidence field to MatchCheckResult
ea4dd86 14b Merge dispute agent for gray-band cases
b24e083 15 Profile grounding verification
ffb7c8a Fix ruff 0.9.6 formatting mismatch
bb2adf9 Add pre-push lint checks to CLAUDE.md

16 files changed, 2082+ insertions. 57 new tests across 4 new test files (153 total, all passing).

Key architectural decisions

  • Privacy enforcement via env var: Sets EMBEDDING_MODE=local early in CLI entry point, leveraging existing override path in EmbeddingManager
  • Retry capped at 1: Diminishing returns beyond single retry, prevents runaway LLM costs
  • Dispute agent fallback to DEFER: Prevents false merges (data corruption) at the cost of possible duplicates (recoverable)
  • Grounding as batch post-processing: More efficient than inline verification — one pass after all merges, grouped by article_id, skips unchanged profiles

Test plan

  • All 153 tests pass locally (pytest tests/ -v)
  • Lint clean (uv run ruff check . + uv run ruff format --check .)
  • CI passes

When a user passes --local, they expect no data leaves their machine.
Previously the embedding provider was configured independently via
config.yaml, so a user could have mode: cloud while running --local,
silently sending profile text to a cloud API.

Now: --local sets EMBEDDING_MODE=local env var before any embedding
manager is created, forcing local-only embeddings. A startup preflight
check fails fast if sentence-transformers isn't installed. A cache
reset helper ensures no stale managers are reused.

Adds 6 new tests covering cache reset, preflight check, and the
env-var-overrides-config integration path.
When deterministic QC flags zero_entities, high_drop_rate, or
many_duplicates, the extraction pipeline now retries once with a
repair hint appended to the system prompt. The better result (by
output count, then fewer severe flags) is kept.

Retry metadata (attempted, trigger flags, output count, whether v2
was used) is recorded in PhaseOutcome.meta for observability.

Changes:
- article_processor.py: retry decision + loop + _run_extraction helper
- extractors.py: accept optional repair_hint parameter
- 14 new tests covering retry triggers, hint passing, and selection
MatchCheckResult now includes confidence: float (0-1, default 0.5) so
the merge pipeline can detect when the match checker is uncertain.
This is the prerequisite for gray-band dispute routing (Item 14).

Changes:
- match_checker.py: add confidence field with backward-compatible default,
  update both cloud/local prompts to request confidence score
- constants.py: add MERGE_GRAY_BAND_DELTA and MERGE_UNCERTAIN_CONFIDENCE_CUTOFF
- Smoke tests: explicit confidence in stubs, schema validation tests
When embedding similarity falls in the "gray band" (within ±0.05 of the
threshold) AND the match checker's confidence is below 0.7, a second-stage
dispute agent now makes a more deliberate merge/skip/defer decision.

New module: src/engine/merge_dispute_agent.py
- MergeDisputeAction enum (merge/skip/defer)
- MergeDisputeDecision Pydantic model with confidence validation
- run_merge_dispute_agent() with cloud/local LLM parity
- Optional JSONL review queue for deferred cases

Integration in mergers.py:
- Gray-band routing after match check, before merge/skip decision
- Dispute agent can override match checker in either direction
- Defer action treated as skip (conservative safety default)

15 new tests covering schema, routing activation/non-activation,
override behavior in both directions, and review queue persistence.
New grounding system verifies that profile claims are supported by
their cited source articles. Uses existing CITATION_RE regex for
deterministic claim extraction, then LLM calls to verify each claim
against the source text.

Models added to quality_controls.py:
- SupportLevel enum (supported/partial/not_supported/unclear/missing_source)
- ClaimVerification for individual claim results
- GroundingReport with scoring, flags, and per-claim details

Key features:
- Claims grouped by article_id for efficient LLM batching
- SHA-256 hash-based skip for unchanged profiles across runs
- Batch post-processing integration in process_and_extract.py
- Never raises — errors become flags in the report
- Cloud/local model parity matching existing LLM patterns

20 new tests covering claim extraction, report schema, missing source
handling, model routing, scoring thresholds, error fallbacks, and
batch post-processing (entity mutation, hash skip, no-citation skip).
The local format.sh was running an older ruff (0.6.9) while CI uses
the lock file version (0.9.6). Reformatted to match CI expectations.
Document that `uv run ruff` must be used before pushing to match CI's
lock file ruff version, avoiding format mismatches between local and CI.
@strickvl strickvl merged commit 4e08b51 into main Feb 14, 2026
2 checks passed
@strickvl strickvl deleted the feature/enforce-privacy-mode-embeddings branch February 14, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant