Summary
Research and plan blob persistence for report review results in the Chat.Api → Reporting.Api flow. This is Phase 2 of the ReportReviewerService fail-closed fix, to be implemented after the Phase 1 fail-closed PR merges.
Background
Phase 1 (in progress) converts the 3 fail-open paths in ReportReviewerService to a fail-closed "present with warnings" pattern with in-memory caching via IMemoryCache. Phase 2 adds durable persistence so that once a report is reviewed, subsequent CheckReportStatus calls skip re-review entirely by reading persisted review fields from blob metadata.
Research Areas
New Reporting.Api Endpoint
PUT /api/reports/{jobId}/review — accepts review result, updates blob metadata
- Auth: same
ChatApiAgent policy (no new auth configuration)
- Validation: reject if report status is not
generated (idempotent first review only)
- Error responses: 404 (not found), 400 (not in
generated status), 401/403 (auth)
ReportMetadata Extension (4 new nullable fields)
ReviewedAt (DateTimeOffset?)
ReviewApproved (bool?)
ReviewConcerns (List<string>?)
ValidatedSummary (string?)
- All nullable for backward compatibility with existing blobs
ReportStatus.Reviewed Wiring
ReportStatus.Reviewed constant exists but is never set anywhere in the codebase
SubmitReviewAsync should set metadata.Status = ReportStatus.Reviewed
- Existing switch expressions in
ReportEndpoints.cs and ReportGenerationAgent.cs already handle the Reviewed status
Updated Chat.Api Flow
"generated" → review + PUT review to Reporting.Api + present result
"reviewed" → skip review, present persisted review fields directly
- Phase 1 in-memory cache retained as defense-in-depth (handles PUT failures)
APIM Impact
- One new operation resource in
infra/apps/reporting-api/main.bicep
- API-level JWT policy applies to all operations — no policy changes needed
IBlobStorageService Extension
- New method:
Task<ReportMetadata> SubmitReviewAsync(string jobId, bool approved, List<string> concerns, string validatedSummary)
- Implementation: get metadata → validate status → set review fields + status → upload → return
Estimated Blast Radius
14 files across 3 components:
- Reporting.Api: 4 modified + 2 created (ReportMetadata, IBlobStorageService, BlobStorageService, Program.cs, ReviewEndpoints.cs, tests)
- Chat.Api: 4 modified (A2AReportTool, GetReportStatusTool + test files)
- Infra: 1 modified (reporting-api main.bicep)
Existing Research
Preliminary research is documented in:
.copilot-tracking/research/2026-04-12/report-reviewer-fail-closed-research.md (Phase 2 section)
.copilot-tracking/research/subagents/2026-04-12/blob-persistence-phase2.md
.copilot-tracking/research/subagents/2026-04-12/review-result-persistence.md
Dependency
Blocked on Phase 1 PR (ReportReviewerService fail-closed fix) merging first.
Summary
Research and plan blob persistence for report review results in the Chat.Api → Reporting.Api flow. This is Phase 2 of the ReportReviewerService fail-closed fix, to be implemented after the Phase 1 fail-closed PR merges.
Background
Phase 1 (in progress) converts the 3 fail-open paths in
ReportReviewerServiceto a fail-closed "present with warnings" pattern with in-memory caching viaIMemoryCache. Phase 2 adds durable persistence so that once a report is reviewed, subsequentCheckReportStatuscalls skip re-review entirely by reading persisted review fields from blob metadata.Research Areas
New Reporting.Api Endpoint
PUT /api/reports/{jobId}/review— accepts review result, updates blob metadataChatApiAgentpolicy (no new auth configuration)generated(idempotent first review only)generatedstatus), 401/403 (auth)ReportMetadata Extension (4 new nullable fields)
ReviewedAt(DateTimeOffset?)ReviewApproved(bool?)ReviewConcerns(List<string>?)ValidatedSummary(string?)ReportStatus.Reviewed Wiring
ReportStatus.Reviewedconstant exists but is never set anywhere in the codebaseSubmitReviewAsyncshould setmetadata.Status = ReportStatus.ReviewedReportEndpoints.csandReportGenerationAgent.csalready handle theReviewedstatusUpdated Chat.Api Flow
"generated"→ review + PUT review to Reporting.Api + present result"reviewed"→ skip review, present persisted review fields directlyAPIM Impact
infra/apps/reporting-api/main.bicepIBlobStorageService Extension
Task<ReportMetadata> SubmitReviewAsync(string jobId, bool approved, List<string> concerns, string validatedSummary)Estimated Blast Radius
14 files across 3 components:
Existing Research
Preliminary research is documented in:
.copilot-tracking/research/2026-04-12/report-reviewer-fail-closed-research.md(Phase 2 section).copilot-tracking/research/subagents/2026-04-12/blob-persistence-phase2.md.copilot-tracking/research/subagents/2026-04-12/review-result-persistence.mdDependency
Blocked on Phase 1 PR (ReportReviewerService fail-closed fix) merging first.