Skip to content

feat: create a slack post when a video is created or updated#9078

Merged
tanflem merged 36 commits into
mainfrom
tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated
May 18, 2026
Merged

feat: create a slack post when a video is created or updated#9078
tanflem merged 36 commits into
mainfrom
tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated

Conversation

@tanflem
Copy link
Copy Markdown
Contributor

@tanflem tanflem commented Apr 24, 2026

Summary by CodeRabbit

  • New Features

    • Slack notifications for video create/update plus scheduled weekly summary and bulk summary posts (Mondays 09:00 UTC) with manual CLI runners.
  • Tests

    • New comprehensive test suites for Slack notifications, weekly/bulk summary flows, renderers, and mutation hooks; improved test environment cleanup.
  • Chores

    • Added Slack environment vars, project targets, worker job startup, and scheduling configuration; updated ignore list.
  • Bug Fixes

    • Improved importer filename parsing to prevent ambiguous version/extra captures.

@tanflem tanflem requested a review from Kneesal April 24, 2026 18:20
@tanflem tanflem self-assigned this Apr 24, 2026
@linear
Copy link
Copy Markdown

linear Bot commented Apr 24, 2026

@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.9: unable to verify checksums signature: openpgp: key expired


@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Slack notifications and weekly summaries to api-media: immediate video mutation alerts and weekly per-video and bulk Slack reports, plus config/helpers, report loaders/renderers, worker scheduling and CLI runners, tests, and infra/Nx entries for env vars and run targets.

Changes

Cohort / File(s) Summary
Infrastructure & Nx
apis/api-media/infrastructure/locals.tf, apis/api-media/project.json, .nxignore
Expose new Slack env var names and add two Nx targets for weekly and bulk Slack summary runners; update .nxignore.
Slack core & config
apis/api-media/src/lib/slack/chatPostMessage.ts, apis/api-media/src/lib/slack/config.ts, apis/api-media/src/lib/slack/index.ts
Add low-level Slack POST helper, env-driven bot/channel config loader, and barrel exports.
Immediate mutation notifier
apis/api-media/src/lib/slack/videoMutationNotification.ts, apis/api-media/src/lib/slack/videoMutationNotification.spec.ts, apis/api-media/src/schema/video/video.ts, apis/api-media/src/schema/video/video.spec.ts
Fire-and-forget notifier for video create/update; resolver hooks added; tests for message composition and error/env handling.
Weekly per-video reporting
apis/api-media/src/lib/videoSlackReport.ts, apis/api-media/src/lib/videoSlack.ts, apis/api-media/src/lib/videoSlackRenderer.ts, apis/api-media/src/lib/videoSlack.spec.ts, apis/api-media/src/scripts/run-weekly-video-slack-summary.ts
New report types/loader, window utilities, orchestrator validating window and posting chunked ASCII tables to Slack, CLI runner and tests.
Bulk weekly reporting
apis/api-media/src/lib/videoSlackBulkReport.ts, apis/api-media/src/lib/videoSlackBulkRenderer.ts, apis/api-media/src/lib/videoSlackBulk.spec.ts, apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts
Bulk report loader and renderer grouping by month, truncating language lists, chunking messages, sequential posting with delays; CLI runner and tests.
Worker scheduling & service
apis/api-media/src/workers/videoSlackSummary/config.ts, apis/api-media/src/workers/videoSlackSummary/service/service.ts, apis/api-media/src/workers/videoSlackSummary/index.ts, apis/api-media/src/workers/server.ts
New worker config (cron Mon 09:00 UTC), service wrapper calling summary, index re-exports, and dynamic import/start in worker bootstrap.
Renderer & posting logic
apis/api-media/src/lib/videoSlackRenderer.ts, apis/api-media/src/lib/videoSlackBulkRenderer.ts
Table rendering, per-month grouping, chunking to Slack-size limits, sequential posting with delay, and early-exit on failed post.
Report loaders & helpers
apis/api-media/src/lib/videoSlackReport.ts, apis/api-media/src/lib/videoSlackBulkReport.ts
SQL/Prisma report loaders producing per-video and bulk rows, language name resolution, change-date and isNew logic, sorting and aggregation rules.
Tests & specs (misc)
apis/api-media/src/lib/slack/*.spec.ts, apis/api-media/src/lib/videoSlack*.spec.ts, apis/api-media/src/lib/videoSlackBulk.spec.ts, apps/video-importer/src/importers/*.spec.ts
Extensive tests for Slack flows and summary behaviors; importer tests updated to preserve/restore SKIP_ENV_VALIDATION.
Filename regex adjustments
apps/video-importer/src/importers/video.ts, apps/video-importer/src/importers/subtitle.ts
Tightened filename regexes: final ---<version> part now optional exactly once (prevents repeated captures); tests adapted for env cleanup.
Scripts & CLI
apis/api-media/src/scripts/run-weekly-video-slack-summary.ts, apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts
New CLI runners to compute/validate weekly windows and invoke per-video and bulk summary flows.

Sequence Diagram(s)

sequenceDiagram
    participant GraphQL as GraphQL Resolver
    participant Notifier as notifyVideoSlackOfMutation
    participant Config as getMediaDataLangSlackConfig
    participant Msg as slackChatPostMessage
    participant SlackAPI as Slack API

    GraphQL->>Notifier: call with video, user, kind
    Notifier->>Config: load token & channel from env
    Config-->>Notifier: config | null
    alt Config available
        Notifier->>Msg: build blocks & POST payload
        Msg->>SlackAPI: POST https://slack.com/api/chat.postMessage
        SlackAPI-->>Msg: JSON response (ok / ts / error)
        Msg-->>Notifier: ts | undefined
    else Config missing
        Notifier-->>GraphQL: log warning & return
    end
Loading
sequenceDiagram
    participant Worker as Worker/CLI
    participant Summary as sendWeeklyVideoSummary
    participant Report as loadWeeklyVideoReport
    participant PrismaMedia as Prisma Media
    participant PrismaLang as Prisma Languages
    participant Renderer as postWeeklyVideoSlackMessages
    participant SlackAPI as Slack API

    Worker->>Summary: invoke with date window
    Summary->>Report: load rows & counts
    Report->>PrismaMedia: query videos/variants/updates
    PrismaMedia-->>Report: rows
    Report->>PrismaLang: fetch language names
    PrismaLang-->>Report: name mappings
    Report-->>Summary: rows & counts
    alt has changes
        Summary->>Renderer: format messages (group by month, chunk)
        Renderer->>SlackAPI: POST message(s) sequentially
        SlackAPI-->>Renderer: ts responses
        alt a post fails (no ts)
            Renderer-->>Summary: abort further posting
        end
    else no changes
        Summary-->>Worker: log info & exit
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.77% 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 accurately describes the main feature addition: creating Slack posts when videos are created or updated, which aligns with the substantial changes across Slack integration modules.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Warnings
⚠️ ❗ Big PR (1570 changes)

(change count - 1570): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against fdb2e15

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 24, 2026

View your CI Pipeline Execution ↗ for commit 314b36e

Command Status Duration Result
nx affected --target=subgraph-check --base=e0ff... ✅ Succeeded 2s View ↗
nx affected --target=extract-translations --bas... ✅ Succeeded <1s View ↗
nx affected --target=lint --base=e0ffd51bc633be... ✅ Succeeded 1s View ↗
nx affected --target=type-check --base=e0ffd51b... ✅ Succeeded <1s View ↗
nx run-many --target=codegen --all --parallel=3 ✅ Succeeded 1s View ↗
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 5s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-14 17:23:16 UTC

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (9)
apis/api-media/src/schema/video/video.spec.ts (1)

2591-2596: Consider also asserting the user payload passed to notifyVideoSlackOfMutation.

The resolvers forward context.user into the notification call, but neither assertion verifies that the user is propagated. Not critical — the tests pass as-is — but adding user: expect.anything() (or an explicit shape) would guard against regressions where the context is dropped from the resolver signature.

Also applies to: 2801-2806

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/schema/video/video.spec.ts` around lines 2591 - 2596,
Update the test assertions that mock notifyVideoSlackOfMutation to also assert
the forwarded user payload: when calling
expect(notifyVideoSlackOfMutation).toHaveBeenCalledWith(...), include a user
property in the objectContaining (e.g., user: expect.anything() or a precise
shape like expect.objectContaining({ id: ..., email: ... })) so the test
verifies the resolver forwards context.user; update both occurrences that assert
notifyVideoSlackOfMutation (the one around the create assertion and the similar
one near the other block referenced) to include this user check.
apis/api-media/src/lib/videoSlack.ts (1)

22-59: Short-circuit on missing Slack config before running the report queries.

loadWeeklyVideoReport issues three Prisma queries plus a language-name fetch. When Slack env vars are missing, the config check only happens inside postWeeklyVideoSlackMessages (videoSlackRenderer.ts line 197-200), so every scheduled weekly run in an environment without the Slack secrets still hits the DB just to discard the result. Moving the getMediaDataLangSlackConfig check to the top of sendWeeklyVideoSummary (in addition to the one inside the renderer) avoids wasted query work and keeps parity with notifyVideoSlackOfMutation, which also guards early.

♻️ Sketch
+import { getMediaDataLangSlackConfig } from './slack/config'
 ...
   const childLogger = currentLogger.child({
     report: 'weekly-video-summary'
   })
+  if (getMediaDataLangSlackConfig(childLogger) == null) {
+    return
+  }
   const { startDate, endDate } = resolveWeeklyVideoSummaryWindow({ ... })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlack.ts` around lines 22 - 59, Move the Slack
config guard to the top of sendWeeklyVideoSummary so we short-circuit before
running DB work: call getMediaDataLangSlackConfig (the same config used by
postWeeklyVideoSlackMessages/videoSlackRenderer) at the start of
sendWeeklyVideoSummary and return early when config is missing, in addition to
keeping the existing check inside postWeeklyVideoSlackMessages; this prevents
loadWeeklyVideoReport from executing Prisma queries when Slack env vars are not
present and mirrors notifyVideoSlackOfMutation's early-guard behavior.
apis/api-media/src/scripts/run-weekly-video-slack-summary.ts (1)

31-58: Argument parsing silently accepts missing flag values.

When --start or --end is the last argv token (or --start= with empty value), argv[i + 1] is undefined and gets assigned to parsed.startIso/parsed.endIso. Downstream parseIso then throws Invalid start date with an ISO example, which misleads a user who actually forgot to supply the value. Consider validating that the next token exists and isn't another flag, and emitting a clearer "--start requires a value" error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/scripts/run-weekly-video-slack-summary.ts` around lines 31
- 58, The parseCliArgs function silently accepts missing or empty values for the
--start/--end flags (assigning undefined or empty strings to
parsed.startIso/parsed.endIso); update parseCliArgs to validate that when
encountering '--start', '--start=' (and similarly '--end', '--end=') the value
exists and is not another flag or empty, and throw a clear error like "`--start`
requires a value" or "`--end` requires a value" instead of allowing downstream
parseIso to fail; reference the parseCliArgs function and the
parsed.startIso/parsed.endIso assignments when adding these checks and error
messages.
apis/api-media/src/lib/slack/chatPostMessage.ts (1)

55-57: Use Pino's conventional err key for caught errors.

Pino's default error serializer is keyed on err, not error. Logging { error } bypasses the serializer, so stack traces and cause chains don't get expanded in structured output.

Changes needed in two locations:

  • apis/api-media/src/lib/slack/chatPostMessage.ts line 56: Change log.warn({ error }, errorMessage) to log.warn({ err: error }, errorMessage)
  • apis/api-media/src/lib/videoSlack.ts lines 69-71: Change { error } to { err: error }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/slack/chatPostMessage.ts` around lines 55 - 57,
Replace the logged error property to use Pino's standard "err" key so the error
serializer expands stacks: in chatPostMessage.ts update the catch block's
log.warn call from logging { error } to { err: error } (the catch in function
chatPostMessage where log.warn({ error }, errorMessage) is used), and similarly
in videoSlack.ts change the log invocation that currently passes { error } to
pass { err: error } instead; keep the same log message string and return
behavior unchanged.
apis/api-media/src/lib/videoSlackReport.ts (4)

441-445: Redundant type guard — videoId is already string.

UpdatedVariantRow.videoId is typed as string (and the Prisma schema declares it non-nullable), so the typeof id === 'string' && id.length > 0 filter is unreachable noise. A plain new Set(updatedVariants.map(v => v.videoId)) is sufficient — and if empty-string defense is intentionally wanted, keep only the length check with a comment.

♻️ Proposed simplification
-  const videoIdsWithVariantUpdates = new Set(
-    updatedVariants
-      .map((variant) => variant.videoId)
-      .filter((id): id is string => typeof id === 'string' && id.length > 0)
-  )
+  const videoIdsWithVariantUpdates = new Set(
+    updatedVariants.map((variant) => variant.videoId)
+  )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackReport.ts` around lines 441 - 445, The
filter using typeof id === 'string' is redundant because
UpdatedVariantRow.videoId is already a non-nullable string; simplify the logic
that builds videoIdsWithVariantUpdates by removing the type check and using new
Set(updatedVariants.map(v => v.videoId)), or if you want to guard against empty
strings only, replace the current filter with id.length > 0 and add a comment
explaining why empty-string defense is needed; update the code around the
videoIdsWithVariantUpdates declaration and references to updatedVariants/videoId
accordingly.

267-349: Add a brief docstring explaining "package totals".

This function encodes non-obvious domain logic (parent = series/featureFilm, package = parent + episode/segment children, "total" = number of package members having a variant in the row's language, with a floor of 1). A short doc comment at the signature would save future readers a lot of reverse-engineering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackReport.ts` around lines 267 - 349, Add a
concise docstring to getLanguagePackageTotals describing its domain logic:
explain that "parent" refers to series/featureFilm identified by
packageParentLabels, "package" includes the parent plus children matching
packageChildLabels, and "total" is the count of package members that have a
VideoVariant for the row's language (with a minimum of 1). Place this comment
above the getLanguagePackageTotals signature and mention key inputs/outputs
(rowSeeds: ReportRowSeed[], returns Map keyed by
`${mediaComponentId}:${languageId}` mapping to the package total) and note
reliance on packageMembersByParentId, variantVideoIdsByLanguage,
packageParentLabels and packageChildLabels so future readers understand the
computation without reversing the code.

193-207: Prefer Prisma.VideoWhereInput over hand-rolled where shape.

Hand-typing the where shape duplicates Prisma's generated types and will drift if the schema/filters evolve. Using Prisma.VideoWhereInput aligns with the existing pattern in this module (e.g., apis/api-media/src/schema/video/video.ts) and keeps IDE autocomplete working.

♻️ Proposed refactor
-import { prisma } from '@core/prisma/media/client'
+import { Prisma, prisma } from '@core/prisma/media/client'
@@
-  const where: {
-    label: { not: 'collection' }
-    createdAt: { lt: Date }
-    updatedAt: { gte: Date; lte: Date }
-    id?: { notIn: string[] }
-  } = {
+  const where: Prisma.VideoWhereInput = {
     label: { not: 'collection' },
     createdAt: {
       lt: startDate
     },
     updatedAt: {
       gte: startDate,
       lte: endDate
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackReport.ts` around lines 193 - 207, The
manually declared type for the where object should be replaced with
Prisma.VideoWhereInput to avoid drifting from the generated schema: change the
type of the where constant (variable named where in videoSlackReport.ts) to
Prisma.VideoWhereInput, ensure the same shape (label.not = 'collection',
createdAt.lt = startDate, updatedAt.gte = startDate, updatedAt.lte = endDate,
and optional id.notIn array) is preserved, and add an import for Prisma from
'@prisma/client' if it isn’t already imported so the type resolves correctly.

288-311: Push child-label filter into Prisma query.

Children are fetched without a label filter and then filtered in JS at line 304. For series/featureFilm parents with many unrelated children, this ships extra rows across the wire every run. A Prisma where on the nested children relation keeps the filter at the DB.

♻️ Proposed refactor
   const parentVideos = await prisma.video.findMany({
     where: {
       id: { in: parentIds }
     },
     select: {
       id: true,
       children: {
+        where: { label: { in: [...packageChildLabels] as any } },
         select: {
           id: true,
           label: true
         }
       }
     }
   })

Note: as any is only needed if packageChildLabels stays a Set<string>. Prefer typing it as Set<VideoLabel> so the Prisma call stays fully typed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackReport.ts` around lines 288 - 311, The
parentVideos query is fetching all children then filtering in JS (lines around
parentVideos, packageChildLabels), which sends unnecessary rows; modify the
Prisma query that builds parentVideos to push the child-label filter into the
nested children select (use a where: { label: { in:
Array.from(packageChildLabels) } } on the children relation) so only matching
children are returned, then keep the existing logic that builds
packageMembersByParentId and packageVideoIds from parent.id and returned
children; also change packageChildLabels to be typed as Set<VideoLabel> (instead
of Set<string>) to avoid needing any casts so the Prisma call remains fully
typed.
apis/api-media/src/lib/slack/videoMutationNotification.spec.ts (1)

1-159: Coverage looks solid; consider a fetch-rejection case.

The suite covers success, env-gating, and Slack error responses well. One gap worth considering: a fetch rejection (network error) path to confirm the module still logs instead of surfacing an unhandled promise rejection — important given notifyVideoSlackOfMutation is invoked fire-and-forget.

🧪 Example test
+  it('logs a warn when fetch rejects (network error)', async () => {
+    mockFetch.mockRejectedValueOnce(new Error('network down'))
+
+    notifyVideoSlackOfMutation({
+      kind: 'create',
+      video: { id: 'video-6', label: 'segment' },
+      user: { id: 'user-6', email: 'editor@example.com' }
+    })
+
+    await flushAsync()
+
+    expect(mockLoggerWarn).toHaveBeenCalled()
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/slack/videoMutationNotification.spec.ts` around lines
1 - 159, Add a new test that simulates a network error by having
mockFetch.mockRejectedValueOnce(new Error('network error')), then call
notifyVideoSlackOfMutation with a sample payload and await flushAsync; assert
that mockFetch was attempted and that mockLoggerWarn was called with an error
object (or expect.any(Error)) and the same 'Video mutation Slack notification
failed' message—use the existing mockFetch, mockLoggerWarn,
notifyVideoSlackOfMutation, and flushAsync identifiers to locate and implement
the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apis/api-media/src/lib/slack/chatPostMessage.ts`:
- Around line 31-58: The Slack POST call in chatPostMessage.ts has no timeout
causing hung promises; update the fetch(slackApiUrl, {...}) call inside the try
block to include a node-fetch v2 compatible timeout option (e.g., timeout:
10000) so the request fails fast on network stalls, and adjust any
typing/payload handling as needed; also change the catch logging to use the Pino
idiom by passing the error as { err: error } (or rename the caught variable to
err and use { err }) when calling log.warn(errorMessage) so error serialization
is correct.

In `@apis/api-media/src/lib/slack/videoMutationNotification.ts`:
- Around line 79-94: formatUser currently returns raw PII (email or full name)
into Slack posts; update formatUser to avoid leaking editor PII unless the
channel is explicitly approved: change the function (formatUser) to either
return a masked email (e.g., keep local-part initial and replace remainder with
ellipsis like "a…@domain" or "a.…@domain"), or return first-initial+surname, or
simply user.id based on a new flag or environment/config setting (e.g.,
SLACK_INCLUDE_PII) so callers can opt-in when channel retention/audience is
approved; ensure the function still handles null/empty values the same and
document the config toggle so default behavior is non-PII (return masked
identifier or user.id).

In `@apis/api-media/src/lib/videoSlack.spec.ts`:
- Around line 11-34: Replace the hand-rolled jest.fn() Prisma mocks with deep
typed mocks from jest-mock-extended: create mockDeep<PrismaClient>() instances
and return those from the jest.mock calls instead of the current objects that
only stub prisma.language.findMany, prisma.video.findMany and
prisma.videoVariant.findMany; update the test to use the deep-mock instances (so
new model/method calls like prisma.video.count are auto-stubbed) and remove the
need for casting to any, ensuring the mocks maintain Prisma types and can be
configured via .language.findMany.mockResolvedValue(...) /
.video.findMany.mockResolvedValue(...) as needed.

In `@apis/api-media/src/lib/videoSlack.ts`:
- Around line 61-76: The try/catch only wraps postWeeklyVideoSlackMessages so
failures in resolveWeeklyVideoSummaryWindow or loadWeeklyVideoReport escape;
modify sendWeeklyVideoSummary so the try begins before calling
resolveWeeklyVideoSummaryWindow and loadWeeklyVideoReport (or wrap the entire
function body) to catch any errors from resolveWeeklyVideoSummaryWindow,
loadWeeklyVideoReport, and postWeeklyVideoSlackMessages, then log the error via
childLogger.warn (including the error object) and return gracefully instead of
letting the exception propagate to the worker; reference sendWeeklyVideoSummary,
resolveWeeklyVideoSummaryWindow, loadWeeklyVideoReport, and
postWeeklyVideoSlackMessages when making the change.

In `@apis/api-media/src/lib/videoSlackReport.ts`:
- Around line 332-346: The code in the loop that computes totals (variables:
totals, eligibleRows, packageMembersByParentId, variantVideoIdsByLanguage)
forces a minimum of 1 when a language has no variants by setting
countForLanguage to 1 and using Math.max(1, …), which over-reports; change the
logic so that when variantVideoIdsByLanguage.get(row.languageId) is undefined
you set countForLanguage = 0 (not 1) and remove the unconditional Math.max(1, …)
so totals.set(`${row.mediaComponentId}:${row.languageId}`, countForLanguage) can
be zero, or if you do intend to count the production as at least one, keep the
Math.max but add an explanatory comment near this block referencing
countForLanguage and the packageMembers logic to prevent future regressions.

---

Nitpick comments:
In `@apis/api-media/src/lib/slack/chatPostMessage.ts`:
- Around line 55-57: Replace the logged error property to use Pino's standard
"err" key so the error serializer expands stacks: in chatPostMessage.ts update
the catch block's log.warn call from logging { error } to { err: error } (the
catch in function chatPostMessage where log.warn({ error }, errorMessage) is
used), and similarly in videoSlack.ts change the log invocation that currently
passes { error } to pass { err: error } instead; keep the same log message
string and return behavior unchanged.

In `@apis/api-media/src/lib/slack/videoMutationNotification.spec.ts`:
- Around line 1-159: Add a new test that simulates a network error by having
mockFetch.mockRejectedValueOnce(new Error('network error')), then call
notifyVideoSlackOfMutation with a sample payload and await flushAsync; assert
that mockFetch was attempted and that mockLoggerWarn was called with an error
object (or expect.any(Error)) and the same 'Video mutation Slack notification
failed' message—use the existing mockFetch, mockLoggerWarn,
notifyVideoSlackOfMutation, and flushAsync identifiers to locate and implement
the test.

In `@apis/api-media/src/lib/videoSlack.ts`:
- Around line 22-59: Move the Slack config guard to the top of
sendWeeklyVideoSummary so we short-circuit before running DB work: call
getMediaDataLangSlackConfig (the same config used by
postWeeklyVideoSlackMessages/videoSlackRenderer) at the start of
sendWeeklyVideoSummary and return early when config is missing, in addition to
keeping the existing check inside postWeeklyVideoSlackMessages; this prevents
loadWeeklyVideoReport from executing Prisma queries when Slack env vars are not
present and mirrors notifyVideoSlackOfMutation's early-guard behavior.

In `@apis/api-media/src/lib/videoSlackReport.ts`:
- Around line 441-445: The filter using typeof id === 'string' is redundant
because UpdatedVariantRow.videoId is already a non-nullable string; simplify the
logic that builds videoIdsWithVariantUpdates by removing the type check and
using new Set(updatedVariants.map(v => v.videoId)), or if you want to guard
against empty strings only, replace the current filter with id.length > 0 and
add a comment explaining why empty-string defense is needed; update the code
around the videoIdsWithVariantUpdates declaration and references to
updatedVariants/videoId accordingly.
- Around line 267-349: Add a concise docstring to getLanguagePackageTotals
describing its domain logic: explain that "parent" refers to series/featureFilm
identified by packageParentLabels, "package" includes the parent plus children
matching packageChildLabels, and "total" is the count of package members that
have a VideoVariant for the row's language (with a minimum of 1). Place this
comment above the getLanguagePackageTotals signature and mention key
inputs/outputs (rowSeeds: ReportRowSeed[], returns Map keyed by
`${mediaComponentId}:${languageId}` mapping to the package total) and note
reliance on packageMembersByParentId, variantVideoIdsByLanguage,
packageParentLabels and packageChildLabels so future readers understand the
computation without reversing the code.
- Around line 193-207: The manually declared type for the where object should be
replaced with Prisma.VideoWhereInput to avoid drifting from the generated
schema: change the type of the where constant (variable named where in
videoSlackReport.ts) to Prisma.VideoWhereInput, ensure the same shape (label.not
= 'collection', createdAt.lt = startDate, updatedAt.gte = startDate,
updatedAt.lte = endDate, and optional id.notIn array) is preserved, and add an
import for Prisma from '@prisma/client' if it isn’t already imported so the type
resolves correctly.
- Around line 288-311: The parentVideos query is fetching all children then
filtering in JS (lines around parentVideos, packageChildLabels), which sends
unnecessary rows; modify the Prisma query that builds parentVideos to push the
child-label filter into the nested children select (use a where: { label: { in:
Array.from(packageChildLabels) } } on the children relation) so only matching
children are returned, then keep the existing logic that builds
packageMembersByParentId and packageVideoIds from parent.id and returned
children; also change packageChildLabels to be typed as Set<VideoLabel> (instead
of Set<string>) to avoid needing any casts so the Prisma call remains fully
typed.

In `@apis/api-media/src/schema/video/video.spec.ts`:
- Around line 2591-2596: Update the test assertions that mock
notifyVideoSlackOfMutation to also assert the forwarded user payload: when
calling expect(notifyVideoSlackOfMutation).toHaveBeenCalledWith(...), include a
user property in the objectContaining (e.g., user: expect.anything() or a
precise shape like expect.objectContaining({ id: ..., email: ... })) so the test
verifies the resolver forwards context.user; update both occurrences that assert
notifyVideoSlackOfMutation (the one around the create assertion and the similar
one near the other block referenced) to include this user check.

In `@apis/api-media/src/scripts/run-weekly-video-slack-summary.ts`:
- Around line 31-58: The parseCliArgs function silently accepts missing or empty
values for the --start/--end flags (assigning undefined or empty strings to
parsed.startIso/parsed.endIso); update parseCliArgs to validate that when
encountering '--start', '--start=' (and similarly '--end', '--end=') the value
exists and is not another flag or empty, and throw a clear error like "`--start`
requires a value" or "`--end` requires a value" instead of allowing downstream
parseIso to fail; reference the parseCliArgs function and the
parsed.startIso/parsed.endIso assignments when adding these checks and error
messages.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 74788101-07db-4628-85ed-3358cdb8daa8

📥 Commits

Reviewing files that changed from the base of the PR and between edbfa3b and 2f1e27a.

📒 Files selected for processing (18)
  • apis/api-media/infrastructure/locals.tf
  • apis/api-media/project.json
  • apis/api-media/src/lib/slack/chatPostMessage.ts
  • apis/api-media/src/lib/slack/config.ts
  • apis/api-media/src/lib/slack/index.ts
  • apis/api-media/src/lib/slack/videoMutationNotification.spec.ts
  • apis/api-media/src/lib/slack/videoMutationNotification.ts
  • apis/api-media/src/lib/videoSlack.spec.ts
  • apis/api-media/src/lib/videoSlack.ts
  • apis/api-media/src/lib/videoSlackRenderer.ts
  • apis/api-media/src/lib/videoSlackReport.ts
  • apis/api-media/src/schema/video/video.spec.ts
  • apis/api-media/src/schema/video/video.ts
  • apis/api-media/src/scripts/run-weekly-video-slack-summary.ts
  • apis/api-media/src/workers/server.ts
  • apis/api-media/src/workers/videoSlackSummary/config.ts
  • apis/api-media/src/workers/videoSlackSummary/index.ts
  • apis/api-media/src/workers/videoSlackSummary/service/service.ts

Comment thread apis/api-media/src/lib/slack/chatPostMessage.ts
Comment thread apis/api-media/src/lib/slack/videoMutationNotification.ts Outdated
Comment thread apis/api-media/src/lib/videoSlack.spec.ts Outdated
Comment thread apis/api-media/src/lib/videoSlack.ts Outdated
Comment thread apis/api-media/src/lib/videoSlackReport.ts Outdated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.9: unable to verify checksums signature: openpgp: key expired


…s-created-or-updated' of https://github.com/JesusFilm/core into tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.9: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.9: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.9: unable to verify checksums signature: openpgp: key expired


Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
apis/api-media/src/lib/videoSlackBulkReport.ts (2)

48-87: Duplicated loadLanguageNames function.

This function is nearly identical to the one in videoSlackReport.ts (lines 224-264). Consider extracting it to a shared utility module to reduce duplication and ensure consistent behavior.

♻️ Suggested approach

Create a shared module, e.g., apis/api-media/src/lib/languageUtils.ts:

import { prisma as languagesPrisma } from '@core/prisma/languages/client'
import { ENGLISH_LANGUAGE_ID } from './algolia/languages/languageLists'
import { logger } from '../logger'

export async function loadLanguageNames(
  ids: string[],
  warnContext: string
): Promise<Map<string, string>> {
  // ... shared implementation
}

Then import and use in both report files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackBulkReport.ts` around lines 48 - 87, Extract
the duplicated async function loadLanguageNames into a shared utility (e.g.,
languageUtils.ts) and export it for reuse; update both callers
(loadLanguageNames usages in videoSlackBulkReport.ts and videoSlackReport.ts) to
import the shared function, preserve behavior (unique filtering,
languagesPrisma.language.findMany, fallback to id, and logger.warn with a
contextual message) and consider adding a small parameter like warnContext or
caller name so the logger message can remain specific when called from different
reports; ensure the exported function signature and return type
(Promise<Map<string,string>>) match the existing implementations so callers need
only swap the local function call for the shared import.

7-7: Use the existing ENGLISH_LANGUAGE_ID constant instead of hardcoding '529'.

The codebase already defines this constant in apis/api-media/src/lib/algolia/languages/languageLists.ts. Using it improves maintainability and consistency.

♻️ Suggested fix
+import { ENGLISH_LANGUAGE_ID } from './algolia/languages/languageLists'
 import { prisma as languagesPrisma } from '@core/prisma/languages/client'
 import { prisma } from '@core/prisma/media/client'

 import { logger } from '../logger'

-const englishLanguageIdForNames = '529'
+const englishLanguageIdForNames = ENGLISH_LANGUAGE_ID
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackBulkReport.ts` at line 7, Replace the
hardcoded string assigned to englishLanguageIdForNames ('529') with the shared
ENGLISH_LANGUAGE_ID constant: import ENGLISH_LANGUAGE_ID from the languages
constant module (where it's defined) and use that constant in place of '529' so
englishLanguageIdForNames = ENGLISH_LANGUAGE_ID; ensure the import name matches
the exported symbol and remove the literal.
apis/api-media/src/lib/videoSlackBulkRenderer.ts (1)

15-52: Duplicated utility functions with videoSlackRenderer.ts.

formatMonthYearUtc, getMonthKey, truncateEnd, and padCell are duplicated between the bulk and weekly renderers. Consider extracting these to a shared formatting utility.

♻️ Suggested approach

Create a shared module, e.g., apis/api-media/src/lib/slackFormatUtils.ts:

export function formatMonthYearUtc(value: Date): string { ... }
export function getMonthKey(value: Date): string { ... }
export function truncateEnd(value: string, maxLength: number): string { ... }
export function padCell(value: string, width: number): string { ... }

Then import in both renderer files to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackBulkRenderer.ts` around lines 15 - 52, The
four functions formatMonthYearUtc, getMonthKey, truncateEnd, and padCell are
duplicated; extract them into a single shared module (e.g., slackFormatUtils)
and export each function, then replace the local implementations in
videoSlackBulkRenderer and videoSlackRenderer with imports from that module;
ensure the signatures and behavior remain identical and update any references to
call the imported functions.
apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts (1)

100-101: Inconsistent logging: mix of console.log/console.error and Pino logger.

The script uses Pino logger at lines 105-111 but falls back to console.log/console.error elsewhere. Per coding guidelines, use Pino logger everywhere for consistent structured logging.

♻️ Suggested fix
-  console.log(
-    `Running weekly bulk summary window: ${startDate.toISOString()} -> ${endDate.toISOString()}`
-  )
+  logger.info(
+    { windowStart: startDate.toISOString(), windowEnd: endDate.toISOString() },
+    'Running weekly bulk summary window'
+  )
   const rows = await loadBulkVideoReport({ startDate, endDate })
   ...
   await postBulkVideoSlackMessages({
     rows,
     startDate,
     endDate,
     childLogger: logger
   })
-  console.log('postBulkVideoSlackMessages finished')
+  logger.info('postBulkVideoSlackMessages finished')
 }

 main().catch((error) => {
-  console.error(error)
+  logger.error({ error }, 'Weekly bulk summary failed')
   process.exit(1)
 })

As per coding guidelines: "Use Pino logger everywhere — nestjs-pino in legacy, direct Pino in modern APIs, with pretty-print in dev and structured JSON in production".

Also applies to: 120-120, 123-125

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts` around
lines 100 - 101, Replace the ad-hoc console.log/console.error calls with the
script's Pino logger instance (the same logger used in the block around lines
105-111) so all output is structured; for the startup message use logger.info
with the startDate and endDate (e.g., logger.info({ startDate:
startDate.toISOString(), endDate: endDate.toISOString() }, "Running weekly bulk
summary window")), and convert other console.error uses (including the ones
around lines 120 and 123-125) to logger.error, passing the error object as
metadata for proper structured logs.
apis/api-media/src/lib/videoSlackReport.ts (1)

8-8: Use the existing ENGLISH_LANGUAGE_ID constant instead of hardcoding '529'.

Same issue as in videoSlackBulkReport.ts. Import from apis/api-media/src/lib/algolia/languages/languageLists.ts for consistency.

♻️ Suggested fix
+import { ENGLISH_LANGUAGE_ID } from './algolia/languages/languageLists'
 import { prisma as languagesPrisma } from '@core/prisma/languages/client'
 import { prisma } from '@core/prisma/media/client'

 import { logger } from '../logger'

 const oneWeekInDays = 7
-const englishLanguageIdForNames = '529'
+const englishLanguageIdForNames = ENGLISH_LANGUAGE_ID
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-media/src/lib/videoSlackReport.ts` at line 8, Replace the hardcoded
const englishLanguageIdForNames = '529' with the shared ENGLISH_LANGUAGE_ID
constant: remove or stop using englishLanguageIdForNames and import
ENGLISH_LANGUAGE_ID from the languageLists module (languageLists.ts), then use
ENGLISH_LANGUAGE_ID wherever englishLanguageIdForNames was referenced (same
change as in videoSlackBulkReport.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts`:
- Around line 29-56: parseCliArgs currently reads argv[i+1] for the
--start/--end flags without checking bounds, which can yield undefined; update
parseCliArgs to validate that i+1 exists and is not another flag before
assigning parsed.startIso/parsed.endIso (for both the '--start' and '--end'
branches), and if the value is missing or looks like a flag, throw a clear error
or return a validation error indicating the flag is missing a value so
downstream parseIso isn't given undefined.

---

Nitpick comments:
In `@apis/api-media/src/lib/videoSlackBulkRenderer.ts`:
- Around line 15-52: The four functions formatMonthYearUtc, getMonthKey,
truncateEnd, and padCell are duplicated; extract them into a single shared
module (e.g., slackFormatUtils) and export each function, then replace the local
implementations in videoSlackBulkRenderer and videoSlackRenderer with imports
from that module; ensure the signatures and behavior remain identical and update
any references to call the imported functions.

In `@apis/api-media/src/lib/videoSlackBulkReport.ts`:
- Around line 48-87: Extract the duplicated async function loadLanguageNames
into a shared utility (e.g., languageUtils.ts) and export it for reuse; update
both callers (loadLanguageNames usages in videoSlackBulkReport.ts and
videoSlackReport.ts) to import the shared function, preserve behavior (unique
filtering, languagesPrisma.language.findMany, fallback to id, and logger.warn
with a contextual message) and consider adding a small parameter like
warnContext or caller name so the logger message can remain specific when called
from different reports; ensure the exported function signature and return type
(Promise<Map<string,string>>) match the existing implementations so callers need
only swap the local function call for the shared import.
- Line 7: Replace the hardcoded string assigned to englishLanguageIdForNames
('529') with the shared ENGLISH_LANGUAGE_ID constant: import ENGLISH_LANGUAGE_ID
from the languages constant module (where it's defined) and use that constant in
place of '529' so englishLanguageIdForNames = ENGLISH_LANGUAGE_ID; ensure the
import name matches the exported symbol and remove the literal.

In `@apis/api-media/src/lib/videoSlackReport.ts`:
- Line 8: Replace the hardcoded const englishLanguageIdForNames = '529' with the
shared ENGLISH_LANGUAGE_ID constant: remove or stop using
englishLanguageIdForNames and import ENGLISH_LANGUAGE_ID from the languageLists
module (languageLists.ts), then use ENGLISH_LANGUAGE_ID wherever
englishLanguageIdForNames was referenced (same change as in
videoSlackBulkReport.ts).

In `@apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts`:
- Around line 100-101: Replace the ad-hoc console.log/console.error calls with
the script's Pino logger instance (the same logger used in the block around
lines 105-111) so all output is structured; for the startup message use
logger.info with the startDate and endDate (e.g., logger.info({ startDate:
startDate.toISOString(), endDate: endDate.toISOString() }, "Running weekly bulk
summary window")), and convert other console.error uses (including the ones
around lines 120 and 123-125) to logger.error, passing the error object as
metadata for proper structured logs.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5b844828-0949-436c-a6ff-8e39d0749a1a

📥 Commits

Reviewing files that changed from the base of the PR and between 2f1e27a and a5cb53c.

📒 Files selected for processing (10)
  • apis/api-media/project.json
  • apis/api-media/src/lib/videoSlack.spec.ts
  • apis/api-media/src/lib/videoSlackBulk.spec.ts
  • apis/api-media/src/lib/videoSlackBulkRenderer.ts
  • apis/api-media/src/lib/videoSlackBulkReport.ts
  • apis/api-media/src/lib/videoSlackRenderer.ts
  • apis/api-media/src/lib/videoSlackReport.ts
  • apis/api-media/src/schema/video/video.spec.ts
  • apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts
  • apis/api-media/src/scripts/run-weekly-video-slack-summary.ts
✅ Files skipped from review due to trivial changes (1)
  • apis/api-media/project.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • apis/api-media/src/schema/video/video.spec.ts
  • apis/api-media/src/scripts/run-weekly-video-slack-summary.ts

Comment thread apis/api-media/src/scripts/run-weekly-video-slack-bulk-summary.ts Outdated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.0: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.0: unable to verify checksums signature: openpgp: key expired


tanflem added 2 commits April 30, 2026 18:20
…s-created-or-updated' of https://github.com/JesusFilm/core into tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.0: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.0: unable to verify checksums signature: openpgp: key expired


tanflem added 2 commits May 1, 2026 14:30
Removes the per-video create/update Slack notification (called directly
from the videoCreate/videoUpdate resolvers) so QA-378 is scoped to the
weekly summary only. The per-video work moves to QA-452 on a stacked
branch.

Shared infra (chatPostMessage, config) stays — the weekly summary uses
it.
…Slack/

Mirrors the project's folder convention (cf. lib/algolia/) and
distinguishes the video-domain feature code from the generic Slack
platform primitives in lib/slack/.

- Move videoSlack.ts, videoSlackRenderer.ts, videoSlackReport.ts and the
  spec into lib/videoSlack/.
- Add lib/videoSlack/index.ts barrel.
- Update relative imports inside the moved files.
- Collapse the script's two imports into one barrel import.
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.0: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.1: unable to verify checksums signature: openpgp: key expired


- Add blank line between import groups in videoSlackRenderer.ts
- Disable no-unnecessary-type-assertion where the cast is
  load-bearing for cross-file type inference
  (prismaMock.ts, video.ts, videoSlack.spec.ts)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.15.2: unable to verify checksums signature: openpgp: key expired


…create-a-slack-post-when-a-video-is-created-or-updated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.8: unable to verify checksums signature: openpgp: key expired


…create-a-slack-post-when-a-video-is-created-or-updated
@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.8: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.8: unable to verify checksums signature: openpgp: key expired


@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Plan Error

error downloading terraform version 1.14.8: unable to verify checksums signature: openpgp: key expired


@tanflem tanflem added this pull request to the merge queue May 18, 2026
Merged via the queue into main with commit a7ff469 May 18, 2026
23 checks passed
@tanflem tanflem deleted the tannerfleming/vmt-38-create-a-slack-post-when-a-video-is-created-or-updated branch May 18, 2026 16:09
tanflem added a commit that referenced this pull request May 19, 2026
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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