feat(a2a): stream review findings per-finding at finalize (ADR-0098, #457) - #458
feat(a2a): stream review findings per-finding at finalize (ADR-0098, #457)#458stephane-segning wants to merge 1 commit into
Conversation
…457) Replace the single terminal artifact blob with incremental streaming: at finalize_review, emit one A2A artifact-update per confirmed finding (finding-{file}:{line}, ADR-0032 JSON), then a conclusion `review` artifact (summary + context, no findings blob). The existing terminal COMPLETED status-update stays the sole stream freeze/close. Emit-only change — the PR posts one grouped review through the outbox as before, and the end-of-run refute pass is unchanged. GetTask polling is untouched (rebuilds the combined artifact from the reviews row), so streaming and polling agree on content. On the common posting path the stream now carries the findings at all, instead of closing empty and forcing a GetTask. - events.rs: new append_review_stream (FOR UPDATE serialization, has_final freeze, has_artifact_update idempotency); remove the terminal completed_artifact emit. - mapping.rs: finding_artifact / conclusion_artifact pure builders. - internal.rs: one best-effort call in finalize_review after the summary. - docs/a2a-review-skill.md §5 + docs/adr/0098: caller contract + decision. Tests: per-finding ordering, idempotent re-finalize, non-A2A no-op, freeze-after-terminal, builder unit tests. cargo test -p control-plane: 307 passed. Workspace check / fmt / clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
✅ AI Governance check passedThis PR declares AI usage, references a source of truth, and provides verification evidence. Thank you. |
There was a problem hiding this comment.
Code Review
This pull request implements incremental A2A per-finding review streaming as detailed in ADR-0098. Instead of emitting a single large artifact blob at the terminal transition, findings are now streamed individually as they are confirmed during the finalize phase, followed by a conclusion artifact containing the summary and context. The terminal transition now only carries the status update. Corresponding updates have been made to the documentation, event emission logic, mapping helpers, and test suite. I have no feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
@lightbridge-assistant review |
…en by A2A #458) + present-tense fixes PR #458 (feat(a2a): stream review findings per-finding) already owns docs/adr/0098-a2a-per-finding-review-streaming.md, so this ADR takes the next free number, 0099. Also addresses gemini's ADR-tense nits: convert the two genuinely future-tense statements ("will be replaced", "won't start") to present tense. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-based base + full override) (#460) * docs(adr): ADR-0098 operator OpenCode config overlay for review (file-based base + full override) Records the owner-directed decision (2026-07-17) to (1) move the review OpenCode config from a Rust json! blob to a checked-in OpenCode-native review.jsonc, and (2) let a trusted SysAdmin supply a `review.opencode` overlay deep-merged host-side with FULL OVERRIDE, so they can add custom sub-agents/models/providers and different per-agent access models. Documents the three-layer model (base file → runtime injection → operator overlay), the exact keys the runtime injects/overrides internally (SysAdmin-facing contract), the coverage/read-only floor WARNING (awareness over prevention, per the owner), and the constraints (opencode rejects unknown keys; secrets stay {env:*}; the untrusted checkout is still never a config source, reaffirming ADR-0097 #6). Complements the mediated ADR-0066 MCP path as the trusted-operator power tool. Implementation is a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(adr): renumber operator-config-overlay ADR 0098 → 0099 (0098 taken by A2A #458) + present-tense fixes PR #458 (feat(a2a): stream review findings per-finding) already owns docs/adr/0098-a2a-per-finding-review-streaming.md, so this ADR takes the next free number, 0099. Also addresses gemini's ADR-tense nits: convert the two genuinely future-tense statements ("will be replaced", "won't start") to present tense. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
@lightbridge-assistant review |
1 similar comment
|
@lightbridge-assistant review |
There was a problem hiding this comment.
Lightbridge review
The change correctly implements per-finding review streaming (ADR-0098): findings and the conclusion emit as artifact-update chunks from finalize_review while the task is still non-terminal, carrying a lower seq than the eventual COMPLETED status-update that closes the stream; polling (GetTask) still rebuilds the combined artifact from the reviews row. Verified load-bearing claims against the code: runner ordering (finalize→succeeded) ensures artifacts precede the terminal event; idempotency is transactionally sound (single-tx all-or-nothing, so has_artifact_update never sees a partial stream); freeze-safety holds via has_final + tasks ... FOR UPDATE. SAST found nothing; no P0/P1 defect reproducible from the diff. One non-blocking P2: the finalize_review→append_review_stream wiring (ReviewContext construction + call placement in internal.rs) is not exercised end-to-end — the four new tests in tests.rs call append_review_stream directly, so a regression there wouldn't be caught by a test. All changed source files reviewed (events.rs, mapping.rs, internal.rs, handler/tests.rs); the two docs (a2a-review-skill.md, adr/0098) carry low signal and were not opened. Prior reviews' "ready to merge" stands; nothing to retract (no prior findings were recorded).
ℹ️ 2 additional changed file(s) carry low review signal and aren't counted above: 2 config/docs.
A custom operator OpenCode config (review.opencode) was active and relaxed the review floor: built-in tool task re-enabled (coverage may go blind). Coverage/read-only guarantees may differ from the default.
🤖 AI-generated review — treat it as untrusted, verify before acting; a human owns the final decision (AI governance).
| head_sha: context.head_sha.clone(), | ||
| review_url: None, | ||
| }; | ||
| if let Err(error) = |
There was a problem hiding this comment.
No integration test for finalize→stream wiring
The new append_review_stream invocation here (the ReviewContext constructed from context.owner/name/target_id/base_sha/head_sha, the call placed right after effective_summary) is the one piece of this change not exercised end-to-end: the four new tests in a2a/handler/tests.rs all call append_review_stream directly, so they validate the events.rs layer but not this wiring. A field-mapping regression at this call site (e.g. wrong pr/head_sha, or the call drifting below the buffer-clear so findings is empty) would not be caught. A single finalize_review-level test that seeds an A2A-fronted task, finalizes, and asserts the artifact-update rows appear with the right reviewUrl: null context would close the loop and mirror the repo's "write the regression test rather than trust the change" convention.
Evidence: The added block in internal.rs builds ReviewContext from context and calls append_review_stream(pool, id, &findings, &summary, &review_ctx); the new tests in a2a/handler/tests.rs (e.g. review_stream_emits_per_finding_then_conclusion_before_terminal) invoke crate::a2a::events::append_review_stream directly and never go through finalize_review, so this construction/placement is unverified.
Was this useful? React 👍/👎 to give us feedback
) * docs(adr): unify A2A/MCP/webhook/API under one domain, path-routed Adds ADR-0109 (code-intelligence-api.ai.camer.digital, /a2a /mcp /api/v2 including multi-forge webhook paths), superseding closed Epic #295's own-Deployment/Ingress topology. Fixes the dangling "ADR-0098" roadmap reference to point directly at PR #458, and adds a Retired section documenting #294/#295's closures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(adr): invest in apps/web — session refresh, pagination, per-repo pages (#521) Adds ADR-0110 (session/token refresh, cursor pagination, per-repo detail page, Grafana-embedded Neo4j-graph + analytics panels), reversing the apps/web-retirement direction of ADR-0063/ADR-0064 (both marked Superseded) and closed Epic #241. Updates ROADMAP.md with the new Epic #493 and the #241 retirement note. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1. Summary
This PR changes:
finalize_review, oneartifactUpdateper confirmed finding (finding-{file}:{line}, ADR-0032 JSON), then a conclusionreviewartifact — all before the terminalCOMPLETEDstatus-update, which stays the sole stream freeze/close.completed_artifactblob emit fromappend_transition_events(the terminal transition now carries only the status-update).docs/a2a-review-skill.md§5 and records the decision indocs/adr/0098-a2a-per-finding-review-streaming.md.It solves:
2. Intent
The intent of this PR is:
3. Scope
In Scope
append_review_streamproducer (services/control-plane/src/a2a/events.rs) with the ADR-0077 invariants preserved: gap-freeseq,has_finalfreeze,FOR UPDATErow-lock serialization, andhas_artifact_updateidempotency for re-finalize.finding_artifact/conclusion_artifactbuilders (mapping.rs) + unit tests.finalize_review(http/internal.rs) after the summary is computed.Out of Scope
4. Verification
I verified this change by:
Commands run:
Results:
New tests: per-finding-then-conclusion ordering before the terminal event; idempotent re-finalize (
has_artifact_update); non-A2A run no-op; freeze-after-terminal (has_final); updated terminal-replay test (no artifact on the transition);finding_artifact/conclusion_artifactbuilder unit tests including the streaming⟷polling parity assertion.Rebased onto current
main(b8d3223): the A2A files applied cleanly;internal.rsauto-merged with the Restate-egress removal (no conflict). ADR renumbered 0094→0098 aftermainclaimed 0094–0097.5. Screenshots / Evidence
docs/adr/0098-a2a-per-finding-review-streaming.md.docs/a2a-review-skill.md§5 (updated stream ordering + permalink note).6. Risk Assessment
Risk level:
Potential risks:
GetTask).Mitigation:
reviews.findings==GetTask— asserted by a parity unit test;datapart is byte-identicalserde_json::to_value(finding).has_artifact_update(idempotent re-finalize) +has_final(freeze) guards, both covered by tests; the append is best-effort so a failure never fails the finalize.7. AI Usage Declaration
AI was used for:
Human verification:
8. Reviewer Focus
Please focus your review on:
Specifically: the event ordering/freeze invariants in
append_review_stream(does it correctly land before the terminal event and never after a freeze?), the streaming⟷polling parity claim, and whether the conclusion-carries-no-findings-blob shape is what A2A consumers want.