Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.d/2.20.0-tepp-terminal-result-consumer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 2.20.0 — TEPP terminal result consumer

- Reads TEPP's versioned status/result contract from a stored accepted receipt
without resubmitting the measurement request.
- Revalidates every request binding and persists only digest-bound terminal
measurement evidence; accepted/running states remain measurement-free.
- Rejects changed result digests and maps a validated provider terminal failure
to a typed local failure without inventing a score or theta.
57 changes: 52 additions & 5 deletions backend/app/analysis_run_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@
from lineageweave.http_client import HttpClientError, post_json
from lineageweave.lineage_persistence import lineage_edge_specs
from lineageweave.models import Edge
from lineageweave.tepp_client import AnalysisRunRequest, TeppClient, TeppNotAvailable
from lineageweave.tepp_client import (
AnalysisRunRequest,
TeppClient,
TeppInvalidResponse,
TeppNotAvailable,
)

_LINEAGE_KIND = "analysis_run_lineage"
_TEPP_KIND = "analysis_run_tepp"
Expand Down Expand Up @@ -277,6 +282,32 @@ def classify_tepp_submission(
return TeppSubmissionOutcome(_FAILED, "tepp_result_not_persisted", None, "")


def classify_tepp_status(
client: TeppClient,
request: AnalysisRunRequest,
remote_run_id: str,
) -> TeppSubmissionOutcome:
"""Read one bounded, request-bound TEPP status without resubmitting work."""
try:
response = client.read_analysis_run_status(remote_run_id, request)
except TeppNotAvailable:
return TeppSubmissionOutcome(_RUNNING, "", None, "")
except TeppInvalidResponse:
return TeppSubmissionOutcome(_FAILED, "tepp_result_not_persisted", None, "")
Comment on lines +295 to +296

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Invalid status payload permanently fails a receipt-bearing run

A TeppInvalidResponse from a status read maps to _FAILED (analysis_run_start.py:295-296); _deliver_tepp_measurement then appends a terminal Failed and marks the outbox delivered, unlike TeppNotAvailable which stays Running. One malformed status payload permanently fails a run that holds a durable accepted receipt, with no later retry. ADR 0178 documents fail-closed on invalid/mismatched, and no production status transport is wired yet, so impact today is nil.

Open in Devin Review

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

state = response["run_state"]
if state in {"accepted", "running"}:
return TeppSubmissionOutcome(_RUNNING, "", response, "")
terminal = response["terminal_result"]
if state == "failed":
return TeppSubmissionOutcome(
_FAILED,
str(terminal["failure_code"]),
terminal,
"",
)
return TeppSubmissionOutcome(_SUCCEEDED, "", terminal, _PERSIST_RESULT)


def _tepp_submission(
client: TeppClient,
request: AnalysisRunRequest,
Expand Down Expand Up @@ -309,6 +340,19 @@ async def _persist_tepp_result(
result_sha256 = hashlib.sha256(result_json.encode("utf-8")).hexdigest()
try:
async with conn.transaction():
existing = await conn.fetchrow(
"""
select remote_run_id, result_sha256
from analysis_run_tepp_result
where analysis_run_id = $1
""",
analysis_run_id,
)
if existing is not None:
return (
str(existing["remote_run_id"]) == remote_run_id
and str(existing["result_sha256"]) == result_sha256
Comment on lines +343 to +354

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Stored result digest recomputes the DTO, not TEPP's artifact digest

_persist_tepp_result stores result_sha256 as a hash of the full canonical terminal DTO, not TEPP's terminal['result_sha256']. The replay-rejection check (analysis_run_start.py:343-354) compares against this recomputed value. Since the DTO embeds TEPP's digest, a changed provider digest still changes the stored hash and is rejected, so the reject-changed-digest contract holds. Noted only so the column is not mistaken for TEPP's artifact digest.

Open in Devin Review

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

)
await conn.execute(
"""
insert into analysis_run_tepp_result
Expand Down Expand Up @@ -973,10 +1017,13 @@ async def _deliver_tepp_measurement(
knowledge_cutoff=locked["knowledge_cutoff"],
corporate_entity_id=str(locked["corporate_entity_id"]),
)
outcome = classify_tepp_submission(tepp_client, request)
if not outcome.persist_kind and await fetch_tepp_accepted_receipt(
conn, analysis_run_id
) is not None:
receipt = await fetch_tepp_accepted_receipt(conn, analysis_run_id)
outcome = (
classify_tepp_status(tepp_client, request, str(receipt["remote_run_id"]))
if receipt is not None
else classify_tepp_submission(tepp_client, request)
)
if receipt is not None and outcome.status_code == _RUNNING:
return False
status_code = outcome.status_code
failure_code = outcome.failure_code
Expand Down
11 changes: 6 additions & 5 deletions docs/adr/0162-tepp-accepted-receipt.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
**Depends on:** ADR 0022 authorized TEPP start; ADR 0023 analysis-run outbox
**Amends:** ADR 0022 (accepted envelopes are no longer Failed /
`tepp_result_not_persisted` when they carry a remote run id)
**Refs:** Issue #277; blocked completed-result poll remains
[TEPP#156](https://github.com/ContextualWisdomLab/TEPP/issues/156)
**Refs:** Issue #277; terminal-result consumption continues in ADR 0178 after
[TEPP#156](https://github.com/ContextualWisdomLab/TEPP/issues/156) closed

## Context

Expand Down Expand Up @@ -65,9 +65,10 @@ empty attachment, not a 500; list rows load receipts in one bounded
query rather than one query per run. Global Ask must not promote this
receipt into an answer claim.

Completed-result polling, bounded backoff, and request-binding
revalidation remain TEPP#156. This ADR does not invent a local
psychometric substitute while waiting.
Completed-result request-binding revalidation is ADR 0178. Automatic polling
and backoff remain unavailable until TEPP publishes a provider-owned HTTP
status service and evidence-based retry policy. This ADR does not invent a
local psychometric substitute while waiting.

```mermaid
sequenceDiagram
Expand Down
77 changes: 77 additions & 0 deletions docs/adr/0178-tepp-terminal-result-consumer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ADR 0178 — Read TEPP terminal results through the provider contract

**Decision status:** Accepted on this stacked PR; not protected-main truth until merge
**Date:** 2026-08-26
**Depends on:** ADR 0022, ADR 0023, ADR 0162; TEPP PR #157
**Refs:** LineageWeave issue #277; TEPP issues #156 and #249

## Context

ADR 0162 correctly separates TEPP's accepted receipt from measurement, but it
predates TEPP's versioned `AnalysisRunStatus` and
`AnalysisRunTerminalResult` v1 contracts. TEPP PR #157 merged those Rust wire
contracts on 2026-08-25. It deliberately did not publish a production HTTP
status service, so LineageWeave must not guess a URL or poll interval.

## Decision

`TeppClient` exposes a pluggable status-read transport alongside its existing
submission transport. A stored accepted receipt causes the delivery retry to
read that remote run once instead of resubmitting the request. The read is
bounded by the supplied transport call and is crash-resumable because the
receipt and PostgreSQL outbox remain durable.

The consumer requires the exact v1 status shape and revalidates remote run,
idempotency key, tenant/workspace, snapshot, cutoff, model contract, output
profile, terminal state, result schema, lowercase SHA-256 digest, bounded
identity-free summary, completion time, and failure code. Accepted/running
contains no terminal result and keeps the local run Running. A succeeded
terminal result is persisted before Succeeded. A terminal provider failure
appends its validated snake-case failure code without a result. Any mismatch
fails closed. Replaying the same remote run with a changed canonical result
digest is rejected.

The configured HTTP client does not synthesize `GET /v1/analysis-runs/{id}`
while TEPP documents it only as a target endpoint. A production status
transport is enabled only when the owning TEPP service publishes that route.
No theta, score, estimator, poll cadence, or backoff coefficient is implemented
in LineageWeave.

```mermaid
sequenceDiagram
participant Worker
participant Registry
participant TEPP
Worker->>Registry: read accepted receipt + immutable request
Worker->>TEPP: read status(remote run id)
alt accepted or running
Note over Registry: remain Running; outbox remains claimed
else succeeded and all bindings match
Worker->>Registry: persist terminal DTO + Succeeded atomically
else failed and all bindings match
Worker->>Registry: append typed Failed
else unavailable
Note over Registry: retain receipt; retry remains possible
else invalid or mismatched
Worker->>Registry: fail closed
end
```

## Consequences

The contract consumer is testable now through an in-process or future HTTP
adapter without pretending TEPP has deployed a route. Automatic scheduled
polling remains unavailable until the provider owns an executable status
endpoint and an evidence-based retry policy.

## References — APA 7th

Hohpe, G., & Woolf, B. (2003). *Enterprise integration patterns: Designing,
building, and deploying messaging solutions*. Addison-Wesley.

International Organization for Standardization. (2019). *ISO 8601-1:2019:
Date and time—Representations for information interchange—Part 1: Basic
rules* (confirmed 2024; Amendment 1:2022).

Moreau, L., & Missier, P. (Eds.). (2013). *PROV-DM: The PROV data model*.
World Wide Web Consortium. https://www.w3.org/TR/prov-dm/
45 changes: 36 additions & 9 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
# Product & Technical Gap Baseline

> Audit scope: the current LineageWeave reader/source-context worktree and all
> 56 open PRs, compared with protected `main`, the UI/UX Standard Guide v3.0,
> Audit scope: the current LineageWeave TEPP terminal-result continuation and
> all 9 open PRs, compared with protected `main`, the UI/UX Standard Guide v3.0,
> ADR 0118, the accepted TEPP contracts, and contextual-orchestrator. Real
> source identifiers are deliberately replaced with case labels; they must not
> enter repository artifacts.

## 1. Exact-head evidence

### 1.0 Current TEPP continuation

Observed at `2026-08-26`: protected `main` is
`04e6b610655d0db91d5f7ba9486bdda1440e0b19`. Nine PRs target `main`:
#644 `c1018a0a`, #643 `041ec13b`, #640 `2fad1fe6`, #639 `aee02dca`,
#636 `f7b9a65f`, #632 `3e3f0ead`, #631 `c0022c97`, #629 `4b4d6707`, and
#579 `689a21b6`. All have auto-merge armed and zero unresolved review threads;
none has the independent exact-head approval required by protected rules, so
no protected merge is claimed. The older snapshots below remain historical.

TEPP PR #157 merged as `7ce87c305981819f5333c7eb90ea0feafc0f7bf6`
and closed TEPP issue #156 by publishing `AnalysisRunStatus` and
`AnalysisRunTerminalResult` v1. The provider explicitly did not deploy a
production HTTP status service. This continuation therefore consumes the
strict contract through a pluggable status-read port, retains accepted/running
as transport evidence, persists only fully request-bound terminal results,
and rejects changed digests. It does not guess a provider URL, polling cadence,
backoff coefficient, theta, or score. Focused exact-source evidence is
`64 passed`; the full Python suite passed `1087` with `16` live-stack skips.
PR #656 carries this stacked consumer at exact head `5f8613e3`; TEPP issue #249
now owns the executable HTTP status-service gap. The unrelated
Starlette `httpx2` migration and short synthetic JWT-test key warnings remain
pre-existing dependency/test-fixture gaps and are not suppressed in this
TEPP-scoped continuation. Protected checks and independent review remain
required.

### 1.1 Current continuation head

Observed at `2026-08-24T06:45:00+09:00`: protected `main` and `origin/main`
Expand Down Expand Up @@ -551,7 +577,7 @@ adapter, fixture, or HTTP-shaped test double never upgrades a row to
| Evidence-grounded chat and source navigation | `/chat`, `/ask`, citation/evidence UI | source + unit; synthetic orchestrator judge route verified, corpus chat/runtime evidence open |
| OpenTelemetry across LineageWeave, contextual-orchestrator, Valkey, and GRC | LineageWeave PR #383 adds API/Valkey/session spans; contextual-orchestrator PR #818 carries session/provider telemetry; governance-risk-compliance PR #51 adds request telemetry, W3C trace context, OTLP export, and ADR 0009 | source + PR; protected merge and end-to-end collector evidence open |
| PU/team/project weekly/monthly reports | report API/UI and grouping controls | source + unit; TEPP-backed live report open |
| TEPP calibrated measurement, dichotomous items, multilevel/MMM/time model | published import/REST boundary and TEPP ADR/PRD references | boundary-only; live-external open |
| TEPP calibrated measurement, dichotomous items, multilevel/MMM/time model | accepted receipt parent #496 plus TEPP terminal-result v1 consumer (ADR 0178); arithmetic remains TEPP-owned | strict contract consumer source + focused unit; provider HTTP status service and live-external evidence open |
| contextual-orchestrator routing, VISION, embedding, schema repair | clients and provenance/session boundary; synthetic authenticated route returned a judge score of `0.98`, OCR succeeded, and region location returned five regions | source + local-integration partial; corpus backfill, capability/readiness evidence, and schema-repair workflow open |
| HTML semantic units, tables, indentation, footnotes, formulas | parser modules and synthetic tests; adjacent open PR #367 at exact head `b628722cb000717b0198e4337d12306d4306922d` adds numbered-footnote, leading-empty-cell, and short-ID regressions; 11-case authenticated popup sweep had no popup errors and rendered the supplied footnote/table cases; bounded metric superscript/subscript normalization has backend/frontend focused coverage | source + unit + local-integration partial; PR #367 protected checks, arbitrary formula/semantic correctness, and corpus re-backfill remain open |
| Base64/file image regions and multimodal evidence | image-region schema and VISION client boundary; live aggregate has 12,823 images, 25 described images, 421 failed images, 12,377 unavailable images, and 19 persisted regions; the bounded real-data queue run published three Valkey wake-ups and the worker claimed one | source + local-integration partial; supplied image-table case re-backfill and complete corpus coverage open |
Expand Down Expand Up @@ -780,21 +806,22 @@ or an explicit unavailable result.
digest mismatches before writes. The remaining acceptance work is to mount
the authorized raw artifacts and run the real import/backfill; do not map an
unrelated metadata column as body.
- **TEPP measurement — boundary accepted, runtime open:** LineageWeave must
- **TEPP measurement — terminal consumer implemented, runtime open:** LineageWeave must
call TEPP through its published import/REST contract and must not implement a
local theta, psychometric calibration, CAT, or judge score. TEPP owns the
Rust numerical/psychometric layer and its multilevel/multiple-membership/time
model. Live inspection on 2026-08-23 found that the upstream TEPP repository
currently publishes strict `AnalysisRunRequest` / `AnalysisRunAccepted` DTOs
and outbound HTTP exchange builders, but no executable HTTP server, completed
measurement response contract, snapshot-evidence ingest, or production
now publishes strict `AnalysisRunRequest`, `AnalysisRunAccepted`,
`AnalysisRunStatus`, and `AnalysisRunTerminalResult` v1 DTOs and outbound HTTP
exchange builders, but no production HTTP status server, snapshot-evidence
ingest, or production
estimator entrypoint. The current request carries only a snapshot digest, so
a service cannot calibrate the underlying observations without a new
purpose-bound evidence artifact/API. `TEPP_TRANSPORT_URL` alone therefore
cannot make measurement available. Close this in TEPP first with an ADR and
PRD update covering authorized evidence transfer, Rust estimator authority,
durable lifecycle/idempotency, completed-result provenance, and CPU/GPU
parity; then pin that service in Compose and prove a persisted
durable lifecycle/idempotency, provider HTTP status route, and CPU/GPU parity;
then pin that service in Compose and prove a persisted
`analysis_run_tepp_result`. An accepted-envelope shim is explicitly not an
acceptable substitute.
- **TEPP temporal context — source-connected, local runtime proven:** TEPP's
Expand Down
Loading