Skip to content

Fix the seven review findings left open on #3590 - #3600

Merged
gitar-bot[bot] merged 3 commits into
mainfrom
fix/pr3590-review-followups
Oct 17, 2026
Merged

gitar-bot[bot] merged 3 commits into
mainfrom
fix/pr3590-review-followups

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

User description

Follow-up to #3590: fixes the seven review threads that were left open so the #3590 merge was not held up. Rebased onto main after #3590 merged.

Thread Fix
tests/worker-lease-transition-contract.test.ts The contract test now checks that the selected deadline is attached by .abortSignal(...) on the .rpc(...) builder. Before, it only checked that the strings appeared somewhere in the adapter.
docs/governance-incident-runbooks.md The runbook now sets the variables the app actually reads: RAG_PERSIST_RAW_QUERY_TEXT, RAG_PERSIST_ANSWER_TEXT and OPENAI_STORE_RESPONSES.
scripts/deploy/write-release-manifest.mjs Uses the same candidate order as resolveDeploymentCommitSha, and takes the first candidate that is a valid SHA.
scripts/ops-digest.mjs Accepts any positive gap of up to six hours, so a manual run between scheduled runs no longer breaks the evidence run.
scripts/trivy-image-scan.mjs Downloads the database again when the cached one has passed its NextUpdate time, or when the cached copy is missing or unreadable. If that download fails, the scan fails.
src/lib/roster/alerts/dispatch.ts An open-shift offer that reached at least one recipient is recorded as delivered. A replay therefore no longer resends it to everyone.
src/lib/teaching/repository.ts A non-organiser who revokes by occurrence, when someone else created one of the links, now gets teaching_role_denied and nothing is revoked. Before, some links were revoked and the call still returned {}. A non-organiser can still revoke their own link by its token.

No migrations and no RAG ranking files are touched.

RAG impact: no retrieval behaviour change. No retrieval or ranking files are touched.

Tests: I added regression tests for each fix except the runbook. npx vitest run passed 110 of 110 across the 8 affected test files. verify:cheap could not run in the session worktree, because its installed dependencies do not match the lockfile. I am re-running it in a fresh checkout.

🤖 Generated with Claude Code


Summary by cubic

Implements the seven review-follow-up fixes left open on #3590, rebased onto main after that PR merged. No RAG retrieval behaviour changes.

Fixes

  • The worker lease contract test now proves the selected deadline is attached to the RPC builder, not merely present in the adapter text.
  • The incident runbook now disables the persistence variables the app actually reads (RAG_PERSIST_RAW_QUERY_TEXT, RAG_PERSIST_ANSWER_TEXT, OPENAI_STORE_RESPONSES).
  • The release manifest resolves the commit SHA in the same candidate order as resolveDeploymentCommitSha, skipping invalid values.
  • The ops digest now requires contiguous evidence from adjacent hourly buckets, so a manual run between scheduled runs no longer counts toward the window.
  • trivy-image-scan.mjs refreshes its cached vulnerability database once NextUpdate has passed or the cache is partial (missing or unreadable metadata included); a failed refresh fails the scan, and the Java database is refreshed on its own when missing or stale.
  • Open-shift offers now track delivery per recipient in a new roster_open_post_alert_deliveries table, so a replay retries only the recipients who were not reached instead of resending to everyone.
  • A non-organiser revoking by occurrence is refused when any link was created by someone else; only their own link can be revoked by token.

Regression tests were added for every fix except the runbook; npx vitest run passes all affected tests.

Written for commit a429bfc. Summary will update on new commits.

View guided diff Turn on auto-fix


CodeAnt-AI Description

Fix duplicate shift alerts, partial teaching-link revocations, and stale vulnerability scans

What Changed

  • A partially delivered open-shift offer is recorded as delivered, so a replay does not resend it to everyone.
  • Revoking teaching display links by occurrence now revokes all active links or none; non-organisers are refused if any link was created by someone else.
  • Vulnerability scans refresh missing, unreadable, or expired databases and fail if a refresh fails, rather than using stale data.
  • Manual operations runs can count toward consecutive evidence when no more than six hours apart. Release manifests skip invalid commit values, and the incident runbook now uses the correct privacy-control settings.

Impact

✅ No duplicate open-shift offers after partial delivery
✅ No partial unauthorised teaching-link revocations
✅ Scans avoid using expired vulnerability data
✅ More reliable release and incident evidence

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@greptile-apps greptile-apps 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.

BigSimmo has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 109 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: BigSimmo/PsychSift/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 502195f8-4c04-4638-b4ff-210ad9519ca4
📥 Commits

Reviewing files that changed from the base of the PR and between 770c094 and a429bfc.

📒 Files selected for processing: 12
  • docs/governance-incident-runbooks.md
  • scripts/deploy/write-release-manifest.mjs
  • scripts/ops-digest.mjs
  • scripts/trivy-image-scan.mjs
  • src/lib/roster/alerts/dispatch.ts
  • src/lib/teaching/repository.ts
  • tests/ops-digest.test.ts
  • tests/roster-alerts-replay.test.ts
  • tests/sentry-release.test.ts
  • tests/teaching-display-revoke-auth.test.ts
  • tests/trivy-image-scan.test.ts
  • tests/worker-lease-transition-contract.test.ts
✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@codeant-ai

codeant-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR db56637 Oct 17, 2026 · 07:05 07:13

@supabase

supabase Bot commented Oct 17, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@codeant-ai

codeant-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T07:07:31.110326Z db56637 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 17, 2026
@codeant-ai

codeant-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: a429bfcd
Scan Time: 2026-10-17 07:53:11 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Resolve seven outstanding review findings from #3590

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Fix release identity, monitoring evidence, and vulnerability database freshness in operational
 scripts.
• Prevent duplicate roster offers and partial teaching-link revocations.
• Correct incident controls and strengthen regression tests, including the worker deadline contract.
Diagram

graph TD
  Manifest["Release manifest"] --> Runtime["Runtime identity"]
  Cache[("Trivy cache")] --> Scan["Offline scan"]
  Dispatch["Roster dispatch"] --> Ledger[("Delivery state")]
  Auth["Revoke authorization"] --> Links[("Display links")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Track delivery per recipient
  • ➕ Could retry failed recipients without duplicating offers for successful recipients.
  • ➖ Requires durable recipient-level state and a broader change than this review follow-up.

Recommendation: Keep the focused fixes for this follow-up. Marking a partially sent offer delivered prevents duplicate broadcasts under the existing offer-level tracking model; consider recipient-level tracking separately if retrying individual failures becomes necessary.

Files changed (12) +203 / -34

Bug fix (5) +55 / -27
write-release-manifest.mjsAlign manifest commit selection with runtime +8/-9

Align manifest commit selection with runtime

• Checks commit candidates in runtime resolver order and skips invalid values before falling back to Git HEAD.

scripts/deploy/write-release-manifest.mjs

ops-digest.mjsAccept intervening manual evidence runs +3/-1

Accept intervening manual evidence runs

• Treats positive gaps of up to six hours between hourly windows as consecutive, preserving evidence when a manual run occurs between scheduled runs.

scripts/ops-digest.mjs

trivy-image-scan.mjsRefresh expired Trivy vulnerability databases +32/-2

Refresh expired Trivy vulnerability databases

• Reads cached database metadata and downloads again when it is absent, unreadable, undated, or past NextUpdate. A failed required download fails the scan rather than using stale data.

scripts/trivy-image-scan.mjs

dispatch.tsAvoid replaying partially delivered offers +4/-1

Avoid replaying partially delivered offers

• Records an open-shift offer as delivered when at least one recipient was reached, preventing a replay from resending it to the full eligible list.

src/lib/roster/alerts/dispatch.ts

repository.tsAuthorize occurrence-wide revocation before changing links +8/-14

Authorize occurrence-wide revocation before changing links

• Requires an active organiser when an occurrence has links created by someone else or with no recorded creator. A denied request now revokes none of the links instead of silently succeeding after a partial revoke.

src/lib/teaching/repository.ts

Tests (6) +145 / -4
ops-digest.test.tsCover manual and excessive evidence gaps +30/-0

Cover manual and excessive evidence gaps

• Tests that a manual run preserves consecutive RPC evidence and that a gap exceeding six hours breaks it.

tests/ops-digest.test.ts

roster-alerts-replay.test.tsCover replay after partial offer delivery +25/-0

Cover replay after partial offer delivery

• Verifies that an offer reaching one recipient but failing another is not resent on replay.

tests/roster-alerts-replay.test.ts

sentry-release.test.tsCover ordered, validated manifest SHA selection +21/-0

Cover ordered, validated manifest SHA selection

• Checks that a valid Vercel SHA takes precedence over GitHub's and that an invalid earlier candidate does not block a valid later one.

tests/sentry-release.test.ts

teaching-display-revoke-auth.test.tsCover occurrence revocation authorization +45/-0

Cover occurrence revocation authorization

• Verifies that a non-organiser cannot revoke any links when another creator has an active link, but can revoke an occurrence containing only their own link.

tests/teaching-display-revoke-auth.test.ts

trivy-image-scan.test.tsCover vulnerability database freshness decisions +19/-0

Cover vulnerability database freshness decisions

• Tests missing, malformed, undated, expired, and still-current Trivy database metadata.

tests/trivy-image-scan.test.ts

worker-lease-transition-contract.test.tsAssert the deadline is attached to the RPC builder +5/-4

Assert the deadline is attached to the RPC builder

• Replaces independent string checks with a contract assertion that the selected control- or data-plane abort signal is passed to .abortSignal(...) on the RPC builder.

tests/worker-lease-transition-contract.test.ts

Documentation (1) +3 / -3
governance-incident-runbooks.mdCorrect incident shutdown variables +3/-3

Correct incident shutdown variables

• Replaces three obsolete variable names with the raw-query, answer-text, and OpenAI response-storage controls the application reads.

docs/governance-incident-runbooks.md

@gitar-bot

gitar-bot Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

⚠️ Gitar auto-approved this PR but could not enable auto-merge: an unexpected error occurred.

gitar-bot[bot]

This comment was marked as resolved.

@qodo-code-review

qodo-code-review Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group

Grey Divider


Resolved findings

1. Image scans fail on a partial cache ✓ Resolved
Description
runDockerTrivy uses only metadata.json to decide whether the vulnerability database needs
downloading, without checking that the database file is present. If the database file is missing
while its metadata has a future NextUpdate, the scan runs offline with database updates disabled
and fails instead of repairing the cache.
Code

scripts/trivy-image-scan.mjs[212]

+      if (trivyDbNeedsRefresh(readTrivyDbMetadata(cacheDir))) {
Relevance

●●● Strong

Matches the PR’s stated Trivy cache-repair intent; partial-cache failures are reliability defects.

PR-#3590
PR-#3537

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The deploy skill calls for inspecting deployment environment contracts and failure behavior. The
changed condition reads only metadata; the subsequent scan disables network access and database
updates, so it cannot restore a missing database file.

scripts/trivy-image-scan.mjs[186-212]
scripts/trivy-image-scan.mjs[266-282]
Skill: deploy

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A current metadata timestamp does not prove that the cached vulnerability database file still exists. The offline scan cannot recover from a partial cache.

## Fix Focus Areas
- scripts/trivy-image-scan.mjs[186-212]
- tests/trivy-image-scan.test.ts[153-170]

## Recommended Fix
Check the cached database file as well as its metadata before skipping the download. Add a focused regression test for a future `NextUpdate` with a missing database file.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Manual runs can trigger premature pages ✓ Resolved
Description
updateHybridRpcHourlyEvidence now treats any positive gap of up to six hours as consecutive
evidence, including gaps between nonadjacent UTC-hour buckets. Three observations at 00:00, 03:00
and 06:00 can therefore produce a paging alert even though the documented threshold requires the
same failing RPC in three contiguous hourly buckets.
Code

scripts/ops-digest.mjs[113]

+      return gapMs > 0 && gapMs <= 6 * hourlyWindowMs;
Relevance

●●● Strong

Recent #3522 precedents explicitly require three contiguous UTC-hour buckets; this change permits
noncontiguous evidence.

PR-#3522

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Observations are rounded to UTC-hour starts, and the new test explicitly accepts 00:00, 03:00 and
06:00 as repeated evidence. The alert consumer turns repeated evidence and a positive current error
rate into a page, whereas the documented criterion says three contiguous UTC-hour buckets.

scripts/ops-digest.mjs[69-74]
tests/ops-digest.test.ts[80-92]
scripts/lib/operational-alerts.mjs[151-166]
docs/observability-slos.md[410-412]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new gap check promotes nonadjacent hourly observations to the three-contiguous-window paging threshold.
## Fix Focus Areas
- scripts/ops-digest.mjs[108-116]
- scripts/lib/operational-alerts.mjs[151-166]
## Recommended Fix
Define whether paging requires contiguous hourly buckets or consecutive scheduled/manual observations. Enforce that policy in the history check and align the alert threshold and regression tests with it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Some staff never receive shift offers ✓ Resolved
Description
runRosterAlertDispatch marks an entire open-shift offer delivered when sendRosterAlerts reports
any successful device send, even if another recipient's settings lookup or push fails transiently.
On replay, isOpenPostAlertDelivered finds the offer-level record and skips sending, leaving the
failed recipient without a retry.
Code

src/lib/roster/alerts/dispatch.ts[R242-243]

+  if (isOpenPostOffer && result.openShiftId && ((delivery?.sent ?? 0) > 0 || (delivery?.failed ?? 0) === 0)) {
    await markOpenPostAlertDelivered(client, event.serviceId, result.openShiftId);
Relevance

● Weak

Recent #3557 precedents rejected retrying partial deliveries; team intentionally records delivery
after any successful recipient send.

PR-#3557

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
sendRosterAlerts aggregates successes and failures across recipients, counting failed settings
lookups and push sends without identifying which recipients need a retry. The dispatch condition
records delivery after any success, while the delivery record has only an offer-level key; the
replay check returns before sending whenever that record exists.

src/lib/roster/alerts/dispatch.ts[238-243]
src/lib/roster/alerts/dispatch.ts[253-270]
src/lib/roster/alerts/send.ts[17-43]
src/lib/roster/alerts/send.ts[15-42]
src/lib/roster/alerts/send.ts[92-105]
src/lib/roster/alerts/dispatch.ts[111-135]
src/lib/roster/alerts/dispatch.ts[260-268]
Skill: api

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A partial open-shift alert send is recorded as fully delivered, so recipients whose settings lookup or push failed transiently are not retried on replay.

## Fix Focus Areas
- src/lib/roster/alerts/dispatch.ts[238-243]
- src/lib/roster/alerts/dispatch.ts[253-270]
- src/lib/roster/alerts/send.ts[15-43]

## Recommended Fix
Persist successful delivery outcomes at recipient or device granularity. On replay, send only to recipients not yet reached, without resending to those already reached. Add a regression test where one recipient succeeds and another fails transiently, then verify replay reaches only the failed recipient.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
❌ Tickets: failed to load — Ticket collection could not establish complete evidence
✅ Compliance rules (platform): 9 rules
✅ Cross-repo context — repo relationships
✅ REVIEW.md
Review mode: Auto: 🚀 Fast: Localized cache-freshness helper change with focused regression tests.

Grey Divider

Tip of the day
💡 Did you know, you can see how risky a PR is at a glance with blast radius labels

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit a429bfc 🚀 Fast

Results up to commit db56637 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group


Resolved findings

1. Manual runs can trigger premature pages ✓ Resolved
Description
updateHybridRpcHourlyEvidence now treats any positive gap of up to six hours as consecutive
evidence, including gaps between nonadjacent UTC-hour buckets. Three observations at 00:00, 03:00
and 06:00 can therefore produce a paging alert even though the documented threshold requires the
same failing RPC in three contiguous hourly buckets.
Code

scripts/ops-digest.mjs[113]

+      return gapMs > 0 && gapMs <= 6 * hourlyWindowMs;
Relevance

●●● Strong

Recent #3522 precedents explicitly require three contiguous UTC-hour buckets; this change permits
noncontiguous evidence.

PR-#3522

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Observations are rounded to UTC-hour starts, and the new test explicitly accepts 00:00, 03:00 and
06:00 as repeated evidence. The alert consumer turns repeated evidence and a positive current error
rate into a page, whereas the documented criterion says three contiguous UTC-hour buckets.

scripts/ops-digest.mjs[69-74]
tests/ops-digest.test.ts[80-92]
scripts/lib/operational-alerts.mjs[151-166]
docs/observability-slos.md[410-412]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new gap check promotes nonadjacent hourly observations to the three-contiguous-window paging threshold.
## Fix Focus Areas
- scripts/ops-digest.mjs[108-116]
- scripts/lib/operational-alerts.mjs[151-166]
## Recommended Fix
Define whether paging requires contiguous hourly buckets or consecutive scheduled/manual observations. Enforce that policy in the history check and align the alert threshold and regression tests with it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Some staff never receive shift offers ✓ Resolved
Description
runRosterAlertDispatch marks an entire open-shift offer delivered when sendRosterAlerts reports
any successful device send, even if another recipient's settings lookup or push fails transiently.
On replay, isOpenPostAlertDelivered finds the offer-level record and skips sending, leaving the
failed recipient without a retry.
Code

src/lib/roster/alerts/dispatch.ts[R242-243]

+  if (isOpenPostOffer && result.openShiftId && ((delivery?.sent ?? 0) > 0 || (delivery?.failed ?? 0) === 0)) {
    await markOpenPostAlertDelivered(client, event.serviceId, result.openShiftId);
Relevance

● Weak

Recent #3557 precedents rejected retrying partial deliveries; team intentionally records delivery
after any successful recipient send.

PR-#3557

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
sendRosterAlerts aggregates successes and failures across recipients, counting failed settings
lookups and push sends without identifying which recipients need a retry. The dispatch condition
records delivery after any success, while the delivery record has only an offer-level key; the
replay check returns before sending whenever that record exists.

src/lib/roster/alerts/dispatch.ts[238-243]
src/lib/roster/alerts/dispatch.ts[253-270]
src/lib/roster/alerts/send.ts[17-43]
src/lib/roster/alerts/send.ts[15-42]
src/lib/roster/alerts/send.ts[92-105]
src/lib/roster/alerts/dispatch.ts[111-135]
src/lib/roster/alerts/dispatch.ts[260-268]
Skill: api

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A partial open-shift alert send is recorded as fully delivered, so recipients whose settings lookup or push failed transiently are not retried on replay.

## Fix Focus Areas
- src/lib/roster/alerts/dispatch.ts[238-243]
- src/lib/roster/alerts/dispatch.ts[253-270]
- src/lib/roster/alerts/send.ts[15-43]

## Recommended Fix
Persist successful delivery outcomes at recipient or device granularity. On replay, send only to recipients not yet reached, without resending to those already reached. Add a regression test where one recipient succeeds and another fails transiently, then verify replay reaches only the failed recipient.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit f6ae178 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group


Resolved findings

1. Image scans fail on a partial cache ✓ Resolved
Description
runDockerTrivy uses only metadata.json to decide whether the vulnerability database needs
downloading, without checking that the database file is present. If the database file is missing
while its metadata has a future NextUpdate, the scan runs offline with database updates disabled
and fails instead of repairing the cache.
Code

scripts/trivy-image-scan.mjs[212]

+      if (trivyDbNeedsRefresh(readTrivyDbMetadata(cacheDir))) {
Relevance

●●● Strong

Matches the PR’s stated Trivy cache-repair intent; partial-cache failures are reliability defects.

PR-#3590
PR-#3537

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The deploy skill calls for inspecting deployment environment contracts and failure behavior. The
changed condition reads only metadata; the subsequent scan disables network access and database
updates, so it cannot restore a missing database file.

scripts/trivy-image-scan.mjs[186-212]
scripts/trivy-image-scan.mjs[266-282]
Skill: deploy

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A current metadata timestamp does not prove that the cached vulnerability database file still exists. The offline scan cannot recover from a partial cache.

## Fix Focus Areas
- scripts/trivy-image-scan.mjs[186-212]
- tests/trivy-image-scan.test.ts[153-170]

## Recommended Fix
Check the cached database file as well as its metadata before skipping the download. Add a focused regression test for a future `NextUpdate` with a missing database file.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit fe4bd40 ⚖️ Balanced


No changes from previous review

Grey Divider

Qodo Logo

Comment thread scripts/ops-digest.mjs Resolved
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (2) · ☑ Fixed (2)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 2 fixed
  • ☑ Fixed: Manual runs can trigger premature pages
  • ☑ Fixed: Some staff never receive shift offers

Comment thread scripts/ops-digest.mjs Resolved
Comment thread src/lib/roster/alerts/dispatch.ts Resolved
@gitar-bot
gitar-bot Bot dismissed their stale review October 17, 2026 07:12

Auto-approval revoked — new findings require attention.

Comment thread scripts/trivy-image-scan.mjs Resolved
Base automatically changed from fix/adversarial-r01-r34-20261016 to main October 17, 2026 07:13
@codeant-ai

codeant-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

CodeAnt Nitpicks

2 code suggestions

1. The check uses a pre-lock snapshot. If another link is created before these RPCs run, a non-organiser revokes only the old rows and succeeds while that link remains active.

Race condition · src/lib/teaching/repository.ts:482-485


2. When no push is sent and none fails, such as when no subscriptions exist, this marks the offer delivered, so replay cannot retry after a device is linked.

Logic error · src/lib/roster/alerts/dispatch.ts:242

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Auto-fix isn't included in your plan ↗

Upgrade to Team

- worker contract test now proves the chosen deadline is attached to the RPC builder
- incident runbook uses the variable names the app actually reads
- release manifest picks the commit in the runtime resolver's order, skipping invalid values
- ops digest accepts the shorter gaps a manual run leaves between scheduled runs
- Trivy refreshes a cached vulnerability database once its NextUpdate has passed
- a partially delivered open-shift offer is recorded as delivered, so a replay does not resend it to everyone
- a non-organiser revoking display links by occurrence is refused when someone else created one, instead of a silent partial revoke

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BigSimmo
BigSimmo force-pushed the fix/pr3590-review-followups branch from 345a5ea to f6ae178 Compare October 17, 2026 07:22
@BigSimmo

Copy link
Copy Markdown
Owner Author

Rebased onto main after #3590 merged (now one commit, f6ae178). I dropped two commits that qodo-code-review[bot] pushed to this branch:

  • 345a5ea added migration 20261016220000_roster_open_post_alert_recipient_delivery.sql. Merging it would create a table in the live production database, which needs the owner's explicit approval for that specific change, and this PR is meant to carry no migrations. Per-recipient delivery tracking can be proposed separately.
  • c4a085e limited ops-digest evidence to 1-hour gaps. The digest runs every 6 hours (15 */6 * * *), so scheduled runs alone could then never build the three-window evidence, and the change failed the new regression test. The 6-hour limit keeps the previous scheduled behaviour and adds manual runs in between.
    Test count correction: the focused run is 110/110 across the 8 affected files.

@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 17, 2026 07:22
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 17, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

Comment thread scripts/trivy-image-scan.mjs Resolved
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit f6ae178

…s own

A cached metadata.json with no database file now triggers a download, and the
Java database is refreshed whenever it is missing or stale rather than only
alongside the main database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 17, 2026

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@BigSimmo

Copy link
Copy Markdown
Owner Author

Review follow-up (fe4bd40):

  • Qodo, Trivy partial cache (High): fixed. A missing db/trivy.db now forces a download even when metadata.json says the database is current.
  • CodeAnt, teaching revoke race: not changed. The occurrence check reads a snapshot, so a link created between the read and the revoke stays active. That is an existing limit of doing the check in the client. Closing it needs the database command to revoke by occurrence itself, which is a database change and out of scope here.
  • CodeAnt, roster offer marked delivered when nothing was sent: unchanged existing behaviour (failed === 0 already counted as delivered before this PR). Left as is.
  • Gitar, non-organiser can't revoke their own link by occurrence when others exist: intended. They can still revoke their own link by its token, which is what the UI uses. Refusing is safer than a partial revoke that reports success.

🤖 Addressed by Claude Code

@codeant-ai

codeant-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; the changed authorization, alert replay, release identity, and Trivy freshness behavior has focused tests.
  • The Trivy Java database is refreshed independently when missing or stale, addressing the prior risk of repeatedly scanning with outdated data.

Assessed commit: fe4bd404c84a

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit fe4bd40

@gitar-bot gitar-bot Bot 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.

Gitar has auto-approved this PR (configure)

@gitar-bot

gitar-bot Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor
Code Review ✅ Approved 2 closed / 2 findings

🔴 High risk · Making occurrence-based teaching-link revocation require organiser status when any link has another or unknown creator could block legitimate non-organiser revocations.

Fixes the seven open #3590 review threads, including the ops-digest adjacency check and per-recipient delivery tracking for open-shift alerts. Hourly-adjacency check means scheduled ops-digest runs can never page, and failed per-recipient persistence no longer causes a replay to resend to everyone. No issues remain open.

✅ 2 closed
✅ Bug: Hourly-adjacency check means scheduled ops-digest runs can never page

📄 scripts/ops-digest.mjs:108-114
ops-digest.yml runs on 15 */6 * * *, so the scheduled runs that write to .ops-digest-rpc-history.json fall into UTC-hour buckets six hours apart. With the gap check now gapMs === hourlyWindowMs, three scheduled observations are never treated as contiguous. repeatedRpcNames therefore stays empty, and repeated hybrid-RPC failures only page if an operator happens to trigger manual runs in three adjacent hours. This commit reverses the earlier fix from this PR ("accepts any positive gap of up to six hours") without changing the schedule.

Fix: keep the cadence-aware tolerance (gapMs > 0 && gapMs <= 6 * hourlyWindowMs). If adjacency is really required, change the cron to hourly. A regression test that feeds three scheduled 6-hour observations would catch this.

✅ Edge Case: Failed per-recipient persistence makes a replay resend to everyone

📄 src/lib/roster/alerts/dispatch.ts:68-82 📄 src/lib/roster/alerts/dispatch.ts:297-304
recordOpenPostRecipients never checks the { error } that supabase-js returns. It returns no error, and only catches throws. A partial delivery also no longer sets the offer-level marker. So if the new table is missing (code deployed before the migration) or the upsert fails, nothing stops the next replay. deliveredOpenPostRecipients then returns an empty set because its query errors too, and the offer is resent to every recipient it already reached. That is the duplicate this PR set out to remove. The comment "replay will retry if persistence failed" describes that duplication.

Fix: check the upsert's error and return whether it succeeded. If recording failed but at least one recipient was reached, fall back to markOpenPostAlertDelivered, so a replay does not re-alert everyone.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Approved

For more verification details, comment gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Important

Your trial ends in 4 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit a429bfc

@gitar-bot
gitar-bot Bot merged commit 1bfa895 into main Oct 17, 2026
41 checks passed
@gitar-bot
gitar-bot Bot deleted the fix/pr3590-review-followups branch October 17, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔴 Large blast radius size:L This PR changes 100-499 lines, ignoring generated files 🕐 40+ Minutes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant