Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a3c57a9
feat(api): publish terminal analysis result contract
seonghobae Aug 20, 2026
003dae3
fix(api): preserve existing contract documentation
seonghobae Aug 20, 2026
0cb693f
fix(api): restore strict terminal result implementation
seonghobae Aug 20, 2026
cffbf4e
test(api): cover terminal analysis result contract
seonghobae Aug 20, 2026
24f00c0
feat(api): add analysis run status contract
seonghobae Aug 20, 2026
30918e5
fix(api): harden analysis result serialization bindings
seonghobae Aug 20, 2026
020c353
fix(api): bound accepted analysis run payloads
seonghobae Aug 20, 2026
f1c94f7
fix: validate terminal result bindings
seonghobae Aug 20, 2026
fb783f5
test(api): cover empty https origin
seonghobae Aug 20, 2026
0cb9ff6
test(api): close unreachable HTTP branch
seonghobae Aug 20, 2026
1fa8e9e
Merge main into completed analysis result contract
seonghobae Aug 21, 2026
63a419e
fix(docs): align naruon maturity with protected main
seonghobae Aug 21, 2026
7625dc6
fix(api): harden accepted receipts and provider headers
seonghobae Aug 21, 2026
6852e9d
fix: harden analysis result contract boundaries
seonghobae Aug 21, 2026
95add88
test: close coverage and match guarded arms
seonghobae Aug 21, 2026
e3770a6
test: cover provider credential header branches
seonghobae Aug 21, 2026
910a54e
fix(api): reject delimiter-free credential headers
seonghobae Aug 21, 2026
efd5386
test: configure repository root for pytest
seonghobae Aug 21, 2026
48643e5
fix(coverage): preserve multiline match guards
seonghobae Aug 21, 2026
8e96984
Merge remote-tracking branch 'origin/feat/completed-analysis-result-c…
seonghobae Aug 21, 2026
a9a49d3
fix(coverage): respect match arm boundaries
seonghobae Aug 21, 2026
ef45763
fix(coverage): reject block-boundary false guards
seonghobae Aug 21, 2026
3b07002
fix coverage guard after destructuring match arm
seonghobae Aug 21, 2026
9238c3a
cover nested and long match guards
seonghobae Aug 21, 2026
e06e504
cover split nested match guard
seonghobae Aug 21, 2026
12ada13
retain guards after sibling match arms
seonghobae Aug 21, 2026
cce90a1
feat(engine): execute cutoff-safe analysis runs (#178)
seonghobae Aug 22, 2026
7402745
test(engine): include evidence available exactly at cutoff
seonghobae Aug 23, 2026
3400874
fix(quality): keep sibling-block guards and lone closes in coverage gate
Aug 24, 2026
810e3a9
Merge current main into analysis result contract
seonghobae Aug 24, 2026
88f10c4
docs: assign analysis engine ADR a unique number
seonghobae Aug 24, 2026
e22dcb1
fix: retain compact guarded match coverage
seonghobae Aug 24, 2026
41a7218
remove dead coverage guard branch
seonghobae Aug 24, 2026
d245a9d
fix(coverage): stop nested match guard scans at let bindings
seonghobae Aug 24, 2026
7521a15
Merge remote-tracking branch 'origin/main' into fix/pr157
seonghobae Aug 24, 2026
f8de47e
Merge remote-tracking branch 'origin/main' into fix/pr157b
seonghobae Aug 24, 2026
e65023f
merge: reconcile analysis-run result contract with protected main
seonghobae Aug 25, 2026
5d1492c
Merge remote-tracking branch 'origin/main' into rebase/157
seonghobae Aug 25, 2026
959108d
Merge remote-tracking branch 'origin/main' into rebase/157
seonghobae Aug 25, 2026
f31d822
Merge remote-tracking branch 'origin/main' into rebase/157
seonghobae Aug 25, 2026
f204ec9
Merge remote-tracking branch 'origin/main' into rebase/157
seonghobae Aug 25, 2026
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
1 change: 1 addition & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ boundaries above remain the target modular MSA architecture.
| `tepp_simulation` | known-truth temporal/event data generation |
| `validation_core` | RMSE, bias, coverage, graph, and Monte Carlo metrics |
| `tepp_api` | versioned DTO, schema, terminal-result, and export contracts |
| `analysis_engine` | bounded cutoff-safe temporal evidence readiness execution and digest-bound terminal artifacts |
| `location_membership` | location is not entity identity and not a language channel |
Comment on lines +69 to +72

@devin-ai-integration devin-ai-integration Bot Aug 25, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Duplicate, conflicting crate rows in topology table

The added rows for validation_core, tepp_api, and location_membership duplicate rows that already exist in the same table further down. validation_core and tepp_api now appear twice with different descriptions, so the table states two conflicting responsibilities for each.

Open in Devin Review

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

| `validation_core` | RMSE, bias, coverage, graph, Monte Carlo, and exact-head claim-promotion metrics |
| `tepp_api` | versioned DTO, schema, and export contracts |
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ All notable changes to TEPP are documented here. The format follows Keep a Chang
- `persistence_postgres` entity/project target SQL now rejects empty, oversized, or hostile type/status labels before insert; interpolated codes are restricted to lowercase ASCII `snake_case` characters so membership foreign keys remain referentially safe (ADR 0003 / ADR 0013).
- `persistence_postgres` live SQLx transport retains one pool-backed PostgreSQL connection per session so tenant binding and the following statement share a session, and closes the connection and owned runtime safely from another Tokio runtime (ADR 0013).
- The authored-line coverage gate now filters LLVM-only literal and expression continuation records while retaining branch coverage for their executable decisions; Rust function signatures and structural branch lines are no longer counted as uncovered statements.
- Stacked `analysis_engine` vertical slice (ADR 0021): bounded Rust execution
from an accepted analysis run to a cutoff-safe, multiple-membership-aware,
SHA-256-digest-bound terminal artifact or redacted no-eligible-evidence
result. This remains active-PR evidence and does not claim estimator
authority.
- `tepp_api` request-bound terminal analysis results and typed analysis-run
status/read responses: accepted/running states cannot carry measurement
evidence, and terminal results bind exact request and receipt identities.
- Coverage classification now preserves multiline Rust `match` guard expression
lines while ignoring structural closing parentheses and match-arm labels.
- `corpus_split` Unicode canonical identity: NFC/NFD-equivalent bodies produce `CanonicalEquivalent` leakage links and cannot occupy independent partitions; empty bodies and duplicate document identities fail closed (ADR 0004/0008/0013; PR #59).
- `semantic_core` binds exact `evidence_core` source spans as semantic units. Language profiles are `unresolved` or a primary ISO 639 subtag with an IANA-registered ISO 3166-1 alpha-2 or UN M.49 region (RFC 5646; IANA File-Date 2026-08-08); private-use and unknown regions fail closed. Unresolved metadata keeps the caller-supplied Korean `측정` span and does not retokenize. `SemanticIdentity::from_language_tag` fails closed. Korean and English report sentences remain distinct units. Not concept alignment, not invariance, not a topic estimator (ADR 0020; issue #168). The APA register cites RFC 5646 once, in the Unicode/language-tags section; the slice-specific note remains `docs/research/span-grounded-semantic-units.md`.
- `corpus_background` identity gate: corpus-level background wording is not unique latent content or a state transition; recovery tests distinguish background evidence from unique content.
Expand Down
21 changes: 0 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ members = [
"crates/compute_backend",
"crates/episode_membership",
"crates/membership_target",
"crates/analysis_engine",
"crates/topic_measurement",
"crates/analysis_engine",
"crates/psychometric_core",
Comment on lines +57 to 60

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 analysis_engine listed twice in workspace members

members and default-members each list crates/analysis_engine twice (lines 57/59 and 116/118). EXPECTED_CRATES received the same duplicate so the contract check still passes, but the duplicate entry is unintended and mirrors pre-existing duplicates like episode_membership.

Open in Devin Review

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

Expand Down Expand Up @@ -112,6 +113,7 @@ default-members = [
"crates/compute_backend",
"crates/episode_membership",
"crates/membership_target",
"crates/analysis_engine",
"crates/topic_measurement",
"crates/analysis_engine",
"crates/psychometric_core",
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ implemented in Rust.

## Current implementation state

This branch preserves the protected-main Rust workspace and quality-gate
foundation. The workspace crates expose tested contracts only: immutable
evidence identities and exact spans, six-clock temporal values with Allen
algebra and cutoff eligibility, event mentions/instances with
evidence-layer intelligence gates, a forward-only relation graph,
cross-classified membership, bitemporal persistence, leakage-safe corpus
splits, simulation manifests, claim-promotion validation, API DTOs, the
purpose-bound privacy envelope, and longitudinal within/between
decomposition. It adds the independently usable `analysis_engine` vertical
slice: bounded cutoff-safe readiness work that emits a digest-bound terminal
artifact or a redacted no-eligible-evidence result. That slice is active-PR
evidence, not a psychometric estimator or a release claim.
The current workspace contains 50 independently documented Rust crates. Each
crate exposes a bounded, tested contract for evidence, temporal semantics,
event and relation reasoning, membership, persistence, simulation, validation,
API exchange, compute planning, or evidence-grounded interpretation. Numerical
and psychometric authority remains on the CPU `f64` reference path; streamed
accelerator plans must preserve the full observation set and fail closed to the
reference path when resources or validation are insufficient.
The repository currently implements 53 independently documented crates rather
than a full commercial release. The implemented crates include topic measurement
and the analysis engine; they do not claim a complete commercial estimator,
Expand Down Expand Up @@ -100,6 +119,7 @@ crates/corpus_split
crates/tepp_simulation
crates/validation_core
crates/tepp_api
crates/analysis_engine
crates/episode_membership
crates/location_membership
crates/prediction_contradiction
Expand Down
14 changes: 10 additions & 4 deletions crates/analysis_engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//! was unavailable at the requested knowledge cutoff, counts multiple-membership
//! assignments without collapsing them, and emits a digest-bound terminal result
//! through [`tepp_api`]. It deliberately does not claim latent-variable or topic
//! estimation authority; those estimators remain separate scientific crates.
//! estimation authority; it invokes estimators through their scientific crate
//! contracts and preserves their artifact meaning.

Expand Down Expand Up @@ -303,6 +304,14 @@ pub fn execute_analysis_run(
});
}

// The corpus bound makes this conversion strictly smaller than
// `u64::MAX`; the fold still fails closed through checked arithmetic so a
// future bound change cannot wrap membership totals silently.
let eligible_evidence_count = eligible.len() as u64;
let eligible_membership_count = eligible.iter().try_fold(0_u64, |sum, unit| {
sum.checked_add(u64::from(unit.membership_count))
.ok_or(AnalysisEngineError::ArithmeticOverflow)
})?;
// The corpus bound makes this conversion and sum strictly smaller than
// `u64::MAX`: 100,000 * u32::MAX is below the 64-bit range.
let eligible_evidence_count = eligible.len() as u64;
Comment on lines +307 to 317

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Overflow-checked membership total silently discarded

The eligible membership total is first summed with checked arithmetic that fails closed on overflow, then immediately re-summed with an unchecked + fold that shadows and discards the checked value. The overflow guard never applies, so raising the evidence-unit bound can wrap the total silently.

(Refers to this code)

Open in Devin Review

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

Expand Down Expand Up @@ -378,6 +387,7 @@ mod tests {
use super::{
ANALYSIS_ARTIFACT_SCHEMA_VERSION, ANALYSIS_STATISTIC_COUNT, AnalysisCorpus,
AnalysisEngineError, AnalysisEvidenceUnit, MAX_ANALYSIS_IDENTIFIER_BYTES,
MAX_EVIDENCE_UNITS, execute_analysis_run,
MAX_EVIDENCE_UNITS, TopicMeasurementError, execute_analysis_run,
};
Comment on lines 387 to 392

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Test module has duplicate imports and truncated calls

The test module imports MAX_EVIDENCE_UNITS and execute_analysis_run twice (lines 390-391), and several unit(...) calls lost their available argument, leaving 3 arguments against the 4-argument unit signature. Both are merge artifacts that stop the crate tests from building.

Open in Devin Review

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

use temporal_core::{AvailableTime, EventTime};
Expand Down Expand Up @@ -529,7 +539,6 @@ mod tests {
vec![unit(
"evidence-1",
"2026-07-01T00:00:00Z",
"2026-07-01T00:00:00Z",
1,
)],
)
Expand All @@ -545,7 +554,6 @@ mod tests {
vec![unit(
"evidence-1",
"2026-07-01T00:00:00Z",
"2026-07-01T00:00:00Z",
1,
)],
)
Expand Down Expand Up @@ -586,7 +594,6 @@ mod tests {
let evidence = unit(
"evidence-accessor",
"2026-07-01T00:00:00Z",
"2026-07-01T00:00:00Z",
4,
);
assert_eq!(evidence.evidence_id(), "evidence-accessor");
Expand Down Expand Up @@ -661,7 +668,6 @@ mod tests {
vec![unit(
"evidence-1",
"2026-07-01T00:00:00Z",
"2026-07-01T00:00:00Z",
1,
)],
)
Expand Down
33 changes: 33 additions & 0 deletions crates/analysis_engine/tests/end_to_end_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,39 @@ fn production_shape_run_excludes_future_available_evidence() {
);
}

#[test]
fn evidence_available_exactly_at_cutoff_is_eligible_and_keeps_membership() {
let request = AnalysisRunRequest {
contract_version: 1,
idempotency_key: "boundary-run-2026-08-01".into(),
tenant_workspace_id: "workspace-opaque-1".into(),
snapshot_id: "snapshot-boundary-2026-08-01".into(),
knowledge_cutoff: "2026-08-01T00:00:00Z".into(),
model_contract_version: "temporal-evidence-v1".into(),
output_profile: "validation-report".into(),
};
let accepted =
AnalysisRunAccepted::new("run-boundary-1", "accepted", "boundary-run-2026-08-01")
.expect("accepted");
let corpus = AnalysisCorpus::new(
"snapshot-boundary-2026-08-01",
vec![
evidence("invoice-on-cutoff", "2026-08-01T00:00:00Z", 3),
evidence("later-correction", "2026-08-01T00:00:01Z", 4),
],
)
.expect("snapshot");
let execution = execute_analysis_run(&request, &accepted, &corpus, "2026-08-01T00:01:00Z")
.expect("execute");
assert_eq!(
execution.terminal_result.run_state,
AnalysisRunTerminalState::Succeeded
);
let artifact = execution.artifact.expect("artifact");
assert_eq!(artifact.eligible_evidence_count, 1);
assert_eq!(artifact.eligible_membership_count, 3);
}

#[test]
fn snapshot_identity_is_not_inferred_from_customer_payload() {
let request = AnalysisRunRequest {
Expand Down
15 changes: 15 additions & 0 deletions docs/API_CONTRACT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# TEPP API and Modular Integration Contract

**Status:** Accepted target contract; exact endpoints are introduced only with executable services.
**Last reviewed:** 2026-08-24
**Last reviewed:** 2026-08-21

## 1. Authority boundary
Expand All @@ -25,6 +26,8 @@ Current protected main exposes Rust library/domain contracts. The active stack a
| temporal-context ordering contract | `tepp_api` v1 wire DTOs | LineageWeave | active-PR |
| cutoff-safe analysis-run readiness execution | `analysis_engine` bounded Rust crate | `tepp_api`, future HTTP/service adapters | active product branch |
| project-history projection contract | `tepp_api` v1 wire DTOs | LineageWeave | active-PR |
| analysis-run status/terminal-result contracts | `tepp_api` v1 wire DTOs | naruon, orchestrator, UI | active-PR #157 |
| cutoff-safe analysis-run readiness execution | `analysis_engine` bounded Rust crate | `tepp_api`, future HTTP/service adapters | active-PR |

## 3. Versioning

Expand Down Expand Up @@ -57,6 +60,18 @@ GET /v1/exports/{export_id}

Long-running analysis is durable asynchronous work. `POST /v1/analysis-runs` accepts an idempotency key, immutable input snapshot identity, knowledge cutoff, versioned model contract/configuration, and requested output profile. A retry with the same principal/idempotency key and semantically identical request returns the same run identity; a conflicting body fails closed.

The typed status/read contract returns `accepted`, `running`, `succeeded`, or
`failed`. Accepted and running statuses contain no measurement result. A
terminal status contains exactly one request-bound `AnalysisRunTerminalResult`;
consumers validate its request, receipt, snapshot, cutoff, model, profile, and
idempotency bindings before treating it as measurement evidence.

The stacked `analysis_engine` slice provides the first executable service-side
path behind these DTOs. It consumes a bounded identity-free snapshot, excludes
evidence unavailable at the historical cutoff, preserves multiple-membership
counts, and emits a digest-bound terminal result or a redacted failure. It is
not a substitute for approved topic or psychometric estimators.

`POST /v1/temporal-context` is a bounded LineageWeave read contract. It accepts
only events whose availability time is at or before `knowledge_cutoff`, orders
them by event time and opaque event ID, and emits adjacent forward temporal
Expand Down
2 changes: 2 additions & 0 deletions docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ The full APA 7th standards/literature register remains `docs/research/standards-
| recovery metrics (RMSE, bias, coverage, graph, temporal order, Monte Carlo SE gates) | PRD; Test Strategy; ADR 0007/0014 | `validation_core` on protected main (PR #19); SE-aware Monte Carlo gates included | implemented-main |
| PostgreSQL bitemporal/lineage persistence | ADR 0013; Architecture/ERD | `persistence_postgres` migration contracts, in-memory adapters, live SQL session/document SQL port, tenant RLS (`0002` + session GUC/role helpers), `DATABASE_URL` SQLx gate, optional session-affine `live-sqlx` `PgPool` driver, exact-head live PostgreSQL CI with isolation proof, append-only immutability triggers (`0004`), temporal interval ordering CHECKs (`0005`), typed membership assignment (`0006` implemented-main), event-relation/mention/instance SQL (#37–#39 implemented-main), source-artifact SQL (#40 implemented-main), audit-event SQL (#41 implemented-main), concurrent document-write stress (#43 implemented-main), backup/restore integrity revalidation (#44 implemented-main), `revision_order` later-revision system-time ordering implemented-main, entity/project target SQL on PR #131; remaining physical ERD constraints | partial |
| known-truth temporal/event simulation manifests | PRD; TRD; Test Strategy | `tepp_simulation` on protected main; recovery metrics in `validation_core` | implemented-main |
| versioned service/API contracts and exports | PRD; API contract; ADR 0011/0013 | `tepp_api` analysis-run/export/JSON-LD/GraphML contracts on protected main (PR #21); request-bound terminal result active in PR #157; HTTP service remains accepted-target | partial |
| executable cutoff-safe analysis-run readiness | ADR 0021; temporal research; API terminal-result contract | stacked `analysis_engine` PR on #157: availability cutoff, snapshot binding, multiple-membership aggregation, digest-bound artifact, realistic end-to-end tests | active-PR |
| delayed-reporting cutoff eligibility in truth corpora | ADR 0002; research | `tepp_simulation` eligible-at-cutoff filter on the active PR | active-PR |
| versioned service/API contracts and exports | PRD; API contract; ADR 0011/0013 | `tepp_api` analysis-run/export/JSON-LD/GraphML contracts on protected main (PR #21); HTTP service remaining accepted-target | partial |
| versioned service/API contracts and exports | PRD; API contract; ADR 0011/0013 | `tepp_api` analysis-run/export/JSON-LD/GraphML contracts on protected main (PR #21); LineageWeave loopback contracts and request-bound terminal result are composed on the active product branch; production TLS remaining | partial |
Expand Down
4 changes: 3 additions & 1 deletion docs/adr/0003-relational-event-multiple-membership.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
**Implementation maturity:** partial — membership networks, event mention/instance separation, and the protected-main forward-transition foundation are implemented-main; `support_edge`, `outcome_order`, `retrospective_edge`, `inferred_status`, `copy_identity`, `summarizes_edge`, `subevent_containment`, `location_membership`, `episode_membership`, and typed target kinds are covered by this active consolidation PR; full multilevel/MMMC estimators and remaining persistence remain accepted-target.
**Date:** 2026-08-05
**Implementation maturity:** partial — membership network, event mention/instance separation, inferred/evidential/retrospective status gates, summary/source identity separation, template-copy/source identity separation, typed forward-only relation graph, strict input-process-outcome ordering, nested ICC refusal, and subevent parent-window containment are implemented-main; full multilevel/MMMC estimators and remaining persistence remain accepted-target.
**Date:** 2026-08-24
**Date:** 2026-08-24
**Decision status:** Accepted
**Implementation maturity:** partial — membership network/roles with Kish ESS and nested ICC (cross-classified/multiple-membership refusal), event mention/instance separation, the typed forward-only relation graph, and the copy/summary/outcome-order/support/inferred-status/retrospective-reporting/location identity gates are implemented-main; typed target-kind membership identity in `membership_target` is on PR #131; multilevel psychometric estimators and remaining persistence details follow ADR 0013 and [`docs/TRACEABILITY.md`](../TRACEABILITY.md) as accepted-target.
**Supersedes:** None. ADR 0016 owns TDT/CHRONOS event-intelligence task semantics; this ADR remains authoritative for ontology, relation, role, and membership structure.

## Context
Expand Down
1 change: 1 addition & 0 deletions docs/adr/0011-standalone-modular-msa-boundary.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ADR 0011 — Standalone operation and modular CWL MSA boundary

**Decision status:** Accepted
**Implementation maturity:** partial — Rust crates are independently usable; naruon HTTP interchange is implemented-main, while the loopback live listener (`POST /v1/analysis-runs` and `/v1/exports`, fail-closed table-access, NIM/proxy headers, RFC 3339 cutoff, stream deadline) is active-PR #157; `service_tls` production rustls bind gates and remaining live HTTP listeners and persistence integrations remain accepted-target
**Implementation maturity:** partial — Rust crates are independently usable; naruon HTTP interchange and loopback live listener (`POST /v1/analysis-runs` and `/v1/exports`, fail-closed table-access, NIM/proxy headers, RFC 3339 cutoff, stream deadline) is implemented-main; `service_tls` production rustls bind gates and orchestrator live-port refusal of loopback plaintext are on this active PR; the loopback consumer listener composition and terminal-result contract are composed on the active product branch; production TLS/`$PORT`, remaining live HTTP listeners, and remaining persistence integrations remain accepted-target
**Date:** 2026-08-10
**Date:** 2026-08-10
Expand Down
Loading
Loading