Skip to content

Add optional JetStream publishing and a durable pull consumer to the NATS adapter - #612

Merged
gerardrecinto merged 3 commits into
masterfrom
nats-jetstream-publish
Oct 22, 2026
Merged

gerardrecinto merged 3 commits into
masterfrom
nats-jetstream-publish

Conversation

@gerardrecinto

@gerardrecinto gerardrecinto commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Adds opt-in JetStream storage for the NATS bridge's barrier decisions, so a consumer that was offline can catch up.

  • VerifyBridge.WithJetStream creates the stream or checks that an existing one captures the bridge's subject. The default stream covers joltrin.verify.>; a custom-named stream is scoped to the bridge's own subject.
  • Publishing is async. The barrier result is returned unchanged and failures go to OnPublishError. Flush waits for pending acks.
  • CheckAndCommitIdempotent sets a message ID from workflow, step and key, so a retry is stored once inside the duplicate window. The step is in the ID so a refused key reuse isn't dropped as a duplicate.
  • SubscribeJetStream reads decisions through a durable pull consumer with explicit ack, AckWait and MaxDeliver.
  • Without WithJetStream the bridge behaves as before.

Tested against an embedded JetStream server in bridge_test.go.

Thanks,
Gerard Recinto

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: d038141063e193df2d9e3fbcb8043c863d474166
Verdict: FAIL

  • Correctness: In adapters/nats/bridge.go, the idempotencyKey for jetstream.WithMsgID is constructed as b.workflow+"/"+idempotencyKey. While NATS Message IDs are opaque strings, if either b.workflow or idempotencyKey could legitimately contain the / character, it could lead to ambiguity or unexpected collision behavior if the intention was for them to be distinct segments. This is a minor concern as NATS usually treats the entire MsgID string opaquely, but it's worth noting for clarity or if specific parsing were ever intended.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: e33063605965762415a708ff1f305aab50151fb2
Verdict: FAIL

  • Correctness Bug: adapters/nats/bridge.go in WithJetStream, when creating the DefaultStream ("JOLTRIN_VERIFY"), the stream's subject configuration can be inadvertently narrowed. If the VerifyBridge's subject (b.subject) is not covered by the defaultStreamSubjects pattern ("joltrin.verify.>" ), the creation logic replaces the default broad pattern with only b.subject. This means the DefaultStream, intended to be a general-purpose stream for all joltrin.verify decisions, could be created with a highly specific subject filter, preventing other VerifyBridge instances with different subjects from publishing to it. The intent described in the CHANGELOG.md ("captures every subject a VerifyBridge publishes to, whatever the workflow name") is contradicted if the stream is created for a specific, non-default subject. The subjects list for the DefaultStream should ideally include defaultStreamSubjects and add b.subject if it's not already covered, rather than replacing the default.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: e33063605965762415a708ff1f305aab50151fb2
Verdict: FAIL

  • adapters/nats/bridge.go: WithJetStream: When creating the DefaultStream ("JOLTRIN_VERIFY"), if the specific VerifyBridge's subject (b.subject) is not covered by the defaultStreamSubjects ("joltrin.verify.>" ), the stream is created with Subjects set only to b.subject. This contradicts the documentation for DefaultStream, which states it "captures every subject a VerifyBridge publishes to, whatever the workflow name." This behavior means the DefaultStream may not be generally usable by other VerifyBridge instances whose subjects are different or were intended to be covered by defaultStreamSubjects. The DefaultStream should ideally be created to include defaultStreamSubjects and then extend its subjects to cover b.subject if b.subject is not already covered by the default pattern.

This result blocks merge. Push a fix and comment /gemini review. To contest a finding, see the Gemini dispute process in CONTRIBUTING.md.

@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 4c61a6ebd449bce499e656026602698675b15eeb
Verdict: PASS

Here's an evaluation of the pull request diff:

  • adapters/nats/bridge.go: The TestJetStream_DefaultStreamSharedCustomStreamScoped test correctly asserts that creating a new stream with a subject that overlaps with an existing stream's wildcard subject (e.g., joltrin.verify.three with joltrin.verify.>) will result in an error from JetStream due to subject overlap. This behavior is expected from NATS JetStream and the test accurately reflects this.

@gerardrecinto gerardrecinto self-assigned this Oct 22, 2026
@gerardrecinto
gerardrecinto enabled auto-merge (squash) October 22, 2026 05:00
@gerardrecinto

Copy link
Copy Markdown
Collaborator Author

/gemini review

@gerardrecinto
gerardrecinto force-pushed the nats-jetstream-publish branch from 4c61a6e to 480765e Compare October 22, 2026 05:10
@github-actions

Copy link
Copy Markdown

Gemini PR Review

Reviewed commit: 480765e56f198482bd0ebe2b36eeb28c22013f49
Verdict: PASS

There are no actionable findings.

@gerardrecinto
gerardrecinto merged commit 3f9c09f into master Oct 22, 2026
24 of 25 checks passed
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