fix(marketing): decouple dashboard + auto-schedule from publish-stage payload shape#723
Merged
Merged
Conversation
…rom publish-stage shape Plan + plan-eng-review + Codex outside-voice findings for the Aries-side fix. Part A (auto-schedule fallback) cleared; Part B1 (narrow dashboard safety net) chosen over full DB reconstruction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Jq6YvGxzP23enohEiMpmD
… shape Hermes now runs publish as a separate stage emitting a strategy-shaped placeholder instead of a publish-shaped artifact. Two Aries surfaces broke: **Part A — default-cadence auto-schedule** Remove the hard early-return in `autoScheduleApprovedPostsForJob` when `weeklySchedule.length === 0`. When no explicit schedule is present, fall through to a new default-cadence path: ordinal k → baseDate + (k-1) days at the platform's default hour. baseDate = max(now+10min, campaignStart) so the first slot is never in the past. Pure exported helper `computeDefaultCadenceSlots` (tested) + DB-writing wrapper `autoDefaultCadenceSchedulePosts` mirror the existing auto-schedule pair. Exported `parsePostNumberFromIdempotencyKey` so ordinals can be recovered from post rows for the default-cadence input. **Part B1 — dashboard safety net** In `buildSocialContentJobContentInternal`, ONLY when both payload-derived posts and assets are empty, supplement from DB: creative_assets rows (served_asset_ref as previewUrl) and posts status counts (approved→readyToPublish, scheduled→scheduled, published→live). STRICT GATE: byte-identical for healthy jobs with a real publish payload. Moved `queryProductionCreativeAssets` to a neutral module `production-assets-query.ts` to avoid circular import (workspace-views.ts already imports dashboard-content.ts). Added NaN guard on the B1 pool.query call to match the pattern in queryProductionCreativeAssets and countPublishedPostsForJob — prevents warning bleed into concurrent test warn-count assertions. Added projection invalidation call in `synthesizePublishPostsOnCompletion` so the dashboard_list_projection is rebuilt after posts+creative_assets are written to DB. Gates: npm run verify (214/214), npm run validate:social-content (92/92). Closes the auto-schedule + dashboard zero-posts regressions introduced by the Hermes publish-stage decoupling. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Jq6YvGxzP23enohEiMpmD
Contributor
There was a problem hiding this comment.
Pull request overview
This PR decouples Aries’ dashboard rendering and post auto-scheduling from the Hermes publish-stage primary_output payload shape, so both features keep working even when the publish stage emits a strategy-shaped placeholder (no posts[] / no schedule[]) while the DB rows (posts, creative_assets) are still correct.
Changes:
- Add a default-cadence auto-scheduling fallback when
weekly_scheduleis empty (one content piece per day by ordinal), keeping existing feature-flag gates and idempotent upserts. - Add a narrow “B1” dashboard safety net: when payload-derived posts/assets are both empty but DB rows exist, supplement thumbnail assets from
creative_assetsand non-zero post counts fromposts. - Move the production-assets DB query helper to a neutral module to avoid circular imports, and recompute the dashboard list projection after synthesize/ingest so DB-backed supplementation is visible immediately.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
backend/marketing/hermes-callbacks.ts |
Adds default-cadence scheduling fallback when publish payload has no schedule; triggers projection recompute after synthesize/ingest. |
backend/marketing/auto-schedule.ts |
Implements computeDefaultCadenceSlots + DB-writer wrapper for default-cadence scheduling. |
backend/marketing/dashboard-content.ts |
Implements B1 dashboard DB-supplementation (assets + counts) behind a strict empty-payload gate. |
backend/marketing/production-assets-query.ts |
New neutral module for job-scoped creative_assets query (reused by dashboard + workspace view). |
backend/marketing/workspace-views.ts |
Switches to importing the moved production-assets query helper. |
tests/marketing/default-cadence-slots.test.ts |
Adds unit coverage for default-cadence slot computation and a callback-level wiring test. |
tests/marketing/publish-stage-decoupling-b1.test.ts |
Adds coverage for the B1 safety net behavior and projection recompute behavior. |
docs/plans/2026-06-24-aries-publish-stage-decoupling.md |
Documents the reasoning, options, and chosen approach (A + B1). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+486
to
+498
| if (dayInstant > windowEnd) { | ||
| skipped.push({ | ||
| row: { ...row, recommendedDay: null }, | ||
| reason: `overflow_beyond_window:${row.ordinal}`, | ||
| }); | ||
| console.info('[auto-schedule] default-cadence slot overflow — skipping', { | ||
| ordinal: row.ordinal, | ||
| platform: row.platform, | ||
| dayInstant: dayInstant.toISOString(), | ||
| windowEnd: windowEnd.toISOString(), | ||
| }); | ||
| continue; | ||
| } |
Comment on lines
+55
to
+59
| import { | ||
| SELECT_PRODUCTION_CREATIVE_ASSETS_SQL, | ||
| type ProductionCreativeAssetRow, | ||
| queryProductionCreativeAssets, | ||
| } from './production-assets-query'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Brendan intentionally split publish into a separate Hermes publish-stage. That stage currently emits a strategy-shaped placeholder — publish
primary_output = {stage:"strategy", content_package}— instead of the publish-shaped artifact the old combined pipeline produced ({stage:"publish", posts, platform_strategy, campaign_cadence/schedule, …}). Two Aries surfaces read that shape, so both break even though the DB is correct:/dashboard/postsrenders campaigns with 0 posts / no images — the dashboard view assembles posts/assets from stage payloads, which are now empty.autoScheduleApprovedPostsForJobhard-bails when the publish schedule is empty.The DB source-of-truth (
posts+creative_assets) is correct and publishable regardless (FB post 220 went live from exactly this data). This PR decouples both surfaces from the publish-stage payload shape so they work no matter what that stage emits.Plan + review:
docs/plans/2026-06-24-aries-publish-stage-decoupling.md(reviewed via/plan-eng-review+ Codex outside voice; the full-DB-rebuild option was rejected for breaking the projection-cache invariant — the narrow B1 safety net was chosen instead).What
Part A — auto-schedule fallback (
hermes-callbacks.ts,auto-schedule.ts)computeDefaultCadenceSlotshelper (absolute day offsets, so ordinal order == calendar order regardless of start weekday; IG+FB of an ordinal share a day).computeAutoScheduleSlots"default hour already passed → jump a week" behavior. A one-daybaseDateroll-forward guarantees piece 1 lands on the first future-hour day (never silently dropped).ARIES_AUTO_APPROVE_MARKETING_PIPELINE/ARIES_AUTOSCHEDULE_ON_APPROVALflags — behavior only changes for operators already opted into auto-scheduling. Idempotent (upsert ON CONFLICT(post_id)).Part B1 — narrow dashboard safety net (
dashboard-content.ts, newproduction-assets-query.ts)creative_assets) and post counts (fromposts). Strict empty-gate → a healthy job with a real publish payload is byte-identical (no double-count).queryProductionCreativeAssetsto a neutral module to avoid a circular import.dashboard_list_projectionrecompute after synthesize/ingest (the cache freshness gate doesn't cover DB-row changes that don't bump the job doc).Tests
tests/marketing/default-cadence-slots.test.ts— pure-helper coverage incl. the after-hours roll-forward (all 14 posts scheduled, 0 dropped, ordinal 1 = tomorrow) and window overflow.tests/marketing/publish-stage-decoupling-b1.test.ts— B1 fires on empty payload + DB rows; does NOT fire on a healthy payload; projection recompute.npm run verify214/214,npm run validate:social-content92/92, typecheck clean (independently re-run).Activation note
Part A activates under the auto-approve / auto-schedule-on-approval flags (which are ON in prod), and B1 activates whenever a completed job's publish payload is empty — so this takes effect for new jobs on deploy. It does not retroactively touch existing rows.
🤖 Generated with Claude Code