Problem
RollupSummaries gives the calendar-report Lambda the policy returned by summaryReadStatements. That policy allows s3:GetObject on the summaries bucket objects and no action on the bucket itself. src/functions/report-store.ts reads deterministic summary keys and maps NoSuchKey or NotFound to neverComputed.
Amazon S3 returns 403 Access Denied for a missing GetObject key when the caller lacks s3:ListBucket. It returns 404 Not Found when the caller has that permission (GetObject API). The deployed report role therefore turns a supported missing summary into a period failure before neverComputed can reach the report section.
This contradicts the calendar report contract in docs/reports/. A missing source should produce an unavailable section with reason: "incomplete-source".
Reproduction
On AWS with the Rainlytics 0.3.1 report path, run the daily report schedule at a week or month boundary when the requested period includes dates before summary collection began. Daily reports whose summary keys exist are written. The week or month report fails with AccessDenied, even when all period-wide Athena queries succeed.
A production run on 2026-09-01 wrote the two recomputed daily reports, then failed the closing week and month. The next run wrote its daily reports and failed the recomputed month again.
Desired behaviour
The report Lambda can distinguish a missing summary key from a denied read. Missing summaries produce unavailable sections and the calendar report is written. Other S3 access failures still fail the affected period.
The public grantReadingSummaries grant can remain limited to s3:GetObject. The report Lambda needs s3:ListBucket on the summaries bucket ARN because its missing-key behaviour is part of the report contract.
Acceptance criteria
Problem
RollupSummariesgives the calendar-report Lambda the policy returned bysummaryReadStatements. That policy allowss3:GetObjecton the summaries bucket objects and no action on the bucket itself.src/functions/report-store.tsreads deterministic summary keys and mapsNoSuchKeyorNotFoundtoneverComputed.Amazon S3 returns
403 Access Deniedfor a missingGetObjectkey when the caller lackss3:ListBucket. It returns404 Not Foundwhen the caller has that permission (GetObject API). The deployed report role therefore turns a supported missing summary into a period failure beforeneverComputedcan reach the report section.This contradicts the calendar report contract in
docs/reports/. A missing source should produce an unavailable section withreason: "incomplete-source".Reproduction
On AWS with the Rainlytics 0.3.1 report path, run the daily report schedule at a week or month boundary when the requested period includes dates before summary collection began. Daily reports whose summary keys exist are written. The week or month report fails with
AccessDenied, even when all period-wide Athena queries succeed.A production run on 2026-09-01 wrote the two recomputed daily reports, then failed the closing week and month. The next run wrote its daily reports and failed the recomputed month again.
Desired behaviour
The report Lambda can distinguish a missing summary key from a denied read. Missing summaries produce unavailable sections and the calendar report is written. Other S3 access failures still fail the affected period.
The public
grantReadingSummariesgrant can remain limited tos3:GetObject. The report Lambda needss3:ListBucketon the summaries bucket ARN because its missing-key behaviour is part of the report contract.Acceptance criteria
s3:ListBucketon the summaries bucket ARN.incomplete-sourcesection and does not fail the period.403/404missing-key distinction instead of relying only on the simulator'sNoSuchKeyresponse.grantReadingSummariesremains a deterministic-key object read unless its public contract is deliberately changed.