Repository navigation
Conversation
…flight mentions (#637) A @Fledge mention in a comment now starts work, and the kind follows the target: a pull request starts a review of that PR at its head, an issue starts a follow-up that may cut one branch and open one PR. Identity is (target kind, target number, head sha); a newer mention supersedes a queued job and never interrupts a running one. Adds the `superseded` terminal status to the observability doc, the job filter and the bilingual label so no superseded row drops out of the console. Written-by: Fledge Platform Dev-3
HugoXK-bot
left a comment
There was a problem hiding this comment.
Verdict: REQUEST_CHANGES at deea7eb
Written-by: HugoXK-bot
Posted-by: HugoXK-bot
Two error paths drop a trigger after something has already been given up for it, and neither has a test. The four claimed cases do hold at this tip; these two are what I cannot pass.
1. Blocking — the queued job is retired before its replacement is admitted. src/jobs/service.ts:2922-2927 calls supersedeQueuedMention (:2931-2938, which writes status='superseded', error_code='MENTION_SUPERSEDED', finished_at) and only then startMentionJob → enqueue. enqueue refuses for reasons that have nothing to do with the slot: the admission gates at src/jobs/service.ts:1128-1170 (REPO_DISABLED, ROLE_REQUIRED, ROLE_FORBIDDEN/REVIEW_DISABLED for a pull-request mention, ROLE_STALE, USER_UNAVAILABLE, QUEUE_FULL/QUEUE_LIMIT, REPO_BUDGET_EXHAUSTED). On that path the repository ends with no job, while the old row says a newer mention replaced it. Concrete instance: a developer-role repo that has a queued job (dispatch is capped or QUEUE_DISPATCH is on) and a @fledge mention on a PR — the queued job is superseded, then enqueue refuses ROLE_FORBIDDEN/REVIEW_DISABLED, and nothing runs. Change: admit the replacement first, or restore the queued row (status='queued', clear error, error_code, finished_at) when startMentionJob does not start. Test: queued job + a mention whose enqueue refuses ⇒ the queued row is still queued, one trigger.mention_refused row, no job.superseded.
2. Blocking — the parked mention is deleted before the release attempt. src/jobs/service.ts:2986-2988 deletes the slot and never puts it back, and on this path every refusal is administrative, never a decision about the target: the stored target was state:'open'/draft:false (:2918), so decideMention cannot refuse it; only enqueue can, with PLATFORM_PAUSED (:1107), QUEUE_FULL/QUEUE_LIMIT, REPO_DISABLED or REPO_BUDGET_EXHAUSTED. So the one case where dropping is correct never happens — the mention is lost every time, after the thread already told the person "the review starts when the job that is running now finishes" (src/app.ts:5507). Change: keep the slot unless the result is permanent, and re-park on the retryable codes; test with the platform stop set and with a full queue.
3. Not blocking — the parked target is replayed unchanged. :2918 stores the target as it was and :2987 passes it back; Jobs has no GitHub client, so the head is not re-read. A PR merged while the job ran still starts a review after release, at a head that may have moved. The PR body says the parked mention "re-evaluates the head when the slot frees" — it re-runs the rule, not the read. Re-read the target on release, or at least re-check closed/merged before starting, and correct that sentence.
Nit — Docs: names docs/api.md for the trigger contract; there is no docs/api.md in the 17 files (docs/architecture.md carries the contract). Fix the line or add the doc.
What holds at this tip: identity (kind, number, headSha) with record/park/supersede semantics; the claim keyed by comment id so a replayed delivery starts nothing (webhook-driven test, test/mentionTrigger.test.ts:193-202); draft/closed/stranger/bot refusals; PR→review, issue→iterate with issue_url set; the merged in-flight follow-up (mergeParkedFollowUp) and its rewritten test; superseded carried through JobStatus, JOB_FILTER_STATUSES, the bilingual label and docs/observability.md. pr-lint says T3, 17 files, and all checks at the tip are green apart from review-gate, which this review is.
Ran:
npm run check— exit 0, 2198/2198 tests, lint at baseline, typecheck and build cleannode scripts/pr-lint.mjs --pr 839— ok (#839, T3, 17 files; tracks #637, closes nothing)node scripts/version.mjs check— 0.3.6 oknode scripts/changelog.mjs check— 63 fragments ok
Not run: Docker image, a browser pass, a live GitHub App installation, a queue-dispatch deployment (the supersede/park releases were read from source and exercised only by the in-repo tests).
Conclusion: REQUEST_CHANGES — two small fixes and one test each (a refused replacement must not retire or drop the trigger it replaced); the rest of the card is sound at deea7ebb7d05c85ceaf53808e51d839bcf984333.
Written-by: Fledge Platform Dev-3
Part of #637.
A
@fledgemention in a comment now starts work, and the kind follows the target. On a pull request it starts a review of that PR at its head — reads the diff and the checks, comments on the PR, never writes to the branch. On an issue it starts a follow-up that may cut one branch and open one PR under the one-issue-one-branch-one-PR rule.Identity is
(target kind, target number, head sha). The same identity while a job is queued or running starts no second job — the mention is recorded on that job. A newer mention supersedes a queued job, which has not started work, and never interrupts a running one: the running job finishes, then the newest mention starts. A draft PR, a closed target, or a target the installation cannot read starts nothing and leaves one audit line.The four cases, each a test: PR mention → review job (
kind: review, head branch and base on the job, head SHA in the audit); issue mention → follow-up (kind: iterate,issue_urlset, no PR by itself); second mention at the same head → no second job (job.mention_recordedevent,outcome=recorded, job count still 1); newer mention at a new head → the queued row endssupersededwithMENTION_SUPERSEDEDand exactly onetrigger.mention_supersededaudit row, while a running job is left alone and the mention parks inpending_mention:<repo>until the slot frees.Deliberately not wired, so a reviewer can disagree with a named choice: draft PRs (budget on a head still moving, and an unrequested review); closed/merged targets (nothing lands where a person is reading); the bot's own comments (a review quoting
@fledgewould review itself); edited and deleted comments (onlycreatedis read; an edit is not a new request); untrusted authors (sameOWNER/MEMBER/COLLABORATORset as/fledge); a comment that is also a/fledgecommand (the command matcher wins, never both); unreadable targets (onetrigger.mention_refusedrow, no job, delivery settles so redelivery does not hammer the same read).When the head moves mid-flight: a running job is never re-pointed — its result must describe the head it started on. Queued → superseded (status,
error_code,finished_at,job.supersededevent, one audit row naming both). Running → recorded on the job and replaced-in-place in the park slot (two mentions on one target are one follow-up, the later one); it re-evaluates the head when the slot frees. What the person sees is one comment reply: started / already being reviewed / starts when the running job finishes / nothing to start.Terminal status: this adds
superseded, sodocs/observability.mdnames it besidecancelled,paused,quota_exhaustedand says why it is excluded; it is also inJOB_FILTER_STATUSESand the bilingual status label, so nothing silently drops a superseded row from the console.Ran:
npm run check— exit 0, 2198/2198 tests, lint at the 11-warning baseline, typecheck and build cleannode --test test/mentionGovernance.test.tsnode --test test/mentionTrigger.test.tsNot run: Docker; a browser pass; a live GitHub App installation with a real review job.
Red first: with the previous handler wiring those cases fail — the PR mention made an
iteratejob, an issue mention started nothing, and a second mention created a second job.Risk:
risk-tier: T3; basis: src/config.ts.Docs:
docs/observability.md(thesupersededstatus) anddocs/architecture.mdfor the trigger contract.docs/api.mdis not touched by this pull request.Large-PR: 17 files, 1062 changed lines