Skip to content

fix(chat): fail closed on unreadable ingress receipts - #8679

Open
Duang777 wants to merge 1 commit into
loopx-project:mainfrom
Duang777:codex/ingress-receipt-fail-closed-20261113
Open

Duang777 wants to merge 1 commit into
loopx-project:mainfrom
Duang777:codex/ingress-receipt-fail-closed-20261113

Conversation

@Duang777

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

  • Outcome basis / optional anchor: self-found core defect. No public issue; the request in this PR is the basis.
  • Goal/source and gap: create_ingress_receipt in loopx/chat_ingress.py read the receipt with the lenient _read_json, which returns {} for a truncated, non-object or unreadable file. steering_receipt right next to it already treated that as an error.
  • Observable before → after, with the validation row that proves it: After a delivered live-steering receipt is damaged, a retry with the same client_ingress_id wrote a fresh pending receipt over it, returned created=True and steered the native turn again; the HTTP reply was 409 live_steering_turn_mismatch / not_delivered. After: 409 live_steering_delivery_unresolved / unresolved, the damaged bytes are left untouched and exactly one turn/steer reaches the peer. Rows: regression_parity, unit.
  • Issue/task and intended base: none. Base is main at 7bb1b01.

Author Declaration

  • Written by: agent, Claude Sonnet 5.5, for @Duang777

Implemented against

  • Specification and revision: no written specification; the request in this PR is the basis.
  • Criteria:
Criterion (spec clause) Disposition Symbol / path Test or command
A damaged receipt is not evidence of absence implemented _strict_ingress_receipt, create_ingress_receipt, steering_receipt in loopx/chat_ingress.py tests/test_chat_session_active_turn.py::test_unreadable_ingress_receipt_is_not_recreated_as_absent
A retried steer is never re-sent after its receipt becomes unreadable implemented same tests/test_chat_turn_steering.py::test_http_steering_never_resends_after_its_receipt_becomes_unreadable
  • Self-check before submission: reproduced the defect on the unfixed code, then confirmed the new test fails before and passes after. Did not run the full suite or CI-equivalent gates locally. Read the Host/Origin gating, asset serving and Content-Length handling in chat_server.py and found nothing provable.

Scope And Continuation

  • Completed scope and remaining work: complete within this scope. update_ingress_receipt and loopx_ingress still use the lenient reader. Neither produced a harmful outcome in my probes, so they are left alone.
  • Slice boundary / successor: N/A, one independent defect.

Validation

  • Tested revision: 87878a7b2cb66dc5969719fec53e16c0fddde7aa for the diff; local runs used a working tree whose touched files were blob-identical to main 7bb1b01 before the change.
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
regression_parity passed New tests fail on the unfixed code (fresh receipt written, second steer sent) and pass with the fix, parametrized over a truncated file, [] and a foreign schema version.
unit passed 323 passed across the two touched files and the neighbouring chat runtime, attached-session and queue tests; ruff check clean.
  • Coverage and gaps: Callers already map OSError to the unresolved reply, so the HTTP path is covered by the second test. Lark-side ingress is not touched.

Frontend / Visual Evidence

  • UI impact: none

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: Core control-plane hardening.

Shared-authority RFC fixture impact

N/A

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • Existing English/Chinese RFC pairs (including ledger entries) both have a diff, and the local premerge check passes, or no paired RFC changed. No RFC changed.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

🤖 Generated with Claude Code

create_ingress_receipt read the receipt with the lenient _read_json, which
turns a truncated, non-object or foreign-schema file into {} and so into
"no receipt". A retry of a live-steering ingress whose receipt was damaged
after delivery then recreated a fresh pending receipt (overwriting the
damaged evidence), reported created=True and sent the steering message to the
native turn a second time; the HTTP retry answered not_delivered instead of
unresolved.

Reuse the strict reader behind steering_receipt for the create path so that
only a missing file means absence; any other unreadable receipt raises and the
existing callers report the delivery as unresolved without resending or
rewriting the file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: duang777 <duangjl007@gmail.com>

This branch has not been deployed

No deployments
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