Skip to content

fix(#404): re-key inert pty-dedup on (generation, offset) + content-hash - #408

Merged
khaliqgant merged 1 commit into
mainfrom
fix/pty-dedup-rekey
Jul 17, 2026
Merged

khaliqgant merged 1 commit into
mainfrom
fix/pty-dedup-rekey

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jul 17, 2026 •

Copy link
Copy Markdown
Member

Closes #404.

Problem

Relay worker_stream PTY chunks never carry seq/event_id — those are ephemeral and excluded from the daemon replay buffer (relay listen_api.rs ~2800). src/main/pty-dedup.ts keyed identity exclusively on them, so the identity could never match: the layer was structurally inert. It logged "… carry no seq/event_id — replay dedup is blind" once per stream and suppressed nothing. AGENTS.md described a protection that did not exist.

Fix

Re-key identity to the metadata worker_stream chunks do carry:

  • offset — cumulative per-worker byte offset, present 5/5 on PTY streams (absent only on headless workers / pre-offset brokers).
  • generation — the broker event-stream generation the delivering listener attached under (BrokerManager.eventStreamGeneration), now passed into isDuplicatePtyChunk. It scopes the offset so a fresh worker stream's low offset can't collide with a stale remembered one.

Identity = `${generation}:${offset}`. This is exactly what the renderer-side guardrail (pty-buffer-store.ts) already keys on — main was the inert half; the two layers are now consistent.

Invariants (CRITICAL — never drop a real byte; when in doubt, DELIVER)

  • Suppress ONLY on full identity (generation + offset) AND content-hash match within the window.
  • Never drop on content alone (identical consecutive chunks are normal terminal traffic — keystroke echoes, byte-identical TUI repaint frames).
  • Never drop on identity alone (a repeated offset with different bytes is fresh output, not a replay).
  • Offset absent → DELIVER + rate-limited loud console.warn (once/60s per stream) carrying a running counter — no more silently claiming a protection that isn't running.

Review note (worth a look)

worker_stream is excluded from the replay buffer, so PTY chunks are never seq-replayed by the rebind path; a rebind also bumps eventStreamGeneration. Including generation in the key is therefore the safe choice: it makes cross-generation offset-collision false-suppression impossible, at the cost of not suppressing across a generation boundary — which is correct, because a genuine daemon double-emit within one generation still matches, and anything ambiguous is delivered. Net: the layer is now functional and honest rather than inert, and it can never drop a real byte.

Verification

  • npx vitest run src/main/pty-dedup.test.ts → 13 passed (suppress / deliver / loud paths, incl. deterministic-clock rate-limit test).
  • npx vitest run src/main/broker.test.ts → 104 passed (e2e PTY suite rewritten seq→offset).
  • npx tsc --noEmit -p tsconfig.node.json → clean.

🤖 Generated with Claude Code

Review in cubic

@khaliqgant khaliqgant added the no-agent-relay-review Disable agent-relay automated PR review/fixes label Jul 17, 2026
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b708f6a2-78b7-4631-ba58-c70e325606da

📥 Commits

Reviewing files that changed from the base of the PR and between bea859f and b38f8a8.

📒 Files selected for processing (6)
  • .gitignore
  • AGENTS.md
  • src/main/broker.test.ts
  • src/main/broker.ts
  • src/main/pty-dedup.test.ts
  • src/main/pty-dedup.ts

📝 Walkthrough

Walkthrough

PTY chunk deduplication now keys replay detection by generation, offset, and content hash. Missing offsets remain deliverable but emit rate-limited warnings. Broker wiring, documentation, telemetry, lifecycle behavior, and tests were updated, alongside an additional node_modules ignore pattern.

Changes

PTY replay deduplication

Layer / File(s) Summary
Generation-and-offset deduper rules
src/main/pty-dedup.ts, AGENTS.md
PTY replay identity uses generation and offset with a chunk hash; offset-less chunks are delivered and logged with rate limiting.
Broker ingress integration and coverage
src/main/broker.ts, src/main/broker.test.ts
BrokerManager passes event-stream generation to the deduper, with tests for replay, offset changes, stream isolation, and missing offsets.
Deduper telemetry and lifecycle tests
src/main/pty-dedup.test.ts
Tests cover suppression, generation and stream scoping, cleanup, warning rate limiting, and conditional debug logging.

Ignore rule update

Layer / File(s) Summary
Additional dependency directory ignore
.gitignore
Adds node_modules alongside the existing node_modules/ pattern.

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

Sequence Diagram(s)

sequenceDiagram
  participant BrokerManager
  participant PtyChunkDeduper
  participant PTYStream
  BrokerManager->>PtyChunkDeduper: Check worker_stream chunk with generation
  PtyChunkDeduper->>PtyChunkDeduper: Match generation:offset and content hash
  PtyChunkDeduper-->>BrokerManager: Return duplicate decision
  BrokerManager->>PTYStream: Deliver accepted chunk
Loading

Suggested reviewers: willwashburn

Poem

I’m a rabbit guarding streams tonight,
Counting offsets by moonbeam light.
Same bytes replayed? Hop—stay away!
New chunks bounce through without delay.
Missing clues make warnings ring,
While hashes keep watch on everything.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pty-dedup-rekey

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

worker_stream PTY chunks never carry seq/event_id — those are ephemeral and
excluded from the daemon replay buffer, so the previous seq/event_id identity
could never match: the layer was structurally inert (it logged "no seq/event_id"
once per stream and suppressed nothing). worker_stream chunks DO carry a
cumulative per-worker byte `offset` (present 5/5 on PTY streams), so re-key
identity to `${generation}:${offset}` where generation is the broker
event-stream generation the delivering listener attached under. This matches
the renderer-side guardrail (pty-buffer-store.ts) exactly, which already keyed
on (generation, offset)+hash.

Invariants preserved (never drop a real byte):
- suppress ONLY on full identity (generation + offset) AND content-hash match
- never drop on content alone (identical chunks are normal terminal traffic)
- never drop on identity alone (repeated offset + different bytes = fresh output)
- generation scopes the offset so a fresh stream's low offset can't collide
  with a stale remembered one → no cross-generation false suppression
- offset absent → DELIVER + rate-limited loud console.warn + running counter
  (no more silently claiming a protection that isn't running)

Updates AGENTS.md "Duplicate Event Hardening" to describe the real mechanism.
Rewrites pty-dedup.test.ts (suppress/deliver/loud paths) and the broker.test.ts
e2e PTY suite from seq-based to offset-based semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant
khaliqgant force-pushed the fix/pty-dedup-rekey branch from d3878e9 to b38f8a8 Compare July 17, 2026 16:02
@khaliqgant
khaliqgant marked this pull request as ready for review July 17, 2026 16:11
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@khaliqgant
khaliqgant merged commit f04b03a into main Jul 17, 2026
5 checks passed
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@khaliqgant
khaliqgant deleted the fix/pty-dedup-rekey branch July 17, 2026 16:12

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b38f8a8bd9

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/pty-dedup.ts
Comment on lines +89 to +90
const offset =
typeof offsetRaw === 'number' && Number.isFinite(offsetRaw) ? offsetRaw : undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject invalid offsets before deduplication

When a legacy or malformed broker uses a finite sentinel such as offset: -1, this treats it as valid correlation metadata. Two legitimate byte-identical chunks carrying that sentinel then share the same generation/offset/hash and the second is dropped, even though the offset cannot establish replay identity; this is especially harmful for repeated terminal echoes or repaint frames. The renderer already treats negative offsets as absent in pty-buffer-store.ts; apply equivalent validation here and deliver/warn when the offset is not a valid nonnegative byte position.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-agent-relay-review Disable agent-relay automated PR review/fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-key inert pty-dedup.ts on offset+generation+content-hash (fail-loud on missing metadata)

1 participant