Skip to content

feat(claims): verifiable claim extraction MVP (VeritasAd 2.0, M2) - #6

Merged
denfry merged 2 commits into
mainfrom
feature-claim-extraction-v2
Jun 18, 2026
Merged

feat(claims): verifiable claim extraction MVP (VeritasAd 2.0, M2)#6
denfry merged 2 commits into
mainfrom
feature-claim-extraction-v2

Conversation

@denfry

@denfry denfry commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Purpose & impact area

Implements VeritasAd 2.0 — Milestone 2 (Claim Extraction MVP) from the master's roadmap: moves the system from ad-signal detection to verifiable advertising claim extraction. From the multimodal analysis signals (ASR transcript, OCR text, metadata, brands, disclosure markers, CTA, links) it extracts individual claims, normalizes them, classifies them by an 11-class taxonomy, scores risk and checkworthiness, and exposes them via API/UI + JSONL/CSV export.

Scope (agreed): M2 full-stack (backend + API + frontend + tests). Dataset collection/annotation (M3), experiments (M4) and thesis text (M5) are out of scope.

Impact area: new isolated app/domains/claims subsystem + a new analysis-page section. The existing analysis verdict path is unchanged.

What's included

Backend

  • schemas/claims.pyClaim / ClaimExtractionRequest / ClaimExtractionResult + enums ClaimType (11), RiskLevel (4), SourceModality (5), ExtractionMethod.
  • Services: claim_extractor (orchestrator), claim_normalizer, claim_classifier, checkworthiness_scorer, claim_fewshot (zero/few-shot prompts), claim_export (JSONL/CSV).
  • Three baselines: rule-based (offline default), LLM zero-shot, LLM few-shot via the unified llm_service (honours MOCK_LLM_RESPONSES, falls back to rule-based).
  • app/domains/claims + endpoints: POST /api/v1/claims/extract, POST /api/v1/claims/from-analysis/{task_id}, GET /api/v1/claims/{task_id}, GET /api/v1/claims/{task_id}/export?format=jsonl|csv.

Frontend (Next.js 15, ru/en)

  • ClaimsTable, ClaimDetailsCard, ClaimRiskBadge, ClaimTimeline, a claims section on the analysis page, api-client functions, TS types, i18n strings.

Docs: changelog, decision-log, M2 plan (docs/research/plans/master/), prompt artifact (docs/research/prompts/master/claim-extraction.md).

Compatibility / behavior preservation

  • Pipeline extraction is gated by CLAIM_EXTRACTION_ENABLED (default false); the hook is wrapped in try/except and never fails an analysis. Default runtime behavior is identical to before.
  • The on-demand /api/v1/claims/* endpoints work regardless of the flag.
  • No forced LLM calls (rule-based is the offline default, no API keys needed).
  • Analysis serialization gains an optional claims field (a ClaimExtractionResult or null).

Migration / rollback notes

  • Migration: 015_add_analysis_claims — additive, nullable analyses.claims (JSONB on PostgreSQL / JSON on SQLite); SQLite dev schema auto-synced. Reversible.
  • Rollback: set CLAIM_EXTRACTION_ENABLED=false to remove extraction from the pipeline (endpoints stay available on-demand); alembic downgrade -1 drops the analyses.claims column.

Test evidence

  • Backend: pytest tests/unit tests/integration146 passed (incl. 55 new claim tests for scorer/classifier/normalizer/extractor/export/schema/service); no existing test broken.
  • Frontend: tsc --noEmit → 0 errors; next lint → clean.
  • Alembic: single head 015.
  • New files are ruff-clean (real issues); pre-existing repo-wide lint/format debt was intentionally not touched. Unrelated uv.lock / tsconfig.tsbuildinfo churn reverted.

Documentation delta

  • docs/changelog.md, docs/decision-log.md (decision + rationale + rollback).
  • docs/research/plans/master/2026-06-17-m2-claim-extraction-implementation.md (executable brief + data contract).
  • docs/research/prompts/master/claim-extraction.md (zero/few-shot prompt artifact).

🤖 Generated with Claude Code

denfry and others added 2 commits June 17, 2026 20:38
…roadmaps

Consolidate scattered research artifacts into docs/research/, organized by
artifact type (roadmaps/prompts/plans/specs/reports/experiments/datasets/
literature), each split into master/ (2.0) and phd/ (3.0) phases.

- git mv (history preserved): M2 spec, 3 shared system prompts, related-work
  review; move both roadmaps into roadmaps/; delete empty docs/superpowers/
- fix self-references in the M2 spec to the new paths
- add hub docs/research/README.md (conventions, frontmatter, status lifecycle,
  milestone map, path-correspondence table) and 8 section READMEs
- add 8 artifact templates in _templates/
- add dataset schemas + annotation guidelines (claims=master, verification=phd)
- scaffold data/datasets/ with .gitignore (raw/large ignored, docs/jsonl kept)
- add Research Workspace section to AGENTS.md; record decision in decision-log
- keep GEMINI.md in repo root (Gemini CLI context file)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the claim-extraction subsystem: extract verifiable advertising claims
from the multimodal analysis signals (ASR/OCR/metadata/brands/disclosure/CTA/
links), normalize, classify (11-class taxonomy), risk- and checkworthiness-
score them, and export to JSONL/CSV (dataset schema rows).

Backend: schemas/claims.py; services claim_extractor/normalizer/classifier/
checkworthiness_scorer/fewshot/export; app/domains/claims with on-demand
endpoints (/api/v1/claims/{extract,from-analysis/{id},{id},{id}/export});
additive reversible migration 015 for analyses.claims; pipeline hook behind
CLAIM_EXTRACTION_ENABLED (off by default, preserves behavior). rule_based is
the offline default; optional LLM zero/few-shot via the unified llm_service.

Frontend: ClaimsTable/ClaimDetailsCard/ClaimRiskBadge/ClaimTimeline, a claims
section on the analysis page, api-client functions, TS types and ru/en i18n.

Tests: 55 unit tests for the services and schema (backend suite: 146 passed).
Docs: changelog, decision-log, M2 plan and prompt artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
veritas-ad Ready Ready Preview, Comment Jun 17, 2026 7:46pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb9dec5787

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +55 to +60
return ClaimExtractionRequest(
transcript=analysis.transcript or "",
detected_brands=analysis.detected_brands or [],
disclosure_markers=analysis.disclosure_markers or [],
cta_matches=getattr(analysis, "cta_matches", None) or [],
commercial_urls=getattr(analysis, "commercial_urls", None) or [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve ASR timestamps in on-demand claim extraction

In the default flow (CLAIM_EXTRACTION_ENABLED=false), the frontend calls /claims/from-analysis/{task_id} after a video completes, but this request only passes the flattened transcript. ClaimExtractor._candidate_fragments only assigns ASR timestamps when transcript_segments are present, so every claim extracted on demand comes back untimed and the new ClaimTimeline can never render markers even though Whisper segments existed during analysis. Persist/pass the timed segments (or another timed source) before exposing timeline extraction.

Useful? React with 👍 / 👎.

Comment on lines +92 to +93
if _SUPERLATIVE_RE.search(t):
return ClaimType.SUPERLATIVE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Classify Russian comparatives before superlatives

When the fragment is a Russian comparative such as лучше, чем у конкурентов, _SUPERLATIVE_RE matches the broad лучш\w* stem here and returns SUPERLATIVE before _COMPARATIVE_RE can match its explicit лучше,?\s+чем pattern. This mislabels common comparative ad claims in the rule-based extractor; check the comparative pattern first or narrow the superlative stem so these cases reach the comparative branch.

Useful? React with 👍 / 👎.

@denfry
denfry merged commit 8370719 into main Jun 18, 2026
5 checks passed
@denfry
denfry deleted the feature-claim-extraction-v2 branch June 18, 2026 05:07
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