feat(claims): verifiable claim extraction MVP (VeritasAd 2.0, M2) - #6
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 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".
| 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 [], |
There was a problem hiding this comment.
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 👍 / 👎.
| if _SUPERLATIVE_RE.search(t): | ||
| return ClaimType.SUPERLATIVE |
There was a problem hiding this comment.
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 👍 / 👎.
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/claimssubsystem + a new analysis-page section. The existing analysis verdict path is unchanged.What's included
Backend
schemas/claims.py—Claim/ClaimExtractionRequest/ClaimExtractionResult+ enumsClaimType(11),RiskLevel(4),SourceModality(5),ExtractionMethod.claim_extractor(orchestrator),claim_normalizer,claim_classifier,checkworthiness_scorer,claim_fewshot(zero/few-shot prompts),claim_export(JSONL/CSV).llm_service(honoursMOCK_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
CLAIM_EXTRACTION_ENABLED(defaultfalse); the hook is wrapped in try/except and never fails an analysis. Default runtime behavior is identical to before./api/v1/claims/*endpoints work regardless of the flag.claimsfield (aClaimExtractionResultornull).Migration / rollback notes
015_add_analysis_claims— additive, nullableanalyses.claims(JSONB on PostgreSQL / JSON on SQLite); SQLite dev schema auto-synced. Reversible.CLAIM_EXTRACTION_ENABLED=falseto remove extraction from the pipeline (endpoints stay available on-demand);alembic downgrade -1drops theanalyses.claimscolumn.Test evidence
pytest tests/unit tests/integration→ 146 passed (incl. 55 new claim tests for scorer/classifier/normalizer/extractor/export/schema/service); no existing test broken.tsc --noEmit→ 0 errors;next lint→ clean.015.uv.lock/tsconfig.tsbuildinfochurn 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