Skip to content

feat(project-history): consume TEPP topic lineage - #495

Merged
seonghobae merged 5 commits into
feat/analysis-run-name-evidence-lineagefrom
feat/tepp-topic-project-history
Aug 23, 2026
Merged

feat(project-history): consume TEPP topic lineage#495
seonghobae merged 5 commits into
feat/analysis-run-name-evidence-lineagefrom
feat/tepp-topic-project-history

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • consume only digest-, snapshot-, cutoff-, and schema-bound tepp.trsl_topic_lineage.v1 artifacts
  • recompute topic counts after filtering predecessor/successor edges to authorized Project History posts
  • keep the shared post/Ask timeline and evidence-DAG navigation, with an explicit unavailable state instead of fallback counts
  • repair the canonical Project History SQL for the current mention_confidence and responsibility_text schema

Stack

Verification

  • uv run pytest -q — 1201 passed, 18 skipped
  • strict artifact consumer — 169 statements / 64 branches, 100%
  • PostgreSQL Project History projection — 101 statements / 18 branches, 100%
  • pnpm run lint
  • pnpm run test — 253 passed
  • pnpm run build
  • pnpm run build-storybook
  • real PostgreSQL/Keycloak/Valkey Project History API integration path passed

No browser/Playwright inspection was performed because direct browser use requires explicit user permission.

seonghobae and others added 5 commits August 23, 2026 22:39
…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
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 223a9812-9ac9-4df3-a23b-0ff08afd448c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Open in Devin Review

Comment on lines +489 to +495
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"]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@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: 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment on lines +491 to +495
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"]:

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 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 👍 / 👎.

Comment on lines +108 to +110
{projectHistoryText(
locale,
topicCountsAvailable ? "summaryCounts" : "summaryCountsUnavailable",

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 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 👍 / 👎.

Comment thread frontend/src/App.tsx
Comment on lines +2785 to 2787
run.run_kind_code === "analysis_run_topic_lineage") &&
(run.status_code === "analysis_status_pending" ||
run.status_code === "analysis_status_running")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment on lines +15 to +16
('analysis_run_kind', 'analysis_run_topic_lineage', 'Topic lineage', 3)
on conflict (lookup_code) do nothing;

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 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 👍 / 👎.

@seonghobae
seonghobae merged commit 52deb37 into feat/analysis-run-name-evidence-lineage Aug 23, 2026
4 checks passed
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