Skip to content

Monitoring 4: verify - #111

Open
hhff wants to merge 10 commits into
monitoring-fixfrom
monitoring-verify
Open

hhff wants to merge 10 commits into
monitoring-fixfrom
monitoring-verify

Conversation

@hhff

@hhff hhff commented Nov 5, 2026

Copy link
Copy Markdown
Member

Builds on #110 (merge that first). Monitoring, part 4 of 5: an incident's fix is followed into production and checked, and a fix that doesn't hold comes back to the owner.

What

  • The lifecycle. monitor poll moves each incident with a fix through:
    • owned: its fix ticket exists.
    • mitigating: the ticket merged.
    • verifying: a deploy observation's commit has the merge commit as an ancestor. When no deploy since the merge names a commit this repo knows, the window starts deploy_lag_minutes (15) after the merge, as "assumed deployed".
    • resolved: quiet for the window. The window is verify_hours (24) or three times the fingerprint's usual gap in the kit's own hourly counts, whichever is longer, capped at 7 days. Resolution writes an outcome: valence +1, or 0 and "unverified" when the incident was seen fewer than 3 times.
  • A regression is the fingerprint happening again inside the window, judged by the provider's last_seen, so an old event fetched late doesn't count. It writes fix-regressed (severity 3), reopens the incident, opens a problem on the decision, and asks the owner one question with no default: was it a wrong call, which they correct and the class drops to rung 1, or a sound call whose fix failed, which is a problem and changes no trust? The question recommends the second, and the kit never corrects. wait wakes on a regression. The next fix comes from a new decision.
  • Recurrence. An incident the kit resolved whose fingerprints come back (and aren't ignored) opens a new incident that refs it.
  • Owner-set status. If the owner set an incident's status, it's theirs: verification leaves that incident alone and says so once.
  • Suspected escaped defects. A new error first seen in a release, whose release's merges (between that deploy and the one before) include a ticket that changed the error's top in-app frame, becomes suspected-escaped-defect on that ticket at severity 1. The supervisor promotes it when the fix shows the cause. This runs at most 50 a poll, after the poll has printed what wait needs; wait reads a killed poll's alerts.
  • Findings.
    • fix-unverified: merged a day past the lag with no deploy seen, and it says why: no deploy, deploys without commits, or a commit this repo doesn't have.
    • probe-missed: an escaped defect, or a problem the owner opened about production, that no monitor saw in the 48h before.
  • Retro. A Production line: incidents, fixes verified, resolved unverified, regressed, suspected escapes, and the spend note.
  • Dashboard. The board gets merged and regressed columns. The drawer shows the verify window and the regressed commits. A regressed card waits on the owner's question.
  • Docs and schema. supervisor.md gets "Verifying a fix". The constitution records the regressed-fix question. problem.verify gains from, until, assumed, seen, unverified and regressed, and the supervisor may set it.

Why

  • Regressions stay the owner's call. The owner chose to stay the only one who corrects calls, and a recurrence has innocent causes (stale dynos, cached bundles, replayed jobs, a second cause sharing the frame). So a regression asks, recommending "sound call, failed fix", rather than moving trust.

Tests

The incidents subtest goes on to:

  • owned, then merged with no deploy, then deployed, verifying
  • a late event that isn't a regression
  • an owner-held status left alone, with no repeated writes
  • the regression, with its observation, reopen, problem, question and wake, once
  • a second fix resolving with an outcome
  • a recurrence opening a new incident
  • a suspected escape on the ticket whose merge changed the frame
  • the retro line

Suite: everything passes except the tmux session test, which fails on main in this environment too.


Generated by Claude Code

monitor poll follows each incident's fix: owned once its ticket exists, mitigating once it
merges, verifying once a deploy observation's commit has the merge commit as an ancestor
(assumed, deploy_lag_minutes after the merge, only when no monitor reports deploys).
Quiet for verify_hours, or 3 times the fingerprint's usual gap, resolves it with an
outcome (unverified below 3 occurrences). A recurrence in the window is fix-regressed: the
incident reopened, a problem on the decision, and one owner question (wrong call or sound
call, failed fix), never a correction; wait wakes on it. A resolved incident that comes
back opens a new one that refs it. A new error first seen in a release whose merge changed
its top frame is a suspected escaped defect on that ticket. Findings fix-unverified and
probe-missed, a Production line in the retro, the merged and regressed board columns and
the verify window in the drawer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T5z3hZtpGaQUGsTaAMQrAg
A status the owner set is left alone, so verification never writes a line every poll.
Regressions go by the provider's last_seen, not the kit's fetch time. Suspected escapes
need the release from the observation itself and the previous deploy, and walk only the
merges between them, 50 observations a poll from a high-water mark, after the poll has
printed what wait needs; wait reads a killed poll's alerts. Assumed deploys only when no
deploy since the merge names a commit this repo knows, and fix-unverified says which. The
window uses the kit's own counts and is capped at 7 days; the seen count is kept at its
start. Recurrence is for incidents the kit resolved, skips ignores, and doesn't stack
titles. A regression that died half-way is finished on the next poll. The regression
question blocks its incident. Fix tickets follow superseded_by; deploy commits are
validated and lowercased; fingerprints are read once per process; probe-missed only for
problems about production.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T5z3hZtpGaQUGsTaAMQrAg
An assumed window moves to the real deploy when it turns up, so a slow pipeline isn't a
regression. A regression writes its problem and question before it reopens the incident,
each once, so a run that dies part-way is finished by the next poll. The release before
a suspected escape is the latest earlier deploy from the same monitor whose commit is an
ancestor. A deploy's own time starts the window. The escape marker reads an old file.
fix-unverified follows superseded tickets. The owner-held test has a merged fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T5z3hZtpGaQUGsTaAMQrAg
@hhff hhff mentioned this pull request Nov 5, 2026
claude and others added 6 commits November 5, 2026 05:31
…commit

- a deploy event's release is a version name and its time an ISO time,
  so neither can carry a '(commit <sha>)' that verification reads back
- deploys count only from the monitors' own lines
- verify_hours is at least 1 and deploy_lag_minutes never negative
- the fix ticket comes from live incident-fix decisions only (from #110)
- an event's provider link is shown without userinfo or query
- answer --from-chat refuses a question asked for an owner-only reason

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hhff

hhff commented Nov 6, 2026

Copy link
Copy Markdown
Member Author

Upstream loop: merged #110's update, adversarial review of this PR's diff

I merged monitoring-fix in as a merge commit. The one conflict was in incidentDecisions and fixTicketOf. I kept #110's liveDecision filter and this PR's choice of the latest decision.

I found no blocker: nothing in this diff reaches an owner_only class, runs a command or URL from settings, or opens a path traversal.

Findings

  1. Should-fix, fixed. A release name could fake the deployed commit. deployCommit and deployAt read the commit and time back from the deploy observation's text. So a provider release named x (commit <merge-sha>) made verification treat an undeployed fix as deployed.
    • A deploy event's release must now be a version name ([\w.@+:/-], at most 100 characters).
    • Its at can't contain spaces or parentheses.
    • Deploys count only from the monitors' own lines.
    • Deferred: storing commit and at as their own fields instead of parsing them back from the text would be cleaner. That needs a schema change.
  2. Should-fix, fixed (in Monitoring 3: triage and fix #110). A corrected or hand-written incident-fix decision no longer picks the fix ticket.
  3. Nit, fixed. The regression question (classless, asked for an owner-only reason) could be relayed from chat. answer --from-chat now refuses any question asked for an owner-only reason, as it already did for one-way-door.
  4. Should-fix, fixed. verify_hours must be positive and deploy_lag_minutes can't be negative. A zero or negative window used to resolve the incident on the next poll with nothing observed.
  5. Should-fix, deferred. Multi-repo. isAncestor and knownCommit run git in home only, so a fix ticket in a sibling repo (Tickets in other repos: one fleet, one log and one graph across sibling repos #108) always falls back to "assumed". The fix is to run them in repoOfTicket(t).root. Not done here.
  6. Should-fix, fixed. An event's provider link (source_ref) is stored and shown without userinfo or query.
  7. Nit, deferred. suspectEscapes spawns git once per merged ticket per observation. Caching by sha would fix it.
  8. Nit, deferred. An unrelated classless question that blocks the incident is shown as the incident's open question. This affects only the display.

Tests: the full test/kit.test.mjs on Node 24.14.1: 134/134 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants