Skip to content

CON-114: honor an exact post count in generatePosts - #72

Merged
grsmv merged 6 commits into
mainfrom
fix/generate-posts-exact-count
Jul 19, 2026
Merged

CON-114: honor an exact post count in generatePosts#72
grsmv merged 6 commits into
mainfrom
fix/generate-posts-exact-count

Conversation

@grsmv

@grsmv grsmv commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

CON-114: honor an exact post count in generatePosts

Summary

Asking the Campaign Assistant to "generate 1 post" produced 3. This fixes the assistant's targeted generatePosts tool so an explicit number is honored exactly, while vague requests ("a few", "some") still default to a sensible count.

Root cause

Not the per-call cap (10) and not the generation engine — runGeneratePosts already generates exactly req.Count, and the tool honors any positive in.Count. The problem was the model-facing instructions: both the count schema ("omit to infer from the request (a few = 3)") and the system prompt ("Infer count from the request (\"a few\" = 3)") told the planner to infer the count rather than pass the number the user named. So "generate 1 post" → the model omitted count → the tool fell back to its requested <= 0 → 3 default. (That default is exactly why it was consistently 3.)

Changes

File Change
src/genkit/flows/campaign_assistant/tools.go count schema now: "Exact number of posts to add. When the user names a quantity, set count to that number ("add 1 post"→1, "5 articles"→5). Omit only when the user gives no number; an omitted count defaults to 3." Also extracted the count ble resolveGenerateCount(requested, maxN)helper (behavior unchanged).
src/genkit/flows/campaign_assistant/prompts/claced "Infer count… (a few = 3)" with explicit"set count to the EXACT number the user names; omit only when no number is given (defaults to 3)"; added a "write 1 post"`
example; de-biased the "a few new drafts" framin
src/genkit/flows/campaign_assistant/tools_test.go New TestResolveGenerateCount: exact 1→1 / 5→5, omitted/negative
→3, over-cap clamped.

Behavior after

  • Exact: "generate 1 post" → 1, "5 LinkedIn articles" → 5 (capped at GENERATE_POSTS_MAX, default 10).
  • Vague: "a few" / "some" → 3.

Test plan

  • gofmt clean, go build, go vet
  • go test ./src/genkit/flows/campaign_assistResolveGenerateCount)
  • Template parses (define blocks intact)
  • Manual: in the assistant, "write 1 Threadst; "add a few" creates 3

Notes / limitations

The deterministic parts (engine honors the count; helper defaults + clamps) are unit-tested. Whether the planner reliably emits count=1 is a model-behavior change dions — not unit-testable. If it everunder/over-generates in practice, the fully deterministic backstop is engine-level enforcement (cap persisted posts at Count in the targeted path); happy to add as a follow-

Summary by CodeRabbit

  • Improvements
    • Campaign Assistant now generates the exact number of posts requested, including clearer behavior for unspecified or vague quantities (defaulting to three).
    • Timeframes are applied more precisely; invalid past start dates are rejected, and single-post windows are pinned to the provided start.
    • Post generation persists only up to the intended batch count to prevent accidental over-production.
    • Unsupported platforms are no longer generated; the assistant clearly explains and redirects to targeted options.
  • Tests
    • Updated and added unit tests covering window handling, count resolution, and batch stopping behavior.

Asking the assistant to "generate 1 post" produced 3. The engine already
generates exactly req.Count, and the tool honors any positive in.Count —
but the model-facing instructions told the planner to *infer* the count
and default to "a few = 3", so it omitted count for explicit requests and
the tool fell back to 3.

Make the instructions explicit instead:
- count schema: "set count to the exact number the user names; omit only
  when the user gives no number (defaults to 3)".
- prompt: same guidance, plus a "write 1 post" example, and de-bias the
  "a few new drafts" framing to "one or more".

Also extract the count resolution into resolveGenerateCount (behavior
unchanged) and unit-test it: exact 1->1 and 5->5, omitted/negative->3,
over-cap->clamped.

Both modes now work: exact numbers pass through, vague ("a few") -> 3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 19, 2026

Copy link
Copy Markdown

CON-114

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The Campaign Assistant now enforces explicit or default post counts, resolves publish windows, restricts unsupported platforms, reports generated dates, rejects explicit past dates, and caps persistence for planned batches.

Changes

Campaign post generation

Layer / File(s) Summary
Generation contract and tool behavior
src/genkit/flows/campaign_assistant/prompts/campaign_assistant.tmpl, src/genkit/flows/campaign_assistant/tools.go
Prompt and tool behavior define exact count handling, timeframe resolution, targeted-platform restrictions, single-post date pinning, past-date rejection, and generated publish dates.
Planned batch persistence limits
src/genkit/flows/content_plan/generate.go
Planned batches pass expected counts to streaming generation, which prevents persisting posts beyond each batch while leaving the no-plan path uncapped.
Count and window validation coverage
src/genkit/flows/campaign_assistant/tools_test.go, src/genkit/flows/content_plan/generate_test.go
Tests cover count defaults and caps, single-post windows, explicit past-date errors, and capped or uncapped persistence.

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

Possibly related PRs

  • ogen-app/ogen#68: Directly overlaps Campaign Assistant prompt, tool, and window-generation behavior.
  • ogen-app/ogen#69: Overlaps asset-explanation guidance and GeneratePostsOutput changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.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 matches the main change: generatePosts now honors explicit post counts.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/generate-posts-exact-count

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/genkit/flows/campaign_assistant/tools.go`:
- Around line 447-450: Remove the inline implementation comment above the
resolveGenerateCount call and remove the function docstring for
resolveGenerateCount in src/genkit/flows/campaign_assistant/tools.go at lines
447-450 and 514-521; leave the code behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 57d8aa46-1c71-4333-95db-3aa61da3108a

📥 Commits

Reviewing files that changed from the base of the PR and between 16f7946 and dad81e1.

📒 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

Comment on lines +447 to +450
// Count: honor an explicit number exactly (so "add 1 post" yields 1); fall
// back to 3 ("a few") only when the model omitted it; clamp to the per-call
// cap. Extracted as resolveGenerateCount for unit testing.
count, requested, clamped := resolveGenerateCount(in.Count, st.maxGeneratePosts)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove comments describing WHAT the code does.

These comments merely detail implementation mechanics. As per path instructions, default to no comments and avoid describing WHAT the code does.

  • src/genkit/flows/campaign_assistant/tools.go#L447-L450: Remove the inline comment.
  • src/genkit/flows/campaign_assistant/tools.go#L514-L521: Remove the function docstring.
Proposed fix
-	// Count: honor an explicit number exactly (so "add 1 post" yields 1); fall
-	// back to 3 ("a few") only when the model omitted it; clamp to the per-call
-	// cap. Extracted as resolveGenerateCount for unit testing.
 	count, requested, clamped := resolveGenerateCount(in.Count, st.maxGeneratePosts)
-// resolveGenerateCount maps the model-supplied count to the number of posts the
-// generatePosts tool will actually create. An explicit positive count is honored
-// exactly — "add 1 post" yields 1, not the "a few" default — while a missing or
-// non-positive count falls back to 3 ("a few"). Anything above the per-call cap
-// (maxN, default 10) is clamped down. Returns the effective count, the requested
-// count after the default is applied (surfaced to the model as RequestedCount),
-// and whether the request was clamped.
 func resolveGenerateCount(requested, maxN int) (count, requestedOut int, clamped bool) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Count: honor an explicit number exactly (so "add 1 post" yields 1); fall
// back to 3 ("a few") only when the model omitted it; clamp to the per-call
// cap. Extracted as resolveGenerateCount for unit testing.
count, requested, clamped := resolveGenerateCount(in.Count, st.maxGeneratePosts)
count, requested, clamped := resolveGenerateCount(in.Count, st.maxGeneratePosts)
Suggested change
// Count: honor an explicit number exactly (so "add 1 post" yields 1); fall
// back to 3 ("a few") only when the model omitted it; clamp to the per-call
// cap. Extracted as resolveGenerateCount for unit testing.
count, requested, clamped := resolveGenerateCount(in.Count, st.maxGeneratePosts)
func resolveGenerateCount(requested, maxN int) (count, requestedOut int, clamped bool) {
📍 Affects 1 file
  • src/genkit/flows/campaign_assistant/tools.go#L447-L450 (this comment)
  • src/genkit/flows/campaign_assistant/tools.go#L514-L521
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/genkit/flows/campaign_assistant/tools.go` around lines 447 - 450, Remove
the inline implementation comment above the resolveGenerateCount call and remove
the function docstring for resolveGenerateCount in
src/genkit/flows/campaign_assistant/tools.go at lines 447-450 and 514-521; leave
the code behavior unchanged.

Source: Path instructions

grsmv and others added 3 commits July 19, 2026 13:30
The generation model can over-produce (stream more posts than a batch
asked for), and generatePostsStreaming persisted every valid post — so a
targeted "generate 1 post" request yielded 3. Add an expectedCount to the
streaming persist loop (withinCount gate): the batched path passes the
batch's planned PostCount, the count-less fallback passes 0 (uncapped).

Deterministic guarantee: count=1 -> one batch of PostCount 1 -> at most 1
post persisted, regardless of how many the model streams. Also stops the
full content-plan path from exceeding a batch's planned size.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"generate 1 draft for Aug 20" set windowStart=Aug 20 and omitted
windowEnd, which resolveWindow expands to a 14-day window — so posts
spread across the range (e.g. Aug 29) instead of landing on Aug 20.
Instruct the model (prompt + windowEnd schema) to set both bounds to the
same date for a single specific date.
Haiku (the planner) omits `count` for "generate 1 post", so the tool's
resolveGenerateCount fell through to its default of 3 — and with the
engine cap in place, a request for 1 still produced 3. Default a
missing/zero count to 1 (the safe minimum) instead: an omitting model can
no longer over-produce, and the planner is told to pass an explicit 3 for
a vague "a few". Combined with the batch cap, "generate 1" now yields
exactly 1 unless the model explicitly asks for more.

Trade-off: a vague "a few" with no number now depends on the model
passing 3; if it omits, one post is created (safe under-, not over-,
delivery).
"generate 1 for Jul 22" landed on Jul 29: the model set windowStart=Jul 22
and omitted windowEnd, resolveWindow derived a 14-day window (->Aug 5), and
the lone post was placed at the window's midpoint. The prompt nudge to set
windowEnd=windowStart wasn't reliable.

Add singlePostWindowEnd: when count==1 and the user gave no explicit end,
collapse the window to the start day so newPostValidator pins the publish
date exactly. Explicit ends and multi-post requests keep their range.
Two problems surfaced testing "generate 1 for Jul 17" while today is
Jul 19: the past date was silently clamped to today (post landed on
Jul 19), and the assistant's reply invented dates ("Jul 12", wrong
counts) because the tool output carried no dates for the model to report.

- Reject past dates instead of clamping (per product decision). The
  planner is told to catch a before-today date and reply conversationally
  ("that date has passed — pick today or later"); resolveWindow rejects an
  explicit past start/end as the backstop so no past-dated draft slips in.
- Stop the date confabulation: GeneratePostsOutput now returns the actual
  publish dates of the created posts, and the prompt tells the model to
  report only the counts/dates the tool returned, never invent them.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/genkit/flows/campaign_assistant/tools.go (1)

448-451: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove comments describing WHAT the code does.

As per path instructions, default to no comments and avoid explaining what the code does. The previous review flagged similar comments.

  • src/genkit/flows/campaign_assistant/tools.go#L448-L451: Remove this comment block.
  • src/genkit/flows/campaign_assistant/tools.go#L453-L453: Remove this comment.
  • src/genkit/flows/campaign_assistant/tools.go#L521-L528: Remove this docstring.
  • src/genkit/flows/campaign_assistant/tools_test.go#L186-L188: Remove this comment block.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/genkit/flows/campaign_assistant/tools.go` around lines 448 - 451, Remove
the descriptive comments around resolveGenerateCount in
src/genkit/flows/campaign_assistant/tools.go lines 448-451 and 453, remove the
docstring at lines 521-528, and remove the corresponding comment block in
src/genkit/flows/campaign_assistant/tools_test.go lines 186-188; leave the
implementation and tests unchanged.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/genkit/flows/campaign_assistant/tools.go`:
- Around line 533-535: Change the fallback in the requested-count handling to
default to 3 instead of 1. Update the related wantCount and wantReq assertions
in src/genkit/flows/campaign_assistant/tools_test.go lines 200-201 to expect 3.
- Line 462: Inline the single-use helper logic and remove its dedicated tests:
in src/genkit/flows/campaign_assistant/tools.go:462-462, replace
singlePostWindowEnd with the count and WindowEnd condition; remove
singlePostWindowEnd at src/genkit/flows/campaign_assistant/tools.go:544-554 and
TestSinglePostWindowEnd at
src/genkit/flows/campaign_assistant/tools_test.go:214-234. In
src/genkit/flows/content_plan/generate.go:392-394, inline the
expectedCount/posts length check; remove withinCount at
src/genkit/flows/content_plan/generate.go:533-538 and TestWithinCount at
src/genkit/flows/content_plan/generate_test.go:5-26.

---

Duplicate comments:
In `@src/genkit/flows/campaign_assistant/tools.go`:
- Around line 448-451: Remove the descriptive comments around
resolveGenerateCount in src/genkit/flows/campaign_assistant/tools.go lines
448-451 and 453, remove the docstring at lines 521-528, and remove the
corresponding comment block in src/genkit/flows/campaign_assistant/tools_test.go
lines 186-188; leave the implementation and tests unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c64ed1ad-cade-47b7-8d02-b72070dc89e7

📥 Commits

Reviewing files that changed from the base of the PR and between dad81e1 and d3be05f.

📒 Files selected for processing (5)
  • src/genkit/flows/campaign_assistant/prompts/campaign_assistant.tmpl
  • src/genkit/flows/campaign_assistant/tools.go
  • src/genkit/flows/campaign_assistant/tools_test.go
  • src/genkit/flows/content_plan/generate.go
  • src/genkit/flows/content_plan/generate_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/genkit/flows/campaign_assistant/prompts/campaign_assistant.tmpl

// for Jul 22" must land ON Jul 22 — not the window's midpoint. When the model
// gave only a start for a single post, collapse the derived range to that day
// so validation pins the publish date exactly (CON-114).
windowEnd = singlePostWindowEnd(windowStart, windowEnd, in.WindowEnd, count)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Inline trivial helpers to avoid premature abstraction.

As per path instructions, avoid premature abstractions; three similar lines is better than a speculative helper. singlePostWindowEnd and withinCount are single-use, one-line conditionals.

  • src/genkit/flows/campaign_assistant/tools.go#L462-L462: Inline the condition: if count == 1 && in.WindowEnd == "" { windowEnd = windowStart }.
  • src/genkit/flows/campaign_assistant/tools.go#L544-L554: Remove the singlePostWindowEnd function.
  • src/genkit/flows/campaign_assistant/tools_test.go#L214-L234: Remove TestSinglePostWindowEnd.
  • src/genkit/flows/content_plan/generate.go#L392-L394: Inline the check: if expectedCount > 0 && len(posts) >= expectedCount { return }.
  • src/genkit/flows/content_plan/generate.go#L533-L538: Remove the withinCount function.
  • src/genkit/flows/content_plan/generate_test.go#L5-L26: Remove TestWithinCount.
📍 Affects 4 files
  • src/genkit/flows/campaign_assistant/tools.go#L462-L462 (this comment)
  • src/genkit/flows/campaign_assistant/tools.go#L544-L554
  • src/genkit/flows/campaign_assistant/tools_test.go#L214-L234
  • src/genkit/flows/content_plan/generate.go#L392-L394
  • src/genkit/flows/content_plan/generate.go#L533-L538
  • src/genkit/flows/content_plan/generate_test.go#L5-L26
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/genkit/flows/campaign_assistant/tools.go` at line 462, Inline the
single-use helper logic and remove its dedicated tests: in
src/genkit/flows/campaign_assistant/tools.go:462-462, replace
singlePostWindowEnd with the count and WindowEnd condition; remove
singlePostWindowEnd at src/genkit/flows/campaign_assistant/tools.go:544-554 and
TestSinglePostWindowEnd at
src/genkit/flows/campaign_assistant/tools_test.go:214-234. In
src/genkit/flows/content_plan/generate.go:392-394, inline the
expectedCount/posts length check; remove withinCount at
src/genkit/flows/content_plan/generate.go:533-538 and TestWithinCount at
src/genkit/flows/content_plan/generate_test.go:5-26.

Source: Path instructions

Comment on lines +533 to +535
if requested <= 0 {
requested = 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Set the default count to 3.

The PR objectives state that omitted or vague counts should default to 3, but the code falls back to 1.

  • src/genkit/flows/campaign_assistant/tools.go#L533-L535: Change requested = 1 to requested = 3.
  • src/genkit/flows/campaign_assistant/tools_test.go#L200-L201: Update the wantCount and wantReq assertions in the test cases to 3.
📍 Affects 2 files
  • src/genkit/flows/campaign_assistant/tools.go#L533-L535 (this comment)
  • src/genkit/flows/campaign_assistant/tools_test.go#L200-L201
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/genkit/flows/campaign_assistant/tools.go` around lines 533 - 535, Change
the fallback in the requested-count handling to default to 3 instead of 1.
Update the related wantCount and wantReq assertions in
src/genkit/flows/campaign_assistant/tools_test.go lines 200-201 to expect 3.

@grsmv
grsmv merged commit d1c6ab6 into main Jul 19, 2026
6 checks passed
@grsmv
grsmv deleted the fix/generate-posts-exact-count branch July 31, 2026 12:44
@coderabbitai coderabbitai Bot mentioned this pull request Aug 6, 2026
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