Skip to content

CON-216: stop campaign_assistant replaying stale prior instructions - #115

Merged
grsmv merged 1 commit into
mainfrom
fix/con-216-campaign-assistant-stale-instruction-misroute
Aug 15, 2026
Merged

CON-216: stop campaign_assistant replaying stale prior instructions#115
grsmv merged 1 commit into
mainfrom
fix/con-216-campaign-assistant-stale-instruction-misroute

Conversation

@grsmv

@grsmv grsmv commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Fixes CON-216"post_assistant seems stuck in previous user message instructions in case of an internal error."

Problem

A user asked a read-only question ("tell me about assets attached to this campaign"), but the cheap Haiku planner:

  1. Spuriously fired generatePosts (a write action nobody asked for) alongside the correct askCampaignAssets, and
  2. Reused a date (2026-07-28) that appears only in earlier conversation history, not the current message.

resolveWindow rejected the past date with a Go error, which aborts the whole genkit Generate turn → surfaced to the user as a raw red model call failed: … is in the past and discarded the good asset answer.

What was already fixed

  • Layer 2 (the crash) was resolved by CON-215: generatePosts/draftPost now fail soft on a past date / non-target platform / unknown phase (a zero-post warning, not a turn-aborting error), leaving the turn conversational. The red 502 in the screenshot can no longer recur.
  • CON-213 added max-turns graceful recovery + the one-heavy-action-per-turn latch.

This PR addresses Layer 1 — the root-cause mis-routing named in the ticket title — plus a slot-leak the mis-route exposed.

Changes

  • Prompt discipline (prompts/campaign_assistant.tmpl): act only on the current message; treat history as background, not a queue to replay. Questions are read-only and must never trigger a write/generation tool; resolve dates only from the current request against today — never reuse a date from an earlier message.
  • Late heavy-action reservation (toolGeneratePosts): reserve the turn's single heavy slot only after the user-correctable validations pass, mirroring the CON-207 draftPost refactor. A mis-routed/invalid generatePosts that fails soft no longer burns the slot for a legitimate heavy action in the same turn. ensureCampaignAssetUse (which persists UseAssets to the DB) also moved past the reservation, so a skipped/soft-failed turn never writes state for work that won't run.
  • Regression test (tools_test.go): TestGeneratePosts_HeavySlotOnlyBurnedOnSuccess — a soft-failed generatePosts leaves the slot claimable; a successful one claims it (second heavy reservation turned away).

Why no server-side date guard

Dates are model-resolved and history is legitimately needed (draftPost sources its material from it), and the crash path is already handled. Prompt discipline + the reservation reorder is the proportionate fix.

Testing

  • go build, go vet, and go test -race ./src/genkit/flows/campaign_assistant/ all pass (existing CON-213/214/215 tests still green).
  • Template parses with both system/context blocks intact.

Summary by CodeRabbit

  • Bug Fixes
    • Improved the assistant’s handling of conversation context, ensuring actions are based only on the current request.
    • Invalid or incomplete post-generation requests now fail gracefully without affecting campaign state.
    • Heavy actions are reserved only after request validation succeeds, preventing valid follow-up actions from being blocked.

The Haiku planner could re-issue a write tool (e.g. generatePosts) for a
read-only question, reusing a date pulled from conversation history rather
than the current message — the "stuck in previous user message" bug. Two
targeted fixes:

- Prompt discipline (campaign_assistant.tmpl): act only on the current
  message; treat history as background, not a queue to replay. Questions are
  read-only and never trigger a write/generation tool; resolve dates only
  from the current request against today, never reuse a date from history.
- Late heavy-action reservation (toolGeneratePosts): reserve the turn's
  single heavy slot only after the user-correctable validations pass,
  mirroring draftPost (CON-207). A mis-routed/invalid generatePosts that
  fails soft (CON-215) no longer burns the slot for a legitimate heavy
  action in the same turn. ensureCampaignAssetUse also moved past the
  reservation so a skipped turn never flips UseAssets in the DB.

Layer 2 (the past-date abort surfacing as a raw "model call failed") was
already resolved by CON-215's soft-fail; this addresses the root-cause
mis-routing. Adds a regression test that the heavy slot is burned only on a
successful generation.
@linear-code

linear-code Bot commented Aug 15, 2026

Copy link
Copy Markdown

CON-216

@grsmv
grsmv deployed to testing August 15, 2026 11:37 — with GitHub Actions Active
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a83b914a-b27d-43a8-822e-640cc2f286b9

📥 Commits

Reviewing files that changed from the base of the PR and between 9d96909 and a8e492a.

📒 Files selected for processing (3)
  • src/genkit/flows/campaign_assistant/prompts/campaign_assistant.tmpl
  • src/genkit/flows/campaign_assistant/tools.go
  • src/genkit/flows/campaign_assistant/tools_test.go

Walkthrough

The campaign assistant now uses only current-message instructions for write actions and dates. generatePosts validates requests before consuming the per-turn heavy-action slot or changing asset state.

Changes

Campaign assistant behavior

Layer / File(s) Summary
Current-message request scoping
src/genkit/flows/campaign_assistant/prompts/campaign_assistant.tmpl
The prompt treats conversation history as background, handles questions read-only, requires explicit current-message write requests, and resolves dates from the current request.
Validated heavy-action reservation
src/genkit/flows/campaign_assistant/tools.go, src/genkit/flows/campaign_assistant/tools_test.go
generatePosts validates inputs before reserving the heavy-action slot. Asset-use initialization occurs after reservation. Tests cover soft failure, successful reservation, and rejection of a second reservation.

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

Merge Risk: ⚪ Minimal · up to a8e49

The change limits stale-instruction replay and prevents invalid generation attempts from consuming the turn's heavy-action slot; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant UserMessage
  participant CampaignAssistant
  participant generatePosts
  participant HeavyActionSlot
  participant GenerationFlow
  UserMessage->>CampaignAssistant: current request
  CampaignAssistant->>generatePosts: explicit generation request
  generatePosts->>generatePosts: validate request
  generatePosts->>HeavyActionSlot: reserve after valid checks
  generatePosts->>GenerationFlow: generate posts
  GenerationFlow-->>CampaignAssistant: generated posts
Loading

Possibly related PRs

  • ogen-app/ogen#111: Introduced the per-turn heavy-action latch that this change refines.
  • ogen-app/ogen#113: Modified generatePosts validation and soft-failure behavior.
  • ogen-app/ogen#114: Modified the Campaign Assistant prompt and tool handling refined here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: preventing campaign_assistant from replaying stale instructions from conversation history.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/con-216-campaign-assistant-stale-instruction-misroute

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.

@grsmv grsmv added the to test label Aug 15, 2026
@grsmv
grsmv deployed to testing August 15, 2026 11:41 — with GitHub Actions Active
@grsmv
grsmv deployed to testing August 15, 2026 11:43 — with GitHub Actions Active
@ogen-app ogen-app deleted a comment from coderabbitai Bot Aug 15, 2026
@grsmv

grsmv commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@grsmv
grsmv merged commit cf2c146 into main Aug 15, 2026
6 checks passed
@grsmv
grsmv deleted the fix/con-216-campaign-assistant-stale-instruction-misroute branch August 15, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant