refactor(stats): rename InclusionTracker to OutcomeTracker, and close out the spec - #81
refactor(stats): rename InclusionTracker to OutcomeTracker, and close out the spec#81bdchatham wants to merge 4 commits into
Conversation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The type stopped being about inclusion when it started reading receipts. It resolves a transaction to committed or reverted, and inclusion is one of the states it reports rather than the thing it measures. Type and filenames only. Log strings keep "inclusion tracker", because they are what an operator greps and what the runbooks name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR SummaryLow Risk Overview Adds Updates Reviewed by Cursor Bugbot for commit 10e1a5f. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
@seidroid review |
There was a problem hiding this comment.
Mechanical rename of InclusionTracker to OutcomeTracker (types, methods, and two filenames) plus a doc-comment correction noting that dropped_at_handoff now has a producer. The rename is complete with no dangling references anywhere in the tree, exported metric names and inclusion tracker: log strings are deliberately preserved, and the updated doc text matches the actual non-blocking Submit implementation.
Findings: 0 blocking | 0 non-blocking | 0 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- None at the file/PR level.
T018 and T020, the last two open tasks in the spec. The behaviour was already right and nothing held it there. Extracting the choice into trackingEndpoint makes both halves of TOT-021 testable: reads land on a peer when one is configured, and fall back to the load node when it is the only one, saying so rather than competing with the workload in silence. TOT-022 follows from the shape. The tracker takes one endpoint and derives the head stream from it, so a caller cannot take heads from one node and status from another. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Added the last two open tasks, T018 and T020, so this closes the spec at 43 of 43. The behaviour was already right and nothing held it there. Extracting the choice into Both mutation-checked: reads landing on the load node despite a configured peer, and a single-node run not announcing that its reads compete.
|
There was a problem hiding this comment.
The InclusionTracker→OutcomeTracker rename is complete and consistent across types, methods, filenames, the metrics registry, and docs, and the sender/doc.go conservation-identity correction matches the real non-blocking Submit. The newly added trackingEndpoint helper is behavior-preserving; two non-blocking notes concern an undocumented precondition and a test that re-derives the invariant it claims to pin.
Findings: 0 blocking | 2 non-blocking | 2 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- None at the file/PR level.
- 2 suggestion(s)/nit(s) flagged inline on specific lines.
Inline comments (could not post inline; listed here)
main_test.go:109(RIGHT) -- [suggestion] This test re-derives the composition in test code rather than asserting the production wiring.trackingEndpoint()andutils.GetWSEndpoint()are both production, but nothing here observes thatOutcomeTracker.Runis actually called with the tracking endpoint — the WS derivation happens insideRun(wsEndpoint := utils.GetWSEndpoint(endpoint)), while the test performs it itself.
So the doc comment's claim ("the caller cannot split them ... This pins that") overstates what is pinned: if main.go:320 were changed to inclusionTracker.Run(ctx, cfg.Endpoints[0]), this test would still pass green. What it currently proves is that trackingEndpoint prefers ReceiptEndpoint (already covered by TestStatusReadsLandOnTheTrackingNode) plus that GetWSEndpoint is deterministic.
To actually pin TOT-022, the assertion needs to observe the endpoint Run receives — e.g. have the head server in stats/inclusion_run_test.go record the endpoint it was dialed on, or restructure so both the status source and the head subscription are built from one value the test can assert against.
main.go:475(RIGHT) -- [suggestion]cfg.Endpoints[0]panics ifEndpointsis empty andReceiptEndpointis unset. The sole caller is guarded bylen(cfg.Endpoints) > 0at line 294, so this is safe today, but now that the logic is a package-level function the precondition no longer travels with the code — andTestOneNodeStillWorkspins the single-node path without covering the zero-endpoint one.
Worth either stating the precondition in the doc comment ("requires at least one endpoint when ReceiptEndpoint is empty") or returning "", false for the empty case, so a future caller gets a misconfiguration signal rather than an index panic.
Eighth and last in the outcome-tracking stack. Closes the Polish phase, and the feature.
The rename
InclusionTrackerstopped being about inclusion when it started reading receipts. It resolves a transaction to committed or reverted; inclusion is one of the states it reports rather than the thing it measures.Type and filenames only, as its own commit. Log strings keep
inclusion tracker:— they are what an operator greps and what the runbooks name, so renaming them would break the thing the rename is supposed to clarify.The conservation identity
sender/doc.gosaiddropped_at_handoffwas a term of the identity that nothing produced yet. #80 gave it a producer, so the note now says what it is.make verify— what it actually saidcheck-lint-pinlinttestbuild--helpsmokecheck-bindingscheck-bindingsneeds Docker to recompile the Solidity, and the daemon is not running on this machine. No contract changed anywhere in this stack, so the bindings cannot have drifted, but I did not verify that locally and CI will.One finding worth naming
lintfailed the first time I ran it, on threeQF1012findings instats/execution_outcomes.gofrom #78. I had earlier reportedstaticcheckas clean on that file, and that was wrong: barestaticcheckdoes not enable the QF category, andgolangci-lintdoes. Fixed at the bottom of the stack in #78 and merged forward, so every PR here is lint-clean rather than only the last one.Mutation pass
Every guard in this stack was broken on purpose before it was believed. Fourteen mutations across the five PRs, all caught:
reapAfterstatus_unavailablerow droppedRejected()underflowsDeferred, with its condition intact
D-2, whether a hand-off drop voids the run. Un-defer when a run first drops a hand-off outside a deliberate overload test. That has not happened, and inventing a verdict rule before it does would be guessing at a threshold with no run behind it.