Repository navigation
fix: share transition history across a pull request's events, and release 0.0.5 - #52
Merged
Merged
Conversation
…ease 0.0.5 A route on `pr_review_requested` matching `reviewersAdded` could never fire. Not intermittently — never, including the "Reviewer agent" template this ships in the route catalog. Observations were keyed `<triggerType>:<ref>`, so every trigger type kept its own transition baseline. But `pr_review_requested` is only emitted once a reviewer is outstanding, so that baseline came into existence at the exact moment of the transition it existed to detect: the cycle a reviewer is added is the row's first sight, which deliberately reports no changes, and by the next cycle the reviewer is no longer new. Every unit test passed because they all hand `changes` to the rule engine directly. None ran collect -> observe -> match, which is the only place the two halves disagree. - What changed belongs to the *item*, not to the trigger type it raised. Observations are keyed on `ref` alone and the poll loop observes once per item per cycle, so both events a pull request raises read one history. `pr_event` is emitted for every open PR whether or not a review is outstanding, which is what makes its baseline the right one to share. - `migrateObservationKeys` promotes existing `<type>:<ref>` rows on boot, so an upgrade does not reseed a history the install already had and go quiet for a cycle. `pr_event` wins where both rows exist. - `test/routing/trigger-pipeline.test.ts` wires collection, observation and matching together as `main()` does, and covers the request, the outstanding request, the re-request, and first sight of a PR whose reviewer predates it. Debuggability, which is the reason this survived: the cycle summary's `no-route` is equally consistent with a typo'd login, a missing target, and a transition that was never recorded. - `explainRule` names the one clause that rejected an event, in the operator's vocabulary. `matchesRule` is now *defined as* `explainRule(...) === undefined` rather than written beside it, so the explanation cannot drift from the decision. - `sentinel0 explain [--project <id>] [--ref <owner/repo#n>]`, over a new `GET /routes/explain`, re-collects live triggers and reports every route's verdict per item. It reads history through the new `changesSince` and never `observe`: a diagnostic that advanced the baseline would consume the transition being asked about and answer differently the second time. - The output says plainly that a matching route may still not run — the dedupe ledger, one-run-per-agent and agent resolution all come after matching. CLAUDE.md and docs/routes.md carry both halves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNEqbFkMFoPiiVnJGxpjj2
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.
A route on
pr_review_requestedmatchingreviewersAddedcould never fire. Not intermittently — never, including the Reviewer agent template this ships in the route catalog.Reported against this config, which is correct as written:
{ "trigger": { "type": "pr_review_requested", "provider": "github", "projectId": "trackside" }, "guard": { "refire": "per-change", "markers": true }, "match": { "reviewersAdded": { "any": ["EomiAIBot"] } } }The bug
Observations were keyed
<triggerType>:<ref>, so every trigger type kept its own transition baseline. Butpr_review_requestedis only emitted once a reviewer is outstanding — so that baseline came into existence at the exact moment of the transition it existed to detect:changesEomiAIBotaddedundefined→ suppressedEomiAIBotstill therereviewersAdded: []First sight reporting no changes is the backlog guard and is correct; the mistake was giving this event type its own baseline at all.
Every unit test passed because they all hand
changesto the rule engine directly. None ran collect → observe → match, which is the only place the two halves disagree.The fix
refalone and the poll loop observes once per item per cycle, so both events a pull request raises read one history.pr_eventis emitted for every open PR whether or not a review is outstanding, which is what makes its baseline the right one to share.migrateObservationKeyspromotes existing<type>:<ref>rows on boot, so an upgrade does not reseed a history the install already had and go quiet for a cycle.pr_eventwins where both rows exist.test/routing/trigger-pipeline.test.tswires collection, observation and matching together asmain()does, and covers the request, the still-outstanding request, the re-request after a review, and first sight of a PR whose reviewer predates it.Debuggability
This is the reason the bug survived. The cycle summary's
no-routeis equally consistent with a typo'd login, a missingtarget, and a transition that was never recorded.explainRulenames the one clause that rejected an event, in the operator's vocabulary.matchesRuleis now defined asexplainRule(...) === undefinedrather than written beside it, so the explanation cannot drift from the decision.sentinel0 explain [--project <id>] [--ref <owner/repo#n>], over a newGET /routes/explain, re-collects live triggers and reports every route's verdict per item:changesSinceand neverobserve: a diagnostic that advanced the baseline would consume the transition being asked about and answer differently the second time it was put.duplicate,agent-busyandunknown-agent.Release
0.0.5across the workspace viapnpm version:set, including the internal@sentinel0/*pins in the publishedclipackage.Verification
pr_review_requestedfailure was reproduced through the real pipeline before the fix (the new pipeline test fails against the old keying) and passes after.🤖 Generated with Claude Code
https://claude.ai/code/session_01QNEqbFkMFoPiiVnJGxpjj2