Skip to content

feat(api): record scientific acceptance via loopback lifecycle POST - #360

Closed
seonghobae wants to merge 1 commit into
feat/scientific-acceptance-http-status-gap-003afrom
feat/scientific-acceptance-http-lifecycle-gap-003a
Closed

feat(api): record scientific acceptance via loopback lifecycle POST#360
seonghobae wants to merge 1 commit into
feat/scientific-acceptance-http-status-gap-003afrom
feat/scientific-acceptance-http-lifecycle-gap-003a

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Consolidation decision — closed after the terminal scientific-acceptance artifact was retired

This PR is not merged. Exact head 99464037d90f7c23ec9213d9b49807be4593e406 contains useful generic Analysis Run lifecycle transitions, but its terminal POST body and subsequent GET contract explicitly store/serve scientific_acceptance_json / tepp.scientific_acceptance.v1. The #356/#358 authority chain is now retired.

Preserve and fold the generic lifecycle invariants into #166/#435: accepted→running→terminal monotonicity, reverse-transition refusal, terminal immutability, run/consumer isolation, exact artifact digest binding, metric-free nonterminal receipts and fail-closed unknown runs. Rebuild the terminal artifact as versioned Validation Evidence produced by the owning Rust validation path. Scientific Claim Promotion must remain a separate aggregate/policy rather than a lifecycle state side effect.

Branch, tests, reviews and doctoring remain available as fold lineage.

GAP-003A fourth slice for issue #166. POST /v1/analysis-runs stays a
metric-free receipt. POST /v1/analysis-runs/{run_id}/running and
POST /v1/analysis-runs/{run_id}/terminal are the production status-update
path so accepted/running stay metric-free and only a succeeded status
with profile scientific_acceptance_v1 may return tepp.scientific_acceptance.v1
on the subsequent GET. Canonical artifact bytes travel as
scientific_acceptance_json. Reverse transitions, mutating a terminal run,
failed-plus-artifact emission, receipt metric keys, an unknown run, and
consumer mismatch fail closed. This does not duplicate the analysis_engine
library bind, the terminal-result DTO wire, or the GET status slice.
@coderabbitai

coderabbitai Bot commented Aug 31, 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: e234cb76-433d-4a48-a055-29575da1b258

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 5 potential issues.

Devin Review

Comment on lines +171 to +173
if let Some(artifact) = self.scientific_acceptance_json.as_deref() {
require_nonempty(artifact)?;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Lifecycle builders emit rejected requests

For succeeded transitions, validate only requires nonempty artifact text. The exchange builders emit requests the live endpoint rejects for profile, schema, or digest mismatches.

Prompt for agents
Make AnalysisRunLifecycleTransition validation enforce the same succeeded-artifact rules as the live endpoint. Use the terminal result's output profile, schema, and result_sha256 to require or forbid scientific_acceptance_json and validate its JSON shape and exact byte digest. Keep one shared validator so constructors, deserialization, exchange builders, and AnalysisRunLiveService cannot diverge. Add tests proving every transition accepted by either public exchange builder is accepted by the corresponding live route, including missing, malformed, wrong-profile, wrong-schema, and digest-mismatched artifacts.
Devin Review

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

Comment on lines +226 to +227
let encoded_run_id = encode_path_segment(&transition.run_id);
let target_path = format!("{ANALYSIS_RUN_STATUS_PATH}/{encoded_run_id}/{suffix}");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Encoded run identifiers always fail

encode_path_segment accepts slash-containing run IDs, but the live decoder rejects their decoded slash. Every resulting lifecycle request receives a 400 response.

Prompt for agents
Align lifecycle exchange construction with live route parsing. Either reject run IDs whose decoded form the server refuses, or define a safe decoding contract that accepts percent-encoded reserved bytes consistently. Apply the same rule to status, running, and terminal builders and add builder-to-live-service round-trip tests for reserved characters, especially slash.
Devin Review

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

Comment on lines +386 to +395
if stored.status == status
&& stored.scientific_acceptance_json == transition.scientific_acceptance_json
{
let response_body = status_http_json(
&stored.status,
&stored.request,
stored.scientific_acceptance_json.as_deref(),
)?;
return Ok(json_response(200, "OK", response_body));
}

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: Exact retries remain idempotent

The equality gate includes both status and artifact bytes. Identical retries return stored output; altered terminal submissions cannot mutate completed runs.

Devin Review

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

Comment on lines +396 to +402
match stored.status.run_state {
AnalysisRunStatusState::Accepted => {}
AnalysisRunStatusState::Running
if matches!(
transition.run_state,
AnalysisRunStatusState::Succeeded | AnalysisRunStatusState::Failed
) => {}

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: Direct terminal completion is permitted

An accepted run can skip running and become terminal. The published lifecycle contract explicitly permits terminal updates from accepted or running states.

Devin Review

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

headers: &HashMap<String, String>,
body: &str,
) -> Result<NaruonLiveResponse, ApiError> {
let transition = AnalysisRunLifecycleTransition::from_json(body)?;

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: Lifecycle size limit remains enforced

The listener uses a broader framing limit, but from_json reapplies the 64 KiB lifecycle limit before parsing. Oversized transitions still fail closed.

Devin Review

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

Copy link
Copy Markdown
Contributor Author

Hour-20 exact-head review request.

Current head 99464037d90f7c23ec9213d9b49807be4593e406. Devin/CodeRabbit/author COMMENTED is not independent APPROVE. OpenCode DISMISSED/REQUEST_CHANGES on predecessor heads does not transfer. Ruleset 18156473 needs two independent APPROVE reviews on this exact SHA.

GAP-003A lifecycle POST draft stacked on #359. ADR 0028. Do not duplicate POST running/terminal.

Do not self-approve. Do not --admin merge. Checks/reviews are not a reason to weaken fail-closed gates.

Copy link
Copy Markdown
Contributor Author

Hour-21 exact-head review request.

Current head 99464037d90f7c23ec9213d9b49807be4593e406.
Devin/CodeRabbit/author COMMENTED is not independent APPROVE. Copilot review requests are not APPROVE. Ruleset 18156473 needs two independent approvals. Do not self-approve. Do not merge without two independent APPROVE reviews on this exact head.

@opencode-agent review

@seonghobae seonghobae closed this Sep 1, 2026
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