Skip to content

fix(marketing): decouple dashboard + auto-schedule from publish-stage payload shape#723

Merged
DeliciousHouse merged 2 commits into
masterfrom
fix/aries-publish-stage-decoupling
Jun 25, 2026
Merged

fix(marketing): decouple dashboard + auto-schedule from publish-stage payload shape#723
DeliciousHouse merged 2 commits into
masterfrom
fix/aries-publish-stage-decoupling

Conversation

@DeliciousHouse

Copy link
Copy Markdown
Owner

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:

  1. /dashboard/posts renders campaigns with 0 posts / no images — the dashboard view assembles posts/assets from stage payloads, which are now empty.
  2. Approved posts never auto-scheduleautoScheduleApprovedPostsForJob hard-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)

  • When the publish stage emits no schedule, approved posts auto-schedule one content-piece per day via a new pure computeDefaultCadenceSlots helper (absolute day offsets, so ordinal order == calendar order regardless of start weekday; IG+FB of an ordinal share a day).
  • Avoids two traps the weekday-name path has: start-weekday dependence, and the computeAutoScheduleSlots "default hour already passed → jump a week" behavior. A one-day baseDate roll-forward guarantees piece 1 lands on the first future-hour day (never silently dropped).
  • Gated behind the existing ARIES_AUTO_APPROVE_MARKETING_PIPELINE / ARIES_AUTOSCHEDULE_ON_APPROVAL flags — 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, new production-assets-query.ts)

  • When the publish payload yields zero posts AND zero assets but the DB has rows, supplement the dashboard with image previews (from creative_assets) and post counts (from posts). Strict empty-gate → a healthy job with a real publish payload is byte-identical (no double-count).
  • Moves queryProductionCreativeAssets to a neutral module to avoid a circular import.
  • Adds a dashboard_list_projection recompute after synthesize/ingest (the cache freshness gate doesn't cover DB-row changes that don't bump the job doc).
  • Does not reconstruct publishItems/cadence/provenance (out of scope; pairs with fixing the Hermes publish-stage contract for full-fidelity cards).

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 verify 214/214, npm run validate:social-content 92/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

hermes-agent and others added 2 commits June 24, 2026 23:54
…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
Copilot AI review requested due to automatic review settings June 25, 2026 01:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_schedule is 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_assets and non-zero post counts from posts.
  • 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';
@DeliciousHouse DeliciousHouse merged commit 9b923b4 into master Jun 25, 2026
6 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.

3 participants