research.completed C-Outbox emit + reusable ProvenanceHandoff SDK capability (P2 Go + P3) - #3
Merged
Conversation
…-Outbox) INT-02 Behavior 1, control-plane Go half. The research.completed event must append to event_outbox in the SAME transaction as the execution's terminal succeeded write (C-Outbox) — otherwise a dual-write bug reintroduces itself (run marked done, event lost, or vice-versa). - internal/events/research_completed.go: builds the research.completed CloudEvent (envelope + small owner DTO) from a terminal execution, gated on the deep-research node id + succeeded status. Mirrors the shape already shipped by silmari-af-deep-research/research_completed_event.py (the canonical producer + golden fixture). - internal/storage/event_outbox.go: AppendEventOutboxTx appends on the caller's *sql.Tx via raw SQL (postgres RETURNING seq / sqlite LastInsertId), since the existing AppendEventOutbox appends via GORM on its own connection and can't share a transaction. - internal/storage/execution_records.go: UpdateExecutionRecord now delegates to UpdateExecutionRecordWithOutbox, which appends the outbox record on the same tx before Commit(). - internal/handlers/execute.go: completeExecution (the one confirmed terminal-succeeded write path for every agent call) type-asserts its store to an ExecutionOutboxUpdater capability and wires in the emit when available, falling back to the plain path otherwise. Closure tests use a real on-disk SQLite store via database/sql transactions (matching internal/events/durable_bus_test.go's existing pattern for same-tx claims): prove the append lands in-tx, a rollback discards it, the gate skips non-research/non-succeeded executions, and the old plain path never appended (red-at-seam baseline). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
📊 Coverage gateThresholds from
✅ Gate passedNo surface regressed past the allowed threshold and the aggregate stayed above the floor. |
📐 Patch coverage gateThreshold: 80% on lines this PR touches vs
✅ Patch gate passedEvery surface whose lines were touched by this PR has patch coverage at or above the threshold. |
CI's coverage-summary gate failed at 75.00% patch coverage (min_patch=80%, 34/139 touched lines uncovered). Closes the reachable gaps: - research_completed.go: cover the nil-execution guard directly; simplify BuildResearchCompletedOutboxRecord to stop duplicating the succeeded-only check that BuildResearchCompletedEvent already owns (the duplicate made its own error branch dead code) — the existing SkipsNonSucceeded test now exercises the real (single) validation path. - execute.go: add tests for the "already cancelled/waiting" guard inside completionUpdater (pre-existing logic, newly extracted into a named closure by the previous commit). - event_outbox.go: cover the empty-payload default, and drive the postgres RETURNING-clause branch directly — mattn/go-sqlite3 supports RETURNING, so wrapping the same real on-disk *sql.Tx with mode="postgres" exercises that branch's actual SQL + Scan behavior without a live postgres connection. Also cover the dead-tx error path for both branches. Remaining 8 uncovered patch lines (94% final) are defensive-only branches not reachable without contriving the failure (json.Marshal erroring on a fixed struct with no cyclic/unsupported fields, sql.Result.LastInsertId failing on a normal rowid-table insert, and a fake test store that pre-checks existence and can never invoke the updater with a nil record the way the real DB-backed store does on a genuinely missing row). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… kit (Behaviors 1-8) Extract the Phase-2 research→reels handoff pattern into a drop-in AgentField ProvenanceHandoff capability with ports, adapters, and a conformance kit certifying R1–R4 rules. SDK capability (provenance_handoff.py): - CompletionEmitter / CompletionConsumer / ProvenanceStore ports (@runtime_checkable Protocol encoding R1–R4) - CompletionEnvelope value object (CloudEvents 1.0, subject == correlation_id) - OutboxCompletionEmitter adapter (appends via DurableExecutionBus.Publish) - IdempotentConsumer adapter (dedup on correlation_id, store.has() guard) - build_envelope() with depth/size validation Conformance kit (testing/conformance.py): - run_provenance_conformance() certifies R1–R4 against a fake outbox - C-Outbox and C-AtLeastOnce delegated to live-Postgres @integration proofs - Deliberately-broken fixture assertions (R1/R2/R3/R4 each proven red) Cross-language golden fixture (testdata/completion_envelope.golden.json): - Go round-trip test asserts same shape as Python testing.py → testing/ package conversion: - Moved to testing/_testing_helpers.py with fixed imports - Added conformance.py to the package Behavior 6: Phase-2 refactored onto generic port: - ReelAfConsumer + ReelAfProvenanceStore adapters prove the existing research→reels pattern is expressible via the generic ports - Conformance kit certifies the pair R1–R4 - All Phase-2 suites remain green (645 carousel-impl, 22 Go research) Test coverage: 41 new Python tests (7 files), 2 new Go tests. All pass: SDK 1664 passed, Go events green. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Performance
✓ No regressions detected |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Control-plane + SDK side of the cross-app research→reels hand-off, governed by
specs/cross-app-handoff.pattern.md. Pairs with the reel-af PR (tha-hammer/silmari-reels-af#12).What's here
2445e16b,288e1b89):research.completedCloudEvent is appended toevent_outboxinside the terminalsucceededstate-write transaction — the run-completion write and the event both commit or neither does (kills the dual-write bug). NewAppendEventOutboxTx(append on the caller's*sql.Tx),UpdateExecutionRecordWithOutbox, and anExecutionOutboxUpdatercapability-interface hook incompleteExecution(falls back cleanly, so existing mock stores compile unchanged). Gated onAgentNodeID == meta_deep_research+succeeded. Small owner DTO only — never the body (C-Notification);subject = execution_id(C-Correlation).7e942a1a):agentfield/provenance_handoff.pyimplements the spec's Producer port (OUT-P) + Consumer port (IN-C/OUT-C) as a drop-inProvenanceHandoffcapability, plus a conformance kit so the four waiting apps plug in by implementing the port — not by re-solving integration or writing each other's tables. Behaviors 1–8.Verification
go build ./...+go vetclean; events/storage/handlers (+subpackages) all green. Same-tx closure proven via real SQLiteBeginTx/Commit/Rollback.🤖 Generated with Claude Code