From 34fdb8928a9f615281bea146713655f291180c4b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 00:08:02 +0900 Subject: [PATCH] feat: seed a TEPP analysis run through tepp_client (v0.84.0) Buyer gap: home Analysis runs only showed lineage reconstruction. make seed now records a Demo Corp TEPP measurement via tepp_client. The default transport is unavailable, so the row is Failed / tepp_not_available -- never a fabricated theta. TEPP stays a wire client, not a local psychometric engine. --- ARCHITECTURE.md | 6 +- CHANGELOG.d/0.84.0-tepp-analysis-run.md | 4 + CHANGELOG.md | 10 ++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 20 ++++ lineageweave/__init__.py | 2 +- pyproject.toml | 2 +- scripts/seed_demo_data.py | 121 ++++++++++++++++++++++++ tests/test_seed_tepp_run.py | 9 ++ uv.lock | 2 +- 10 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.d/0.84.0-tepp-analysis-run.md create mode 100644 tests/test_seed_tepp_run.py diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index bedeba285..362883dfa 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -465,7 +465,11 @@ thread-group scope is visible only when the account can already see a post in that group; `all_visible` is requester-only. Hidden runs 404. Detail also lists ABAC-visible post titles in the run's scope whose `created_at` is at or before `knowledge_cutoff` (ADR 0016) so a buyer can open a post the run was allowed to know -without seeing later live rows or hidden bodies. +without seeing later live rows or hidden bodies. Detail also returns +revision and configuration digest prefixes. +`make seed` also records a TEPP measurement run through +`tepp_client`; the default transport is unavailable, so that run is +Failed / `tepp_not_available` rather than a fabricated score. The home list is clickable: `GET /api/analysis-runs/{id}` fills a labeled detail (cutoff, requested date, counts, status history) without exposing a DSN or raw record. Status history is detail-only diff --git a/CHANGELOG.d/0.84.0-tepp-analysis-run.md b/CHANGELOG.d/0.84.0-tepp-analysis-run.md new file mode 100644 index 000000000..18267d698 --- /dev/null +++ b/CHANGELOG.d/0.84.0-tepp-analysis-run.md @@ -0,0 +1,4 @@ +# 0.84.0 TEPP analysis-run seed + +Seed writes `analysis_run_tepp` via `tepp_client`. Missing transport +is Failed / `tepp_not_available`, not a fake measurement. diff --git a/CHANGELOG.md b/CHANGELOG.md index d0edb5d09..b2a57b24a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.84.0] - 2026-08-16 + +### Added + +- `make seed` records a Demo Corp TEPP measurement run through + `tepp_client`. The default transport is unavailable, so the home + list shows "TEPP measurement · Failed · Demo Corp" with + `tepp_not_available` -- never a fabricated theta. TEPP stays a + wire client, not a local psychometric engine. + ## [0.83.0] - 2026-08-16 ### Fixed diff --git a/frontend/package.json b/frontend/package.json index d1e24268f..c21ed209f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.83.0", + "version": "0.84.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index a3da5de6b..4e5fe7da1 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -236,6 +236,25 @@ describe("App, authenticated", () => { }, ], }, + { + analysis_run_id: "run-demo-tepp", + run_kind_code: "analysis_run_tepp", + run_kind_label: "TEPP measurement", + scope_kind_code: "analysis_scope_corporate_entity", + scope_kind_label: "Corporate entity", + scope_entity_name: "Demo Corp", + status_code: "analysis_status_failed", + status_label: "Failed", + knowledge_cutoff: "2026-01-12T12:00:00Z", + requested_at: "2026-01-12T12:34:00Z", + source_counts: [ + { + count_type_code: "analysis_count_document", + count_type_label: "Documents", + count_value: 3, + }, + ], + }, ], }), ); @@ -1374,6 +1393,7 @@ describe("App, authenticated", () => { expect(await screen.findByRole("heading", { name: "Analysis runs" })).toBeInTheDocument(); const list = screen.getByRole("list", { name: "Analysis runs" }); expect(list).toHaveTextContent("Lineage reconstruction · Succeeded · Demo Corp"); + expect(list).toHaveTextContent("TEPP measurement · Failed · Demo Corp"); expect(list).toHaveTextContent("3 documents"); expect(list).not.toHaveTextContent("postgresql://"); expect(list).not.toHaveTextContent("select "); diff --git a/lineageweave/__init__.py b/lineageweave/__init__.py index 5bd7638d6..e89edfd07 100644 --- a/lineageweave/__init__.py +++ b/lineageweave/__init__.py @@ -55,4 +55,4 @@ "sentence_excerpts", ] -__version__ = "0.83.0" +__version__ = "0.84.0" diff --git a/pyproject.toml b/pyproject.toml index f7b33f6ce..ed229d426 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "0.83.0" +version = "0.84.0" description = "Reconstructs git-branch-style lineage DAGs from scattered short records using multi-channel score fusion and LLM adjudication." readme = "README.md" license = { text = "MIT" } diff --git a/scripts/seed_demo_data.py b/scripts/seed_demo_data.py index cb7c74f87..75a7330f3 100644 --- a/scripts/seed_demo_data.py +++ b/scripts/seed_demo_data.py @@ -333,6 +333,11 @@ def seed( account_ids["demo.analyst"], corporate_entity_id, ) + _seed_demo_tepp_run( + cur, + account_ids["demo.analyst"], + corporate_entity_id, + ) conn.commit() finally: @@ -1298,6 +1303,122 @@ def _seed_demo_analysis_run(cur, requested_by_account_id, corporate_entity_id) - ) +def tepp_seed_outcome() -> tuple[str, str | None]: + """Ask TEPP through the published client. A missing transport is Failed. + + Never invents a psychometric score. ``tepp_not_available`` means the + channel was dropped, not a calibrated negative result. + """ + from lineageweave.tepp_client import AnalysisRunRequest, TeppClient, TeppNotAvailable + + request = AnalysisRunRequest( + idempotency_key="demo-tepp-seed-2026-w02", + tenant_workspace_id="demo-workspace", + snapshot_id="demo-source-contract-v1", + knowledge_cutoff="2026-01-12T12:00:00Z", + model_contract_version="tepp-analysis-run-v1", + output_profile="calibrated_event_measurement", + ) + try: + TeppClient().submit_analysis_run(request) + except TeppNotAvailable: + return "analysis_status_failed", "tepp_not_available" + return "analysis_status_succeeded", None + + +def _seed_demo_tepp_run(cur, requested_by_account_id, corporate_entity_id) -> None: + """Insert one Demo-Corp TEPP run so the kind is visible without a live TEPP. + + Uses :func:`tepp_seed_outcome`. Default transport is unavailable, so + the run ends Failed / ``tepp_not_available`` -- never a fake theta. + """ + import hashlib + + digest = hashlib.sha256(b"lineageweave-synthetic-tepp-snapshot-v1").hexdigest() + cur.execute( + "select analysis_source_snapshot_id from analysis_source_snapshot " + "where snapshot_sha256 = %s", + (digest,), + ) + snapshot_row = cur.fetchone() + if snapshot_row is None: + cur.execute( + """ + insert into analysis_source_snapshot + (snapshot_sha256, source_contract_version, + maximum_available_time, captured_at) + values (%s, 'demo-tepp-contract-v1', + '2026-01-12T00:00:00Z', '2026-01-12T00:05:00Z') + returning analysis_source_snapshot_id + """, + (digest,), + ) + snapshot_id = cur.fetchone()[0] + else: + snapshot_id = snapshot_row[0] + cur.execute( + """ + insert into analysis_source_count + (analysis_source_snapshot_id, count_type_code, count_value) + values (%s, 'analysis_count_document', 3) + on conflict do nothing + """, + (snapshot_id,), + ) + cur.execute( + """ + select analysis_run_id from analysis_run + where requested_by_account_id = %s + and idempotency_key = 'demo-tepp-seed-2026-w02' + """, + (requested_by_account_id,), + ) + run_row = cur.fetchone() + if run_row is None: + cur.execute( + """ + insert into analysis_run + (analysis_source_snapshot_id, run_kind_code, idempotency_key, + requested_by_account_id, knowledge_cutoff, + configuration_schema_version, configuration_sha256, + code_revision_sha, requested_at) + values (%s, 'analysis_run_tepp', 'demo-tepp-seed-2026-w02', + %s, '2026-01-12T12:00:00Z', 'tepp-run-v1', %s, %s, + '2026-01-12T12:34:00Z') + returning analysis_run_id + """, + (snapshot_id, requested_by_account_id, "d" * 64, "e" * 40), + ) + run_id = cur.fetchone()[0] + else: + run_id = run_row[0] + cur.execute( + """ + insert into analysis_run_scope + (analysis_run_id, scope_kind_code, corporate_entity_id) + values (%s, 'analysis_scope_corporate_entity', %s) + on conflict (analysis_run_id) do nothing + """, + (run_id, corporate_entity_id), + ) + final_status, failure_code = tepp_seed_outcome() + events = [ + (1, "analysis_status_pending", "2026-01-12T12:35:00Z", None), + (2, "analysis_status_running", "2026-01-12T12:36:00Z", None), + (3, final_status, "2026-01-12T12:37:00Z", failure_code), + ] + for ordinal, status, occurred, fail in events: + cur.execute( + """ + insert into analysis_run_status_event + (analysis_run_id, status_ordinal, status_code, occurred_at, failure_code) + values (%s, %s, %s, %s, %s) + on conflict do nothing + """, + (run_id, ordinal, status, occurred, fail), + ) + + def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--postgres-dsn", default=DEFAULT_POSTGRES_DSN) diff --git a/tests/test_seed_tepp_run.py b/tests/test_seed_tepp_run.py new file mode 100644 index 000000000..59a22699a --- /dev/null +++ b/tests/test_seed_tepp_run.py @@ -0,0 +1,9 @@ +"""Seeded TEPP analysis runs go through tepp_client, never a local model.""" + +from scripts.seed_demo_data import tepp_seed_outcome + + +def test_tepp_seed_outcome_is_unavailable_not_a_fake_score() -> None: + status, failure = tepp_seed_outcome() + assert status == "analysis_status_failed" + assert failure == "tepp_not_available" diff --git a/uv.lock b/uv.lock index 06408c2a9..411243f56 100644 --- a/uv.lock +++ b/uv.lock @@ -454,7 +454,7 @@ wheels = [ [[package]] name = "lineageweave" -version = "0.83.0" +version = "0.84.0" source = { virtual = "." } dependencies = [ { name = "certifi" },