You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The agent currently re-calls OpenAI on every invocation regardless of whether a newsletter was already generated for the same ticker that day. This ticket implements the skip-if-fresh precheck: before calling the LLM, the agent queries the existing newsletter store for a row matching tickerId within the current calendar day (in the configured IANA timezone). If a qualifying row exists, the agent returns a skipped outcome without calling OpenAI or creating another row.
Scope
In scope:
After config validation and the "no sources" check, add a precheck step that calls the agent-data-api (via the existing SDK) to determine whether a newsletter for tickerId already exists within the current calendar day in config.freshness.timezone.
The "current calendar day" window is: [start-of-day in timezone, end-of-day in timezone) evaluated at invocation time using the IANA timezone from config.
If a qualifying newsletter row exists, return an internal outcome with code skipped_fresh_newsletter_exists (defined in MP-CGA-005) and a descriptive message. Do not call OpenAI.
The precheck must use the typed SDK method (no raw HTTP paths). If agent-data-api does not yet expose a "get most recent newsletter for ticker on date" endpoint, add it as part of this ticket following the agent-data-api-endpoints skill checklist (contract → manifest → handler → SDK → tests → docs).
Update the existing getDataSourcesForTicker source-selection window: document clearly in a code comment whether the skip-if-fresh window and the source-selection window are aligned or differ, and why. (PRD note: today source selection uses UTC "start of day"; this ticket aligns the skip check to the configured IANA timezone. The source window alignment is out of scope — only documentation is required in v1.)
Unit tests for the precheck logic: newsletter exists in window → skipped, newsletter exists but outside window → not skipped, no newsletter exists → proceeds.
Document the delivery-agent coordination behavior in a code comment: when the content-generation step is skipped, a new newsletter row is NOT written; the delivery agent must be designed to use the existing row or skip delivery accordingly.
Out of scope:
A forceRegenerate bypass (PRD phase 2; not in v1).
Changing the source-selection UTC window in getDataSourcesForTicker (alignment deferred; comment only in v1).
Envelope-level skipped: true flag on the HTTP response (current @workspace/agent-runtime envelope does not support a skipped status; the skip is expressed in the diagnostic record written by MP-CGA-007 — no runtime contract change in this ticket).
Acceptance criteria
When a newsletter for tickerId was created within the current calendar day (per config.freshness.timezone), the agent returns { success: false, message: "Newsletter already generated for <tickerId> today (skipped)" } and does not call OpenAI or create a new newsletter row.
When no qualifying newsletter exists, the agent proceeds to the LLM call normally.
The freshness window boundaries are computed using config.freshness.timezone (IANA), not UTC.
The precheck is logged with structured fields (tickerId, timezone, windowStart, windowEnd, existingNewsletterFound: true/false) at the precheck stage; no secrets are logged.
If the agent-data-api endpoint needed for the precheck is new, it is implemented following the agent-data-api-endpoints skill (contract + manifest + handler + SDK + tests).
A code comment at the precheck site documents: (a) the freshness window formula, (b) the fact that source-selection still uses UTC start-of-day (deliberate difference in v1, not a bug), (c) delivery-agent coordination expectation.
Unit tests (following vitest-unit-testing skill): newsletter in window → skip, newsletter outside window → proceed, no newsletter → proceed. Use vi.useFakeTimers() to control "now" deterministically.
pnpm code-quality passes with no new errors.
Dependencies
Depends on: MP-CGA-003 (config schema with freshness group), MP-CGA-005 (outcome taxonomy — skipped_fresh_newsletter_exists code)
Blocks: MP-CGA-007 (writes a skipped diagnostic using this outcome)
Stacked delivery
This ticket introduces new precheck behavior. When config uses default freshness settings (calendar_day, Asia/Jakarta), the check is active immediately after this merges — any re-run of a pipeline for the same ticker on the same calendar day will be skipped. Confirm with the team that the delivery pipeline handles a "no new newsletter row" outcome correctly before merging to production.
No feature flag is required given the skip returns a clear message and the behavior is documented. However, if the team wants to roll out skip-if-fresh gradually, a freshness.enabled: boolean field can be added to the config schema — discuss before implementation.
Merge order: Ship after MP-CGA-003 and MP-CGA-005. Stack this on top of MP-CGA-005's branch (or rebase onto main after both predecessors merge). MP-CGA-007 stacks on top of this.
Notes
Use Temporal (if available in the runtime) or Date with Intl.DateTimeFormat to compute timezone-aware start/end of day. If a date library (e.g. date-fns-tz, luxon) is already used in the monorepo, reuse it rather than adding a new dependency.
The precheck query is a read, not a write — it should be fast (< 100ms in typical conditions). Log a warning if the SDK call takes longer than expected.
If adding a new agent-data-api endpoint for the freshness check (e.g. GET /content-generation/newsletters/latest?tickerId=X&date=YYYY-MM-DD&timezone=...), follow the full agent-data-api-endpoints checklist. Alternatively, if the existing GET /content-generation already returns enough data to filter client-side, that is acceptable — document the choice.
The no_sources skip (from MP-CGA-005) and skipped_fresh_newsletter_exists are both precheck-stage skips. They should produce the same internal AgentOutcome shape with different code fields, so MP-CGA-007 can log them uniformly.
ID: MP-CGA-006 | Priority: P0 | PRD refs: FR3, FR4 | Status: draft
Group: MP Content Generation Agent (
mp-content-generation-agent)Implement skip-if-fresh idempotency
Summary
The agent currently re-calls OpenAI on every invocation regardless of whether a newsletter was already generated for the same ticker that day. This ticket implements the skip-if-fresh precheck: before calling the LLM, the agent queries the existing newsletter store for a row matching
tickerIdwithin the current calendar day (in the configured IANA timezone). If a qualifying row exists, the agent returns a skipped outcome without calling OpenAI or creating another row.Scope
agent-data-api(via the existing SDK) to determine whether a newsletter fortickerIdalready exists within the current calendar day inconfig.freshness.timezone.[start-of-day in timezone, end-of-day in timezone)evaluated at invocation time using the IANA timezone from config.skipped_fresh_newsletter_exists(defined in MP-CGA-005) and a descriptive message. Do not call OpenAI.agent-data-apidoes not yet expose a "get most recent newsletter for ticker on date" endpoint, add it as part of this ticket following theagent-data-api-endpointsskill checklist (contract → manifest → handler → SDK → tests → docs).getDataSourcesForTickersource-selection window: document clearly in a code comment whether the skip-if-fresh window and the source-selection window are aligned or differ, and why. (PRD note: today source selection uses UTC "start of day"; this ticket aligns the skip check to the configured IANA timezone. The source window alignment is out of scope — only documentation is required in v1.)forceRegeneratebypass (PRD phase 2; not in v1).getDataSourcesForTicker(alignment deferred; comment only in v1).skipped: trueflag on the HTTP response (current@workspace/agent-runtimeenvelope does not support askippedstatus; the skip is expressed in the diagnostic record written by MP-CGA-007 — no runtime contract change in this ticket).Acceptance criteria
tickerIdwas created within the current calendar day (perconfig.freshness.timezone), the agent returns{ success: false, message: "Newsletter already generated for <tickerId> today (skipped)" }and does not call OpenAI or create a new newsletter row.config.freshness.timezone(IANA), not UTC.tickerId,timezone,windowStart,windowEnd,existingNewsletterFound: true/false) at the precheck stage; no secrets are logged.agent-data-apiendpoint needed for the precheck is new, it is implemented following theagent-data-api-endpointsskill (contract + manifest + handler + SDK + tests).vitest-unit-testingskill): newsletter in window → skip, newsletter outside window → proceed, no newsletter → proceed. Usevi.useFakeTimers()to control "now" deterministically.pnpm code-qualitypasses with no new errors.Dependencies
freshnessgroup), MP-CGA-005 (outcome taxonomy —skipped_fresh_newsletter_existscode)Stacked delivery
This ticket introduces new precheck behavior. When config uses default freshness settings (
calendar_day,Asia/Jakarta), the check is active immediately after this merges — any re-run of a pipeline for the same ticker on the same calendar day will be skipped. Confirm with the team that the delivery pipeline handles a "no new newsletter row" outcome correctly before merging to production.No feature flag is required given the skip returns a clear message and the behavior is documented. However, if the team wants to roll out skip-if-fresh gradually, a
freshness.enabled: booleanfield can be added to the config schema — discuss before implementation.Merge order: Ship after MP-CGA-003 and MP-CGA-005. Stack this on top of MP-CGA-005's branch (or rebase onto
mainafter both predecessors merge). MP-CGA-007 stacks on top of this.Notes
Temporal(if available in the runtime) orDatewithIntl.DateTimeFormatto compute timezone-aware start/end of day. If a date library (e.g.date-fns-tz,luxon) is already used in the monorepo, reuse it rather than adding a new dependency.agent-data-apiendpoint for the freshness check (e.g.GET /content-generation/newsletters/latest?tickerId=X&date=YYYY-MM-DD&timezone=...), follow the fullagent-data-api-endpointschecklist. Alternatively, if the existingGET /content-generationalready returns enough data to filter client-side, that is acceptable — document the choice.no_sourcesskip (from MP-CGA-005) andskipped_fresh_newsletter_existsare both precheck-stage skips. They should produce the same internalAgentOutcomeshape with differentcodefields, so MP-CGA-007 can log them uniformly.PRD: https://github.com/hyperjumptech/mediapulse/blob/prds/content-generation-agent-prd.md
{ "hyper_pm_id": "01KP1FZ605NFJ71XGYP4F8R58F", "type": "ticket", "parent_ids": { "epic": "01KP1JW157F7WMYR2FEN1VDRXS", "story": "01KP1JW2E08TA8M0CYP3KDT09Q" }, "depends_on": [ "01KP1FZ606H2RFQX2VDHHA8HQH" ] }