Skip to content

feat(analysis): bind longitudinal ESEM/DSEM composition to an analysis-run profile - #376

Draft
seonghobae wants to merge 2 commits into
mainfrom
feat/longitudinal-esem-dsem-composition-gap-169
Draft

feat(analysis): bind longitudinal ESEM/DSEM composition to an analysis-run profile#376
seonghobae wants to merge 2 commits into
mainfrom
feat/longitudinal-esem-dsem-composition-gap-169

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

GAP-169 / #169 remaining operator-visible slice: bind recovered construct-class, posterior-draw, strong/strict mean, event-time lag, membership, and within/between gates to an analysis_engine analysis-run output profile. Recovery primitives and Driver p.16 std-family restores are not this engine composition.

  • New profile longitudinal_esem_dsem_composition_v1 / schema tepp.longitudinal_esem_dsem_composition.v1 (ADR 0036; 0026–0035 remain on other live PRs or local dirty clones).
  • Cutoff-filters posterior draws by available_time against the request knowledge_cutoff.
  • Digest-bound terminal result with posterior-draw mean, preserved membership design, within component, event-time clock, and strong/strict invariance. Inference status is composed_engine_not_estimator.
  • Refuses point estimates, formative/network-as-reflective, unresolved constructs, metric-only latent means, between-as-within change, non-event clocks, collapsed non-nested membership, OLS-as-DSEM, and causal promotion from temporal precedence.
  • Cross-classified membership without collapse is recorded as cross_classified.
  • Does not invent an ESEM/DSEM sampler, persist rows, restore a Driver p.16 std matrix, duplicate CWC/Rubin binds, or claim implemented-main.

This is not implemented-main. Exact-head Checks on this head only. Predecessor-head evidence does not transfer.

Does not duplicate:

Test plan

  • cargo fmt -p analysis_engine -- --check
  • cargo test -p analysis_engine
  • cargo clippy -p analysis_engine --all-targets -- -D warnings
  • RUSTDOCFLAGS="-D warnings" cargo doc -p analysis_engine --no-deps
  • Exact-head Required Checks on this SHA
  • Two independent (non-author, non-bot) APPROVE reviews

Merge bar

Ruleset 18156473: two independent approvals + exact-head Checks. Do not self-approve. Do not --admin merge.


Devin Review

…s-run profile

GAP-169 / #169 remaining operator-visible slice: compose recovered
construct-class, posterior-draw, strong/strict mean, event-time, membership,
and within/between gates as a cutoff-safe analysis-run output. Not an
estimator, not a Driver p.16 std restore, and not implemented-main.
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 2 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 736336a3-e9be-4ef8-a3b1-05f4e5b80ff0

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc02f5 and 02c2275.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • ARCHITECTURE.md
  • CHANGELOG.md
  • crates/analysis_engine/Cargo.toml
  • crates/analysis_engine/src/lib.rs
  • crates/analysis_engine/src/longitudinal_esem_dsem_artifact.rs
  • crates/analysis_engine/tests/longitudinal_esem_dsem_execution_contract.rs
  • crates/membership_core/src/icc.rs
  • docs/TRACEABILITY.md
  • docs/adr/0036-longitudinal-esem-dsem-composition.md
  • docs/adr/README.md
  • docs/doctoring/longitudinal-esem-dsem-composition.md

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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 7 potential issues.

Devin Review

Comment on lines +422 to +425
snapshot_id: &str,
knowledge_cutoff: KnowledgeCutoff,
design: &LongitudinalEsemDsemDesign,
observations: &[LongitudinalEsemDsemObservation],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Cross-snapshot draws receive false provenance

Observations carry no snapshot identity, so execute_longitudinal_esem_dsem_run accepts draws from another snapshot. The signed result attributes them to the requested snapshot.

Prompt for agents
Bind every LongitudinalEsemDsemObservation, or an immutable collection containing them, to its source snapshot. In execute_longitudinal_esem_dsem_run, validate that provenance against AnalysisRunRequest.snapshot_id before admitting draws. Ensure the resulting artifact digest covers the validated provenance and add a test that mixes observations from another snapshot.
Devin Review

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

Comment on lines +347 to +350
for observation in observations {
if observation.available_time.instant() <= knowledge_cutoff.instant() {
draws.extend_from_slice(&observation.posterior_draws);
observation_count = observation_count

@devin-ai-integration devin-ai-integration Bot Aug 31, 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.

🔴 Unbounded draw copying exhausts memory

A single observation can contain unlimited draws. extend_from_slice duplicates them without a total bound, so a valid run can exhaust process memory.

Devin Review

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

Comment on lines +434 to +438
if request.knowledge_cutoff != knowledge_cutoff.to_rfc3339()
|| request.model_contract_version != LONGITUDINAL_ESEM_DSEM_MODEL_CONTRACT_VERSION
|| request.output_profile != LONGITUDINAL_ESEM_DSEM_OUTPUT_PROFILE
{
return Err(AnalysisEngineError::InvalidEvidence);

@devin-ai-integration devin-ai-integration Bot Aug 31, 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.

🟡 Equivalent cutoffs reject valid runs

execute_longitudinal_esem_dsem_run compares timestamp text instead of instants. Valid offset timestamps fail when the supplied cutoff uses equivalent canonical UTC text.

Devin Review

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

Comment on lines +455 to +460
construct_class: design.construct_class.as_str().into(),
membership_design: membership_design_wire_name(design.membership_design)?.into(),
component_level: design.component_level.wire_name().into(),
lag_clock: design.lag_clock.as_str().into(),
invariance_status: design.invariance_status.as_str().into(),
inference_status: LONGITUDINAL_ESEM_DSEM_INFERENCE_STATUS.into(),

@devin-ai-integration devin-ai-integration Bot Aug 31, 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.

🟡 Model provenance disappears from digest

Different model_version or comparison_scope values can produce identical artifacts and digests. Consumers cannot identify which fitted model supplied the invariance claim.

Devin Review

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

Comment on lines +290 to +292
|| self.observation_count == 0
|| self.draw_count < 2
|| !self.posterior_draw_mean.is_finite()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Impossible artifact counts pass validation

validate accepts 100 observations with only two draws, although every observation requires at least two. Tampered artifacts therefore pass full validation.

Prompt for agents
Validate draw_count against observation_count using checked multiplication: every admitted observation contributes at least two draws. Also enforce the execution's observation bound on reconstructed included and excluded counts with checked addition. Add malformed JSON tests for impossible count combinations and overflow boundaries.
Devin Review

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

if design.promote_causal {
claim_causal_effect(CausalHeuristic::TemporalPrecedence)?;
}
let _ = claim_causal_effect(CausalHeuristic::TemporalPrecedence);

@devin-ai-integration devin-ai-integration Bot Aug 31, 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.

🔍 Discarded causal refusal obscures success

Every successful run calls claim_causal_effect and discards its error. The redundant call obscures which branch actually enforces causal refusal.

Devin Review

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

Comment on lines +77 to +81
let comparison_scope = comparison_scope.into();
let model_version = model_version.into();
if comparison_scope.is_empty() || model_version.is_empty() {
return Err(AnalysisEngineError::InvalidEvidence);
}

@devin-ai-integration devin-ai-integration Bot Aug 31, 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.

🔍 Blank provenance labels remain admissible

LongitudinalEsemDsemDesign::new accepts whitespace-only scope and model labels. Such labels provide no usable provenance despite passing the evidence gate.

Devin Review

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

@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 2 new potential issues.

Devin Review

Comment on lines +270 to +272
pub fn to_json(&self) -> Result<String, AnalysisEngineError> {
self.validate()?;
serde_json::to_string(self).map_err(|_| AnalysisEngineError::SerializationFailure)

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: Artifact output remains inherently bounded

The removed to_json check cannot admit oversized valid output. Identifiers are capped, timestamps are bounded, and other strings require fixed values.

Devin Review

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

Comment on lines +385 to +386
)
.expect("design constructor requires nonempty invariance labels");

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: Validation assertions preserve error boundaries

Both assertions are unreachable through public inputs. Private design labels are constructor-validated, while evidence counts remain below the summary limit.

Devin Review

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

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