Skip to content

fix(edi): report what ingest_events stored in the response status - #3819

Open
PetrefiedThunder wants to merge 1 commit into
mainfrom
claude/distracted-greider-f61f07
Open

PetrefiedThunder wants to merge 1 commit into
mainfrom
claude/distracted-greider-f61f07

Conversation

@PetrefiedThunder

@PetrefiedThunder PetrefiedThunder commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Both EDI routes (POST /api/v1/ingest/edi and /document) reported success when ingest_events stored nothing. EDIIngestResponse.status defaulted to "accepted", so a document with every event rejected returned 201 with status: "accepted" and ingestion_result.accepted == 0. A partly stored document looked identical to a fully stored one. #3781 fixes the KDE names and listed this as out of scope.

Contract change

status is now derived from the ingest result. All three values arrive with HTTP 201.

status Stored Where the errors are
accepted Every event None
partial (new) Some events ingestion_result.events[], status and errors per event
rejected Nothing ingestion_result.events[], or rejection for ?strict=false
  • Behavior change: a document that used to say accepted with accepted: 0 now says rejected. A partly stored one now says partial, a value partners have not seen before.
  • rejected is reused from the existing advisory path, which already returns 201 with status: "rejected". Its two shapes stay mutually exclusive: ingestion_result means the document reached ingest, and rejection means it was stopped before ingest.
  • status has no default and is typed as a Literal, so a new return path fails loudly instead of claiming success.
  • 201 stays. I found no reason it must change. The advisory path already pairs 201 with rejected, and the body carries everything a 4xx would. Partners that check only the HTTP code stay misled and must read status, which TRACEABILITY_FILE_INTAKE.md now says.

Consumers checked

  • No frontend code calls these routes or reads this response. frontend/src mentions EDI only as a source-type label and a capability flag.
  • No other backend code reads EDIIngestResponse.status, and all three constructors are in routes.py.
  • No test asserted the old value. The only status="accepted" matches are per-event EventResult stubs.
  • External partners may read status. partial is new to them.

Tests

test_edi_ingest_status.py posts through the routes into the real ingest_events and real _validate_event_kdes, faking only auth, rate limit, database, canonical store and graph sync. The existing EDI tests stub ingest_events, which is why this went unseen.

  • All rejected: the ASN fixture on both routes. Asserts the KDE error and that nothing reached storage.
  • Partial: a batched 945 on /document where one set has no N1*ST, so only that event fails KDE validation.
  • All stored: a batched 945 on /document.
  • The 856 route's accepted and partial cases stub ingest_events, because no real 856 passes KDE validation until fix(ingestion): give EDI events the shipping KDEs validation requires #3781 lands.

Verification

Not in this PR

Jev sidecar was off: TYPESAFE_API_KEY is not set in this session.

🤖 Generated with Claude Code


Note

Medium Risk
This is an external API contract change (partial is new; zero-store documents flip from accepted to rejected) while HTTP 201 is unchanged, so integrations that ignore body fields may mis-handle failures.

Overview
Fixes a misleading EDI API contract where EDIIngestResponse.status always defaulted to "accepted" even when ingest_events stored nothing or only some lines.

Both POST /api/v1/ingest/edi and /document now set status from ingest_status(ingestion_result): accepted when every event persisted, partial (new) when some did, rejected when accepted == 0. HTTP 201 is unchanged; partners must read status and ingestion_result, not the status code alone. status is a required Literal with no default so new return paths cannot silently claim success.

TRACEABILITY_FILE_INTAKE.md documents the three outcomes and where errors live (ingestion_result.events[] vs advisory rejection). test_edi_ingest_status.py exercises the real ingest_events / KDE validation path (existing EDI tests stub ingest and missed this).

Reviewed by Cursor Bugbot for commit d140073. Bugbot is set up for automated code reviews on this repo. Configure here.

Both EDI routes returned status "accepted" by default, so a document
whose events were all rejected answered 201 accepted with
ingestion_result.accepted == 0. A partly stored document looked the same
as a fully stored one.

status is now derived from the ingest result: accepted, partial, or
rejected. The field has no default, so a new return path must state its
outcome. HTTP 201 is unchanged.

The new tests post through the routes into the real ingest_events and
_validate_event_kdes. The existing EDI tests stub ingest_events, which
is why this went unseen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
regengine Ready Ready Preview Nov 4, 2026 8:33am UTC

Request Review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T08:32:06.091814Z d140073 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The EDI response model now defines accepted, partial, and rejected statuses and derives the status from accepted and rejected event counts. The 856 and unified document routes return the derived status. Tests cover the three outcomes, and the documentation describes their stored-event conditions, error locations, and HTTP 201 behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: Minimal · up to d1400

Both EDI routes report ingestion outcomes while retaining HTTP 201. No merge-blocking risk remains.

Security architecture risk: Low · up to d1400

The change improves outcome reporting without changing ingestion permissions or storage behavior. The main remaining risk is partner compatibility with the new partial status. Clients that inspect only HTTP 201 will still miss rejected events.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed behavior is confined to reporting outcomes on the two existing EDI endpoints. The production diff does not add an ingestion caller, tenant selector, write operation, privileged action, or downstream service path.

Trust Boundaries and Controls

  • observed — Both endpoints retain the edi.ingest permission dependency and pass requested tenant identifiers together with the principal tenant to the existing resolver before reading and parsing the upload. The PR does not modify these admission calls or move response derivation ahead of ingestion.

Resilience and Maintainability Implications

  • observed — Retry behavior is pre-existing and unchanged: persistence can count an already durable idempotent event as accepted, while interchange deduplication can reject a repeated ISA13 identifier before ingestion with HTTP 409. The interchange reservation is separate from the database transaction, so a later failure can still obstruct retransmission; this PR changes neither that reservation nor its recovery behavior.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: reporting the events stored by ingest_events in the EDI response status.
Description check Passed The description is detailed, relevant, and covers the problem, contract change, affected consumers, tests, verification results, and out-of-scope items. It does not use the template headings or explic…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed, relevant, and covers the problem, contract change, affected consumers, tests, verification results, and out-of-scope items. It does not use the template headings or explicitly complete the Type of Change, Checklist, or Related Issues sections, but the required information is mostly present.

Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 5 functions across 3 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between bf8e3fb and d140073.

Files selected for processing: 4
  • docs/engineering/TRACEABILITY_FILE_INTAKE.md
  • services/ingestion/app/edi_ingestion/models.py
  • services/ingestion/app/edi_ingestion/routes.py
  • services/ingestion/tests/test_edi_ingest_status.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

PR Blocker Checklist

Head SHA: d140073dde4f46e7692ad6ca3c9659b466c0cf2c

Status: Ready

  • All review threads resolved
  • No reviews requesting changes
  • No blocked review engines

This branch was successfully deployed

1 active deployment
Preview — d140073d Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants