feat(project-history): consume TEPP topic lineage - #495
Conversation
…ed path Implements the runtime scaffolding for ADR 0132: a new analysis_run_topic_lineage kind (migration 0131) and result envelope table (migration 0132) that request TEPP's TRSL-TM topic identity and CHRONOS/TDT event-intelligence status through the same tepp_client boundary as the existing TEPP measurement kind (ADR 0022), reusing TEPP's generic AnalysisRunRequest wire shape with a different model_contract_version/output_profile instead of inventing a new type. - backend/app/analysis_run_start.py: topic_lineage_run_request, _deliver_topic_lineage_measurement, _persist_topic_lineage_result, start_kind_rejection and the outbox dispatch branch now handle the new kind, failing closed (tepp_not_available / tepp_result_not_persisted) exactly like TEPP. - backend/app/analysis_run_ingestion.py: POST /api/analysis-runs still 422s this kind (Create cannot invent a Pending topic-lineage row, same as TEPP). - scripts/seed_demo_data.py: make seed now also writes a Demo Corp topic-lineage run; verified end-to-end against the live dev Postgres (Pending -> Running -> Failed/tepp_not_available). - frontend/src/api.ts, App.tsx: AnalysisRunKindCode gains the new variant; every exhaustive switch (TypeScript `never` checks) and the start/retry UI copy is updated per kind. - Tests: mirrors the existing TEPP coverage in test_analysis_run_start.py, test_analysis_run_create.py, and test_migration_replay.py. Full backend suite: 852 passed, 17 skipped. Frontend: 200 passed, lint clean, build clean. Migrations verified idempotent on re-apply against the local Compose Postgres. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mk2aU7vk6Pn4pTnzcKT3Ws
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| envelope = row["result_json"] | ||
| try: | ||
| decoded = json.loads(envelope) if isinstance(envelope, str) else envelope | ||
| stored = json.dumps(decoded, separators=(",", ":"), sort_keys=True) | ||
| except (json.JSONDecodeError, TypeError, ValueError): | ||
| continue | ||
| if hashlib.sha256(stored.encode("utf-8")).hexdigest() != row["result_sha256"]: |
There was a problem hiding this comment.
🔍 jsonb round-trip can silently reject valid topic artifacts
The row result_sha256 is computed at persist over json.dumps(..., sort_keys=True) (backend/app/analysis_run_start.py:278-279) and rechecked on read after the envelope round-trips the jsonb column (backend/app/project_history.py:489-495). Postgres normalizes number representations, so an unusual float (e.g. a small/large objective) can re-serialize differently, fail this check, and silently render topic counts unavailable. Integer counts and strengths in (0,1] round-trip fine, and tests feed the string directly rather than real jsonb, so they cannot catch it. The independent artifact-digest check in parse_topic_lineage_envelope is not affected.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52b3413605
ℹ️ 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".
| where run.run_kind_code = 'analysis_run_topic_lineage' | ||
| and status.status_code = 'analysis_status_succeeded' | ||
| and scope.corporate_entity_id::text = any($1::text[]) | ||
| and run.knowledge_cutoff <= $2 |
There was a problem hiding this comment.
Reject artifacts that predate displayed project events
When a project gains matching posts after its most recent TEPP run, this <= predicate still selects the older artifact while the event query includes posts through the newer requested cutoff. The response then marks counts as validated even though they describe only the earlier corpus; require the artifact to cover the displayed event set (or return the unavailable state) rather than silently reporting stale topic results. This also conflicts with ADR 0147's explicit stale-artifact boundary.
AGENTS.md reference: AGENTS.md:L71-L75
Useful? React with 👍 / 👎.
| decoded = json.loads(envelope) if isinstance(envelope, str) else envelope | ||
| stored = json.dumps(decoded, separators=(",", ":"), sort_keys=True) | ||
| except (json.JSONDecodeError, TypeError, ValueError): | ||
| continue | ||
| if hashlib.sha256(stored.encode("utf-8")).hexdigest() != row["result_sha256"]: |
There was a problem hiding this comment.
Hash the representation that is actually persisted
For valid numeric values serialized in exponent form, such as a small association_strength or large-magnitude objective, PostgreSQL jsonb can normalize the numeric representation when storing it. Persistence hashes the pre-cast Python serialization, but this read path reserializes the normalized jsonb value, so the hashes differ and an intact result is discarded as unavailable. Store the envelope as text or derive and verify the checksum from a canonical representation that survives the jsonb round trip.
Useful? React with 👍 / 👎.
| {projectHistoryText( | ||
| locale, | ||
| topicCountsAvailable ? "summaryCounts" : "summaryCountsUnavailable", |
There was a problem hiding this comment.
Label validated counts as fitted non-causal associations
When topicCountsAvailable is true, this branch renders the generic summaryCounts copy, which only says “connected” and “lineage count”; no buyer-facing text renders the artifact's fitted_topic_association_not_causation boundary. Thus validated model output can be read as ordinary or causal lineage, contrary to ADR 0147's requirement that these numbers be labeled as fitted association rather than causation.
AGENTS.md reference: AGENTS.md:L71-L75
Useful? React with 👍 / 👎.
| run.run_kind_code === "analysis_run_topic_lineage") && | ||
| (run.status_code === "analysis_status_pending" || | ||
| run.status_code === "analysis_status_running") |
There was a problem hiding this comment.
Provide a runnable retry path for failed topic runs
For the newly added topic-lineage kind, this condition exposes Start only while the run is Pending or Running, but the only product-created topic run is seeded as Failed whenever the transport is absent, the create endpoint rejects this kind, and the database state machine makes Failed terminal. Consequently, after connecting TEPP as instructed, there is no UI or API operation that can submit the run and persist a successful artifact; provide a new retry run or another legal recovery path rather than pointing users back to this terminal row.
AGENTS.md reference: AGENTS.md:L168-L171
Useful? React with 👍 / 👎.
| ('analysis_run_kind', 'analysis_run_topic_lineage', 'Topic lineage', 3) | ||
| on conflict (lookup_code) do nothing; |
There was a problem hiding this comment.
Fail when the topic code belongs to another lookup category
On an existing installation where the globally unique analysis_run_topic_lineage code already exists under another lookup category, this conflict handler silently keeps that row and the migration proceeds to allow the code in analysis_run. Because the foreign key references only lookup_code, subsequent topic runs then point at a value cataloged under the wrong vocabulary; verify the existing row's category and abort on a mismatch, as the registry migration does for its other globally unique codes.
Useful? React with 👍 / 👎.
52deb37
into
feat/analysis-run-name-evidence-lineage
Summary
tepp.trsl_topic_lineage.v1artifactsmention_confidenceandresponsibility_textschemaStack
f4ebfc684cdd6017255712cc4be37528cbb7d21a)Verification
uv run pytest -q— 1201 passed, 18 skippedpnpm run lintpnpm run test— 253 passedpnpm run buildpnpm run build-storybookNo browser/Playwright inspection was performed because direct browser use requires explicit user permission.