Repository navigation
feat: a state says when it began (ADR 0087) - #131
Merged
Merged
Conversation
`sources[].failing` and `shipping.halted` were bare booleans in a payload that supersedes, so a state that ended left nothing behind: a watch that failed for nineteen minutes and recovered produced bytes identical to one that never failed, and an outage shorter than the flush interval was never stated at all. Both instants already existed and were discarded on the way out — `watchHealth` records where each run of failures began, and the halt is now stamped by a clock the shipper did not have. The booleans stay for the compatibility window of backend-requirements.md §6 and are derived from the instants, because `buf breaking` runs the `FILE` category, which refuses a field deletion even with the number reserved (measured, not assumed). `synced` is deliberately untouched: it is a latch, and the instant it would carry is the payload's own `since`. Also corrects a promise that had gone stale: security.md said the startup read covers five kinds and named five, while cmd/agent/recover.go has resumed six since ADR 0080 added `goroutine_counts`. go test -race ./... all packages ok GOOS=linux golangci-lint run 0 issues make proto-lint buf lint and buf breaking clean Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA
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.
Slice A of the "when" work: the two fields in
collection_coveragethat arestates rather than counts now carry the instant they began.
The defect
sources[].failingandshipping.haltedwere bare booleans in a payload thatsupersedes — the backend upserts, last write wins, one row is all it is obliged
to keep. So a state that ended left nothing behind. A watch that failed for
nineteen minutes and recovered produced, on the next flush, bytes identical to a
watch that never failed; an outage shorter than the flush interval was never
stated at all. The agent knew and did not say.
haltedwas the sharper case: a halted agent ships nothing, so the payloadcarrying the fact never travels. Its readers are the spool and the metrics
endpoint, and to both "shipping stopped" without "at 04:31 nine days ago" is
nearly the same as silence.
What changed
sources[].failing_since— the instant the run of failures began, not thelatest failure.
watchHealthalready recorded it andSourceHealthsreducedit to a boolean on the way out.
shipping.halted_since— the halt had no clock at all, because nothing hadever asked it for one.
shipping_halted_since_secondson/metrics. One gauge, not two:for a halt the metrics endpoint is the only channel that works, while
source_failingships on every pass and a scrape dates itself.syncedis deliberately untouched — a latch, not a state that ends, and theinstant it would carry is the payload's own
since.Why the booleans stay
§6 of
backend-requirements.mdrequires both forms across a deprecation period,and the schema gate enforces the same independently:
buf breakingruns theFILEcategory, which refuses the deletion of a field even with its numberreserved. Measured rather than assumed — the attempt and its error are in
ADR 0087 §3. Both booleans are now derived from the instant beside them, in one
place each, so no payload can carry a state with no start or a start with no
state.
Riding along: a promise that had gone stale
security.mdsaid the startup read "is a pure read of five kinds and no others"and named five.
cmd/agent/recover.gohas resumed six since ADR 0080 addedgoroutine_counts— which is the list ADR 0077 had itself declared closed,with the instruction that a sixth kind means changing it there. Corrected in the
same diff and named here so it is not mistaken for part of the feature.
What this does not do
A state that ended is still invisible. Recovering that needs occupancy within a
window — how much of an hour the agent spent in a state, and how many times it
entered — which is slice B, a different mechanism with a different cost, and is
neither decided nor pre-judged here.
Evidence
🤖 Generated with Claude Code
https://claude.ai/code/session_01KeZECJXTDHL6Nts95eKMJA