Skip to content

feat(session-ingest): add feature-flagged direct ingest#4480

Open
pandemicsyn wants to merge 5 commits into
florian/chore/session-ingest-direct-prepfrom
florian/feat/session-ingest-direct-path
Open

feat(session-ingest): add feature-flagged direct ingest#4480
pandemicsyn wants to merge 5 commits into
florian/chore/session-ingest-direct-prepfrom
florian/feat/session-ingest-direct-path

Conversation

@pandemicsyn

@pandemicsyn pandemicsyn commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a feature-flagged, off-by-default direct ingest path for conservative POST /api/session/:sessionId/ingest payloads so eligible requests can persist through one synchronous SessionIngestDO.ingest() RPC before returning.
  • Add fail-closed rollout configuration, deterministic user bucketing, bounded request buffering, strict buffered validation, direct fallback through existing R2 staging and queue enqueue, and terminal structured ingest events.
  • Preserve the legacy queue path for gate misses, missing or invalid Content-Length, oversized/offloaded or multi-chunk envelopes, and direct RPC failures.
  • Add Workers-runtime coverage for the maximum direct RPC envelope and regenerate Worker bindings for the checked-in off-by-default config.
  • Switch session-ingest Wrangler type generation to --include-runtime=false, matching services such as Gastown so checked-in binding diffs exclude bundled runtime declarations.

Verification

  • No manual runtime verification was performed. The feature is disabled by default and route-to-export read-your-writes validation should happen during canary rollout with an allowlisted user.

Visual Changes

N/A

Reviewer Notes

  • This PR is stacked on refactor(session-ingest): prepare direct ingest path #4472 and should stay based on florian/chore/session-ingest-direct-prep until PR 1 merges.
  • Rollout defaults to off: DIRECT_INGEST_PERCENT=0, empty allowlist, and a 4 MiB hard cap. Invalid config closes the gate.
  • Direct RPC uses one attempt only; ambiguous DO failures fall back durably via the existing staging helper and log direct_ingest_fallback.
  • Direct happy path intentionally performs no R2 staging, no queue send, and no permanent item R2 writes.
  • worker-configuration.d.ts now contains only project bindings; runtime types come from @cloudflare/workers-types in tsconfig.json. (matches gas town, and should help cut down on future churn i think)

wtf is this actually doing?

Session ingest is how the CLI/cloud agent sends session data to cloud, including session metadata, messages, parts, diffs, and status changes.

With the feature flag off, which is the default, requests follow the existing durable path: stage the request body in R2, enqueue a queue message, then let the queue consumer validate, chunk, and write items into the per-session Durable Object.

When the feature is enabled for a user and the request is small enough, the route validates the buffered body and writes it to the Durable Object once before returning 200, so reads immediately after the POST can see the new data. Anything outside that conservative envelope falls back to the existing R2 plus queue path.

flowchart TD
  A[POST session ingest] --> B[Validate session access]
  B --> C{Feature gate and envelope eligible?}
  C -- No --> L[Legacy: stage body in R2]
  L --> Q[Queue message]
  Q --> W[Consumer validates and chunks]
  W --> D[SessionIngestDO persists items]
  C -- Yes --> V[Buffer <= 4 MiB and validate]
  V -- Malformed --> R400[400, no writes]
  V -- Empty or no valid items --> R200[200 no-op]
  V -- Offload or multi-chunk --> L
  V -- Eligible --> D2[One SessionIngestDO RPC]
  D2 -- Accepted --> M[Best-effort metadata update]
  D2 -- Tombstoned --> R404[404]
  D2 -- Throws --> L
  M --> OK[200]
Loading

@pandemicsyn pandemicsyn marked this pull request as ready for review July 9, 2026 21:56
@pandemicsyn pandemicsyn marked this pull request as draft July 9, 2026 22:01
@kilo-code-bot

kilo-code-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed the new dark-launched direct session-ingest path (rollout gating, bounded stream reading, DO RPC call, and legacy fallback) and found no high-confidence security, correctness, or breaking-API issues in the changed code.

Files Reviewed (16 files)
  • services/session-ingest/.dev.vars.example
  • services/session-ingest/package.json
  • services/session-ingest/src/env.ts
  • services/session-ingest/src/ingest/bounded-stream-reader.ts
  • services/session-ingest/src/ingest/bounded-stream-reader.test.ts
  • services/session-ingest/src/ingest/direct-ingest-rollout.ts
  • services/session-ingest/src/ingest/direct-ingest-rollout.test.ts
  • services/session-ingest/src/ingest/direct-ingest.ts
  • services/session-ingest/src/ingest/metadata.ts
  • services/session-ingest/src/ingest/stage-and-enqueue.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/test/integration/session-ingest-do.test.ts
  • services/session-ingest/tsconfig.json
  • services/session-ingest/worker-configuration.d.ts (generated)
  • services/session-ingest/wrangler.jsonc

Reviewed by gpt-5.6-sol-20260709 · Input: 19.8K · Output: 1.7K · Cached: 18.6K

Review guidance: REVIEW.md from base branch florian/chore/session-ingest-direct-prep

@pandemicsyn pandemicsyn changed the title feat(session-ingest): add dark-launched direct path feat(session-ingest): add feature-flagged direct ingest Jul 10, 2026
@pandemicsyn pandemicsyn marked this pull request as ready for review July 10, 2026 18:12
@St0rmz1

St0rmz1 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Low, Functional: The direct path surfaces synchronous rejections that the legacy queue path did not. An under-declared Content-Length returns 413 payload_too_large (direct-ingest.ts:160), malformed JSON returns 400 malformed_json (:170), and a tombstoned session returns 404 session_not_found (:252). On the legacy path these cases effectively always returned 200 {success:true}, with malformed or deleted-session writes resolved asynchronously by the consumer.

For selected users this changes what the ingest client can observe. If the CLI/cloud-agent treats a non-200 ingest response as a hard error (aborting the sync or surfacing an error), behavior differs from today for that cohort. Blast radius is limited by the allowlist and 0% default, so this is mainly a rollout gate: please confirm the ingest client tolerates 400/413/404 before widening the percentage.

actualBytes: null,
items: null,
}),
reason: 'declared_bytes_exceeded',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, Functional: This log hardcodes reason: 'declared_bytes_exceeded', but readBoundedStream can return limit: 'configured_cap' as well as 'declared_bytes'. It is unreachable today because the route already rejects contentLength > maxBytes earlier as oversized_body, so the label is accurate for now. If that earlier guard ever moves, this telemetry would silently mislabel a cap overflow. Consider deriving the reason from buffered.limit so the log stays correct regardless of the upstream guards.

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.

2 participants