From b164d6e1e2feb0e99e5491d7c5e00bd9497c470d Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Tue, 27 Oct 2026 17:10:43 -0700 Subject: [PATCH] docs: document the pull request lifecycle once and remove passages against it ARCHITECTURE.md gains a Lifecycle of a pull request section after Components, matching main: the hub mails a live manager directly and holds unrouted heads, the listener deploys only for pull requests with no manager and retries a failed wake once, a head waiting only on CI ends its manager until the reconciler wakes it, and each deployment has a local sidecar of its own up to the cap. The components diagram shows both hosts, the judge and code review in a sandbox, and the triage pipeline names the workflow that runs each step and adds the review post. Code review is described as posted by the manager with the verdict in ARCHITECTURE, PRODUCT and the user manual. Root AGENTS.md gains the writer, isolation, placement and size rules; triage-manager and triage-judge get an AGENTS.md and triage-workflows' is rewritten around the shared steps. References to the deleted pr-triage deployment, hedges, host data wording, the stored version 1 and step `for` reading and the missing package rows are fixed. --- AGENTS.md | 8 +- docs/ARCHITECTURE.md | 150 +++++++++++++----- .../Corbits Triage User Manual.md | 14 +- docs/DECISION_MODEL.md | 2 +- docs/IMPLEMENTATION.md | 6 +- docs/PRODUCT.md | 47 ++++-- docs/provisioners/docker.md | 2 +- packages/triage-contracts/README.md | 2 +- packages/triage-judge/AGENTS.md | 5 + packages/triage-manager/AGENTS.md | 6 + packages/triage-manager/README.md | 6 +- packages/triage-workflows/AGENTS.md | 5 +- 12 files changed, 178 insertions(+), 75 deletions(-) create mode 100644 packages/triage-judge/AGENTS.md create mode 100644 packages/triage-manager/AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index 258e6bced..673e05301 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,11 +23,15 @@ Repository-wide rules. `apps/*` and some `packages/*` have their own AGENTS.md w - **The policy drives everything.** A repository's rules and actions live in its policy file, `.corbits-triage.yml` on its default branch, and nowhere else; under the workspace's `policy` automation that file also decides what Triage does on its own. Nothing repository-specific is written into workflow code. - **Workflows are deterministic.** Agents exist only where inference happens (packages/triage-workflows/AGENTS.md). - **Effects are idempotent** under their effect id, host writes included. +- **One writer per pull request.** The manager is the only workflow that writes to a pull request, and it lives until its final post or until its head waits only on CI (docs/ARCHITECTURE.md, Lifecycle of a pull request). +- **Three isolated parts.** Deterministic checks, the judge and code review each have their own limits; one part's limit never changes what another reads. +- **One local sidecar per deployment.** The listener, each manager and each judge run in a local sidecar of their own, up to a capped number; past the cap a deployment goes to a sandbox, and code review always runs in one. +- **Size is a risk signal**, never a hold: a crossed size limit labels a pull request and never makes it wait. - **Grants decide automation.** Whether an action runs on its own is decided from the workspace's role grants, never a hard-coded limit. -- **Downstream work is another workflow.** Reviewing, fixing or cleaning up a pull request is a workflow of its own, started by mail or a signal from an action; never a step inside pr-triage. +- **Downstream work is another workflow.** Reviewing, fixing or cleaning up a pull request is a workflow of its own, started by mail or a signal from an action; never a step inside the manager. - **API first.** Every custom hub route is described in `/openapi.json`. The portal and the extension wrap those routes and are never the only way to do anything. - **Credentials, grants and tenants are Interchange's.** Secrets live in the hub vault and reach sidecars at launch and rotation; never read them anywhere else, never persist them. -- **Hosts.** GitHub and Bitbucket Data Center. A repository record carries `source` (absent = GitHub), every mail names it as `repoSource`, and each host has its own pr-triage package. A change to host behaviour covers both, or says why not. +- **Hosts.** GitHub and Bitbucket Data Center. A repository record carries `source` (absent = GitHub), every mail names it as `repoSource`, and each host has its own listener, manager, judge and code review workflows. A change to host behaviour covers both, or says why not. ## Interchange reference diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 99691fa46..7a65294d4 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -8,10 +8,12 @@ and tool packages the hub publishes and deploys. ## Components ``` -GitHub ──webhook──► Hub ──spawn──► Sidecar ──► listener, manager - ▲ │ ▲ │ - │ │ └──── portal (/api) ───────┤ - └─── PR actions ──┴──── GitHub reads, actions ──┘ +GitHub, Bitbucket DC ──webhook──► Hub ◄──── portal (/api) + ▲ │ mail + │ ▼ + │ local sidecars: listener ──deploy──► manager ──deploy──► judge + │ │ └──deploy──► code review (sandbox) + └──── labels, comment, review, actions ───────┘ ``` - **Hub** (`apps/hub`): the stock Interchange hub plus one composition @@ -30,8 +32,8 @@ GitHub ──webhook──► Hub ──spawn──► Sidecar ──► listene that touches the policy file reads it again. Bitbucket Data Center has a provider hook of its own (`apps/hub/src/provider-hooks`). Both verify with `@corbits/common/x-hub-signature`, the scheme `@corbits/webhooks` 0.4 adds - as `verify: "x-hub-signature"`; once the hub runs that release, the - bridges move onto the stock hook routes. + as `verify: "x-hub-signature"`; the hub runs an earlier release, so the + bridges keep routes of their own. - **Policy file**: a repository's policy is `.corbits-triage.yml` (or `.yaml`) on its default branch, `extends` included, and nothing else; the hub stores no policy text. Every trigger mail and portal read takes it @@ -88,9 +90,9 @@ GitHub ──webhook──► Hub ──spawn──► Sidecar ──► listene had none, the listener was still starting or the hub held no route to it, is also marked `unrouted` and goes out again (see the reconciler). Every window is mailed when it closes, whatever the - head's CI: the pull request's manager waits for CI itself (see + head's CI: the manager and the reconciler hold the CI wait (see `triage-manager-github` below), and a window that sees a check finish - reaches that manager as one more event. + reaches the pull request as one more event. - **Reconciler**: one per host, for workspaces whose listener for the host is live. On an interval it compares each enabled repository's open pull requests with the managers' runs, their recorded verdicts and stored @@ -221,6 +223,65 @@ GitHub ──webhook──► Hub ──spawn──► Sidecar ──► listene minute and on focus it reads the served `index.html`; once its hashed entry script differs from the one the tab loaded, it offers a reload. +## Lifecycle of a pull request + +What happens to one pull request, end to end. The sections after this one +are the detail of each step. + +1. **Event in.** A host webhook or the reconciler names a pull request head, + and the hub records the event. A repository without a policy file + answers `needs-setup` and gets no mail, and a pull request carrying the + policy's pause label (GitHub) is not mailed. When the pull request's + manager is up, the hub mails it directly. Otherwise it mails the host's + listener; a head whose mail reached no listener is held in its + triage-state row as `unrouted` and sent again once one takes mail. For a + policy file it cannot read the hub mails the reason, and the manager marks + the head failed. +2. **Listener.** It gets only pull requests with no manager up. For each it + deploys a manager and wakes it; when the wake mail fails it cancels that + manager and deploys one more, once. The claim on the pull request lets one + manager run for it at a time. +3. **Policy.** The manager triages under the policy the hub resolves from + `.corbits-triage.yml` on the default branch, the only source. +4. **First post.** Unless the first pass decides, the manager posts at once + that the head is in progress, naming what it waits on. +5. **Three isolated parts**, each with its own limits, none of which writes + to the pull request. The deterministic checks run in the manager on every + pass and read every patch up to their own ceiling, whatever the judge's + limits. The judge is its own workflow with its own budget, deployed when + the rules need it once the host's checks have finished, or once the + manager stops waiting for them. Code review is its own workflow in a + sandbox, deployed once the rules gate opens. +6. **Waiting.** While the judge or code review is out, the manager waits for + it, editing its one comment in place. A head that waits only on CI posts + that it waits, and its manager ends; the reconciler re-reads the head's + checks and queues it again once CI settles, and a fresh manager carries on + from step 1. A head is decided with CI still running after it has waited + an hour, and a manager stops waiting for anything three hours after its + claim: it decides, says what it stopped waiting for, and runs no action on + its own. +7. **Final post.** The verdict label, the comment's final state and the code + review go up, then the policy's actions. Each action runs where the + `pr-triage` role's grants, written from the policy file, allow it, and is + otherwise held for a person, who takes it once or allows that kind in the + repository from then on. +8. **End.** The manager ends after step 7, or while its head waits only on + CI. A new head while it is alive restarts its pass on that head. A pull + request with nothing outstanding has no manager; its next event starts + again at step 1. +9. **Where it runs.** Each deployment, the listener, a manager or a judge, + runs in a local sidecar of its own, up to `TRIAGE_MAX_LOCAL_SIDECARS` + (default 8) at once; past that a deployment goes to a sandbox. Code + review always runs in a sandbox. +10. **Invariants.** Workflows are deterministic, effects are idempotent under + their effect ids, nothing repository-specific is written into workflow + code, and size is a risk signal that labels a pull request, never a hold. + +The workspace's automation switch (`corbitsTriage.automation`, `policy` or +`ask`) is a brake for the whole workspace: under `ask` the grants hold only +posting, so every other action is held, and a repository earns automation +one kind at a time through **Always, in this repository** on a held action. + ## Workflows Each is an `onTrigger` mail listener: each mail spawns a child run that @@ -390,14 +451,14 @@ any other tenant or an unknown sender. new one runs. - `triage-manager-github` and `triage-manager-bitbucket-datacenter` (`triageManager`): one live run per pull request, which lives until its head - has its final verdict, and the only workflow that writes to the pull request. It takes + has its final verdict or waits only on CI, and the only workflow that writes to the pull request. It takes mail from the hub (`tenant`) and the listener that deployed it (`invoker`). A mail starts a loop of passes. Each pass takes, which claims the pull request and its pending events and answers the newest pull request mail the hub recorded. Each held Do approved since runs - as pr-triage runs an approval, which skips one whose pull request moved past + through the shared approval step, which skips one whose pull request moved past its head. Then, when the pass has a mail for its head, a judge or code - review report, or an overdue reviewer, pr-triage's own steps run on the + review report, or an overdue reviewer, the shared triage steps run on the newest mail for the head, handed over as `{ taken }`, which they trust as the hub's. A head is unready first: on a pull request mail on GitHub its pass label comes off (`markUnready`) before facts are read. Every pass runs its approvals step, which @@ -558,14 +619,14 @@ any other tenant or an unknown sender. head is stopped by the hub, which a later manager could not do: see intake below. The hub records the verdict done reports as the manager run's, in done's - transaction (`apps/hub/src/manager-routes.ts`), with the row-building code - pr-triage's run logs use (`apps/hub/src/github/verdict-row.ts`). The + transaction (`apps/hub/src/manager-routes.ts`), with the shared row-building + code (`apps/hub/src/github/verdict-row.ts`). The reconciler and Triage again read the managers of the pull requests they look at from their claims and those rows (`apps/hub/src/github/manager-runs.ts`): a manager that is up runs the head of the newest pull request mail recorded for it, so its head is not mailed again; one that went away with a pass open - failed on that pass's head, which is retried with backoff; a recorded verdict settles its head as - a pr-triage run's does. Triage again can queue a head again as soon as its + failed on that pass's head, which is retried with backoff; a recorded verdict settles its head. + Triage again can queue a head again as soon as its manager has reported done, or went away, since it was queued. The hub also checks, every `TRIAGE_LIVENESS_INTERVAL_MS` (a minute by default) and apart from the reconcile pass, whether each workspace's pull @@ -625,15 +686,17 @@ any other tenant or an unknown sender. ## Triage pipeline -The manager's pass runs facts, rules, and, when the rules need no judge, the -verdict. When they do, the judge it deploys runs the model checks and -evaluate, which decides the verdict; the manager posts that verdict. The -repository's policy decides what each step does. The steps below are those -shared ones. Before facts, a pull request mail from the hub naming a GitHub head +Each step below names the workflow that runs it; all but review are the +shared steps of `@corbits/triage-workflows`. The manager runs facts, rules and route on every pass, and evaluate itself when +no question needs asking. Otherwise the judge it deploys runs judge, parts and +evaluate and reports the verdict. Code review is the separate workflow below, +which the manager deploys once the rules gate opens. Mirror, review and +execute are the manager's, the only workflow that writes to the pull +request. The repository's policy decides what each step does. Before facts, a pull request mail from the hub naming a GitHub head mirrors `unready:--::`, which takes the pass label off that head, so it never stands on a head not yet triaged. -1. **facts**: reads the pull request, its files, reviews, CI, commits (the +1. **facts** (manager): reads the pull request, its files, reviews, CI, commits (the first 100 messages in full), the repository's docs and its contribution rules from the host. On GitHub the body is read from GitHub's own rendering (`body_html`, from the full media type) as plain text (`renderedText` in @@ -687,7 +750,7 @@ label off that head, so it never stands on a head not yet triaged. `logic/duplicates.ts`): up to five that share a path on that first page, lockfiles left out, or two title words, most shared paths first (`openPulls`). -2. **rules**: runs the hard rules (built-in rules over host data, text +2. **rules** (manager): runs the hard rules (built-in rules over host data, text rules and code-pattern rules) and, unless one failed or the pull request is a draft or closed, builds the judged rules' questions, or the verdict question when there are none. @@ -720,15 +783,16 @@ label off that head, so it never stands on a head not yet triaged. no patch, so there the rule is unconfirmed whenever an in-scope TypeScript or JavaScript file is added, edited, moved or copied. The extension leaves code-pattern rules out and swaps the native module for a stub. -3. **route**: skips the judge when no question needs asking. -4. **judge**: one System One call per request of questions. Each first-round request and the verdict request also asks +3. **route** (manager): deploys the judge when a question needs asking, and + otherwise runs evaluate in the manager. +4. **judge** (judge): one System One call per request of questions. Each first-round request and the verdict request also asks whether the pull request's text, diff or docs try to direct the judge; a confident yes in any of them leaves every judged rule unconfirmed and the verdict Unsure, whatever the verdict question answers. The only step that infers; see [DECISION_MODEL.md](DECISION_MODEL.md). -5. **parts**: a second judge map asks one question per file of each slice that +5. **parts** (judge): a second judge map asks one question per file of each slice that failed focused, and the verdict question over every rule's result. -6. **evaluate**: decides the verdict, renders the one comment, and resolves +6. **evaluate** (judge, or the manager when route asks nothing): decides the verdict, renders the one comment, and resolves the verdict's steps when the policy's When is woken, skipping each step whose conditions do not hold. While CI is pending every runnable action is held and an approve that may not stand is skipped; the completed @@ -739,12 +803,13 @@ label off that head, so it never stands on a head not yet triaged. mirror (`mark`). Rule results never decide it, so a rule Triage could not check for its own reasons, as code patterns in a patch it could not read in full, neither waits nor stops the wait. The hub records no verdict for a waiting - one, nor for one on a closed pull request (`closed`); the manager keeps - polling until CI finishes. A mail with `noWait` (a manager past its + one, nor for one on a closed pull request (`closed`); a head that waits + only on CI ends its manager, and the reconciler queues it again once CI + settles. A mail with `noWait` (a manager past its 60-minute CI wait, a head's last attempt, or Triage again) is decided instead of waiting. A CI re-read that queues a head again keeps its attempts, since the head did not fail, and sets `noWait` on its last. -7. **mirror**: writes the verdict label when the `pr-triage` role holds +7. **mirror** (manager): writes the verdict label when the `pr-triage` role holds Labels, and the marker comment when it holds Comment; on Bitbucket, Comment also writes the verdict as the `Corbits Triage` build status. It runs before execute, so a merge or close finds the final verdict on the head. A @@ -756,7 +821,11 @@ label off that head, so it never stands on a head not yet triaged. with no checks (facts' `checks: "none"`) gets no waiting label: the marker only takes the pass label off. The portal reads the wait from the live manager's claim (`waitingSince`), not the label. -8. **execute**: runs the actions whose kind the `pr-triage` role holds on the +8. **review** (manager): posts the code review reported for the head through + the host's `send_code_review` tool under the effect id `review:`, + after mirror and before execute, so the verdict and the review go up + together. A review with no summary and no finding posts nothing. +9. **execute** (manager): runs the actions whose kind the `pr-triage` role holds on the repository. Every other action is held in the verdict (`held: true`). One the role may write, held only because CI is still running on the head, carries the CI state that holds it (`ci: "pending"`), which the portal reads as @@ -797,12 +866,12 @@ per repository and, only while `corbitsTriage.automation` is `ask`, removes one through the stock grant delete route by grant id; otherwise the next policy read would grant it again. -Every step but the judge is a deterministic `action`. Facts, mirror and -execute reach the pull request's host through its credential, bound to each +Every step but the judge is a deterministic `action`. Facts, mirror, review +and execute reach the pull request's host through its credential, bound to each of those action handlers (`credentialBindings` with `action: `): each declares the host's handle in `credentials` and a capability in `effect.requires` (`pull-request.read` for facts, `pull-request.write` for -mirror and execute), and calls the host only inside `ctx.perform`'s `run`. +mirror, review and execute), and calls the host only inside `ctx.perform`'s `run`. Facts is one effect; each mirror write and each Do is its own effect under its call id, the Do's effect id, so a resumed run does not write it again. The calls are the host tool's (`githubCalls`, `bitbucketCalls`), so a host @@ -815,9 +884,9 @@ and **Review again** asks for one whatever the file says; both need a code review provider's key. A workspace runs one provider, the one whose key it stores in Settings → Code review -(`CODE_REVIEW_PROVIDERS` in `triage-contracts`; only Anthropic today). A -pull request's code review runs in its manager: the hub attaches the review -request to a pull request mail whose `codeReview` is on, which the policy +(`CODE_REVIEW_PROVIDERS` in `triage-contracts`, which lists Anthropic). The +manager deploys the code review workflow in a sandbox and posts its result. The +hub attaches the review request to a pull request mail whose `codeReview` is on, which the policy file's `code-review` block or Review again sets, with the host's unified diff cut at a line boundary past 100,000 characters; on GitHub a diff too large for the diff API is built from the files' patches. The files a cut leaves unread, read from the @@ -877,7 +946,8 @@ deadline, and the results its code reviews reported through the reviewer-result route that a pass took or that name a reviewer of the claim. A review that did not finish shows as failed; its run log keeps the error. -Sending a review to GitHub is a person's write through +The manager posts each review with its verdict (step 8 of the triage +pipeline). A person sends a finished review the manager has not posted through `POST /api/integrations/github-actions/{tenantId}` (`action: "code-review"`, the Comment grant): the hub reads the newest finished review the same way and posts it as one `COMMENT` review pinned to its head. The review's marker @@ -976,7 +1046,7 @@ workspace's one `bitbucket-datacenter` token. The vault holds the App as webhook secret under `github-hook`. The manifest callback and `PUT /api/integrations/github-app/:tenantId` write the key and the token minted from it in one transaction; App JSON a stock credential write leaves in -`github` is moved to `github-app` when the hub next reads the key: the next portal read, installation sync or token loop pass, so at most 45 minutes later; until then that credential holds App JSON and a deployment launched from it is handed the key. The hub +`github` is moved to `github-app` when the hub next reads the key: the next portal read, installation sync or token loop pass, so at most 45 minutes later; a deployment launched from that credential in between is handed the key. The hub mints the token on boot and renews it every 45 minutes. Every write holds the tenant's advisory lock, and a renewal stores its token only while the key it minted from is still the workspace's, so a replaced App is never overwritten @@ -1131,8 +1201,8 @@ read it, and passes the rest of the request to stock. recorded file is older than its own last one; the `@corbits/*` packages' migrations keep no record of their own. - Triage's tables, in the `corbits_triage` schema: `verdict` (verdicts per - head, with the outcomes of the actions their run executed, copied from a - pr-triage run's log or reported by a manager's done; a manager run's newest + head, with the outcomes of the actions their run executed, as a manager's + done reports them; a manager run's newest verdict on a pull request replaces the one it recorded before, keeping the outcomes of Dos already written on its head. The verdict's `startedAt` is when the run first took its head, and its trigger the newest mail for it. A verdict's diff --git a/docs/Corbits Triage User Manual/Corbits Triage User Manual.md b/docs/Corbits Triage User Manual/Corbits Triage User Manual.md index 6ec6390a1..e18e4607d 100644 --- a/docs/Corbits Triage User Manual/Corbits Triage User Manual.md +++ b/docs/Corbits Triage User Manual/Corbits Triage User Manual.md @@ -26,7 +26,7 @@ Think of it as a first reader. It does the routine checking (Did the tests pass? ### What it never does on its own -Nothing runs until you turn it on. Connecting GitHub or Bitbucket Data Center only records your repositories, with triage switched off. A repository is checked only after someone turns on **Triage pull requests in this repository**, and only once its policy file is on the default branch. What Triage then does on its own is what that file names, unless your workspace is set to ask first (see [Actions Triage takes on its own](#actions-triage-takes-on-its-own)); closing on Unsure, and merging on anything but Pass, always wait for a person. Code review, too, runs only in repositories whose [policy file](#writing-the-file) turns it on, and it writes to the pull request only when someone sends it. +Nothing runs until you turn it on. Connecting GitHub or Bitbucket Data Center only records your repositories, with triage switched off. A repository is checked only after someone turns on **Triage pull requests in this repository**, and only once its policy file is on the default branch. What Triage then does on its own is what that file names, unless your workspace is set to ask first (see [Actions Triage takes on its own](#actions-triage-takes-on-its-own)); closing on Unsure, and merging on anything but Pass, always wait for a person. Code review, too, runs only in repositories whose [policy file](#writing-the-file) turns it on, and Triage posts its review on the pull request with the verdict. ### The three parts @@ -42,9 +42,9 @@ Code review reads each new commit of a pull request for bugs: mistakes in the ch - **What it costs.** Each commit it reviews is billed to your Anthropic account. It reviews a pull request when it is opened and after every push while it is not a draft, and **Review again**, offered once a pull request has a review, is one more review. A push that leaves the change exactly as it was reviewed, such as a rebase onto a newer base that changed none of the files it touches, is not reviewed again: the new commit keeps the last review, and the section says **Carried over from** the commit it was made for. **Review again** reviews it anyway. - **Approve and merge.** With code review on, Triage approves or merges a pull request on its own only once the review of its latest commit is clean: it found nothing, or, with `clean: { below: }` in the policy file, nothing at that severity or above (P0 is critical, P1 high, P2 medium, P3 low). A review with such a finding fails a pull request that would otherwise pass: its comment says how many problems the review found, the actions you set for Fail run, and Triage takes back an approval it gave an earlier commit. A pull request that is Unsure or already fails keeps its verdict with the review's problems added to its reasons, and approve and merge wait for you. Either way, Triage's comment asks the author to fix the review's problems. A review that failed or ran past its hour, one that read only part of the change, and a review that never ran because the rules failed or had not settled leave approve and merge waiting for you in the Inbox, with the reason beside them, and Triage takes back an approval it gave an earlier commit. A check the file turns on that can't run yet, such as `security`, holds approve and merge until it can. A pull request the policy file chose not to review, by its author, its verdict or its changed files, is approved as if code review were off. - **Where you see it.** While a review runs, the line at the top of Triage's reading pane says **In progress: Waiting on code review**, and until the first review its **Code review** section says **Code review: awaited**. Once it is done it is under **Code review**: each bug with how severe it is, its file and line, and the code it points at. **Review again** reviews the latest commit once more. A very large change is read up to a size limit, and the review says how much of it was read and lists the files it did not review in full (a long list starts collapsed), or says some files were not reviewed when it cannot tell which. A review that stopped before it finished reading the repository says **Stopped early: the reviewer reached its limit for reading the repository.** -- **Sending it to GitHub.** On a GitHub pull request, **Send to GitHub** posts the review as one review from the Triage App: each finding on a changed line is a comment on that line, and the rest are listed in the review. A finding whose lines run past the changed ones sits on its first changed lines and names the others. A review that stopped early carries the same line in the posted review, and a review of a cut change lists the files it did not review in full. A review too long for GitHub lists what fits and says how many more are in the portal. It is sent once per commit; the section then says **Sent to GitHub**, and sending it again only tells you it is already there. A review carried over to a newer commit is sent on that commit, unless it was already sent on the earlier one, where its comments stay on the same lines. A review of a newer commit is sent as a new review, and the earlier one stays. -- **Sending it to Bitbucket.** On a Bitbucket Data Center pull request, **Send to Bitbucket** posts each finding on a changed line as a comment on that line, then one comment with the summary and the rest. A finding whose lines run past the changed ones sits on its first changed line and names the others. It is sent once per commit: sending it again posts only what is missing, then says **Sent to Bitbucket**. It needs the Comment permission for the repository. -- **What it does not do.** It only reads code; it never runs it. Nothing is posted to the pull request until someone sends it. +- **On the pull request.** Triage posts the review with its verdict. On GitHub it is one review from the Triage App: each finding on a changed line is a comment on that line, and the rest are listed in the review. A finding whose lines run past the changed ones sits on its first changed lines and names the others. A review that stopped early carries the same line in the posted review, and a review of a cut change lists the files it did not review in full. A review too long for GitHub lists what fits and says how many more are in the portal. A review of a newer commit is posted as a new review, and the earlier one stays. On Bitbucket Data Center each finding on a changed line is a comment on that line, then one comment holds the summary and the rest. +- **Sending it yourself.** A finished review that is not on the pull request yet offers **Send to GitHub** or **Send to Bitbucket**. It is sent once per commit: on GitHub the section then says **Sent to GitHub**, and sending it again only tells you it is already there; on Bitbucket sending it again posts only what is missing, then says **Sent to Bitbucket**. It needs the Comment permission for the repository. +- **What it does not do.** It only reads code; it never runs it. ## Getting started @@ -456,7 +456,7 @@ Clicking a pull request opens it on the right. The pane is built to be read top Under the line are the actions the policy held. One Triage may take on its own but holds while CI runs reads **Will run when CI finishes:** and what it does, such as **Will run when CI finishes: add label risk: medium**; Triage takes it then without you. Any other reads **Held:** and what it does, such as **Held: Merge**, with why it is held under it, and waits for you. The list names what each does: adding a label, a merge off a fail or unsure verdict, which says so, and a held comment or change request, which shows the start of what it posts, such as **Comment "Please split this."**. **Do it** beside each runs it now, **Always, in this repository** leaves it to Triage there from then on (see [Taking action](#taking-action)), and **Hide for me** under the list hides them in this browser only. 3. **The problems**, if any, failed rules first. Each one names the rule, then why, such as **Linked issue: Pull request does not reference an issue.**, with the evidence, such as the name of the failing check. A rule you need to decide is marked with a question mark and named once, such as **Focused: Triage could not tell whether the changed files serve this pull request.**, since the status line above already says **Decide**. A pull request with no problems skips straight to the description. -4. **Code review**: before the first review of a pull request whose policy file turns code review on, it says **Code review: awaited**, and a pull request the policy left out of code review says **Not reviewed:** and why. Once reviewed: what the change does, then each bug it found as a comment from **Code review**, with the commit it reviewed, how severe it is (P0 is the most severe), the file and line, and the code it points at. A bug in a file code review could not read says **Not checked against the file**, here and on GitHub. If the review fails or runs past its hour, the section shows **The last review did not finish** instead of **No findings**. **Review again** reviews the latest commit once more; it is hidden on a draft. A commit that left the change as it was keeps the last review and says **Carried over from** the commit it was made for. On a GitHub pull request, **Send to GitHub** posts the review to the pull request, inline where the lines are part of the change; it then says **Sent to GitHub**. On a Bitbucket Data Center pull request, **Send to Bitbucket** does the same with a comment per finding and one summary comment. Either needs the Comment permission for the repository, and is refused once the pull request has newer commits that did not keep the review: review again, then send. +4. **Code review**: before the first review of a pull request whose policy file turns code review on, it says **Code review: awaited**, and a pull request the policy left out of code review says **Not reviewed:** and why. Once reviewed: what the change does, then each bug it found as a comment from **Code review**, with the commit it reviewed, how severe it is (P0 is the most severe), the file and line, and the code it points at. A bug in a file code review could not read says **Not checked against the file**, here and on GitHub. If the review fails or runs past its hour, the section shows **The last review did not finish** instead of **No findings**. **Review again** reviews the latest commit once more; it is hidden on a draft. A commit that left the change as it was keeps the last review and says **Carried over from** the commit it was made for. Triage posts the review on the pull request with the verdict, inline where the lines are part of the change. A finished review that is not on the pull request yet offers **Send to GitHub**, which then says **Sent to GitHub**, or on Bitbucket Data Center **Send to Bitbucket**, which posts a comment per finding and one summary comment. Either needs the Comment permission for the repository, and is refused once the pull request has newer commits that did not keep the review: review again, then send. 5. **The description** the author wrote. 6. **Folded rows** you can expand: the linked issues, the changed files (with the diff), and the rules that passed. Evidence for a file cites its changed lines and links them at the exact commit Triage judged. Linked issues are the GitHub issues the title, description or commits mention, and the Linear issues linked in the description, the commits or a comment from one of Linear's bots. Links hidden in the description are ignored. 7. **Commits and conversation** opens the pull request's page. That page reads the verdict, the evidence, then **Commits** and **Conversation**. See [The pull request page](#the-pull-request-page). @@ -605,7 +605,7 @@ Every secret Triage holds can be replaced from Settings without disconnecting an ![The Code review section: Review code with Anthropic, No key, with the Anthropic key field and Save key, then Where it runs: Docker on this machine, In use; Docker on another machine (TLS), Not set up, with its .env lines, Copy and Setup guide](images/43.png) -[Code review](#code-review) runs with the provider whose key is stored here; today that is Anthropic. **Review code with Anthropic** says **Key stored** or **No key**. With no key, the Anthropic key field is open: paste the key and press **Save key**. When a key is stored, that field is hidden and the key itself is not shown. **Rotate** opens a blank field for a new key; press **Save key** to replace the stored one. **Remove** deletes it after you confirm. As for the decision model, when Triage cannot read the saved keys, the row says so and shows no field. +[Code review](#code-review) runs with the provider whose key is stored here, Anthropic. **Review code with Anthropic** says **Key stored** or **No key**. With no key, the Anthropic key field is open: paste the key and press **Save key**. When a key is stored, that field is hidden and the key itself is not shown. **Rotate** opens a blank field for a new key; press **Save key** to replace the stored one. **Remove** deletes it after you confirm. As for the decision model, when Triage cannot read the saved keys, the row says so and shows no field. When no place is in use, the top of this section says **Code review needs an isolated place to run** and names the place recommended, the first in **Where it runs**, with the lines to add to the Triage server's `.env`, **Copy**, and its **Setup guide**. Once a place says **In use**, that notice is gone. @@ -684,7 +684,7 @@ Type in the search box to narrow the list by actor, action or pull request. The | Role | A workspace role (Owner, Admin, Member) that controls what a person may do in Triage. | | CI | Continuous integration: the automated tests and checks a repository runs on every pull request. | | Draft | A pull request its author has marked as not ready. Triage waits until it is marked ready. | -| Code review | Triage's optional bug finder, turned on by a repository's policy file. It reads each new commit and lists the bugs it finds in Triage, and on GitHub sends them to the pull request when you ask. | +| Code review | Triage's optional bug finder, turned on by a repository's policy file. It reads each new commit, lists the bugs it finds in Triage and posts them on the pull request with the verdict. | | Finding | One bug code review reports, with its severity (P0 is the most severe), file and line. | | First-timer | Someone opening their first pull request in a repository. | | Webhook | The channel GitHub or Bitbucket uses to tell Triage about new events. | diff --git a/docs/DECISION_MODEL.md b/docs/DECISION_MODEL.md index 862b05617..e2d24a0a3 100644 --- a/docs/DECISION_MODEL.md +++ b/docs/DECISION_MODEL.md @@ -1,6 +1,6 @@ # The decision model -Triage answers its judged rules (focused, docs, duplicates-open-pull-request and custom model rules) and decides each pull request's verdict with a decision model: TypeSafe's Jev, a System One model. Every other rule is a hard rule over GitHub data. This page describes how Triage asks the model, how it reads the answers, the limits it works within, what it costs and how accurate it has measured. +Triage answers its judged rules (focused, docs, duplicates-open-pull-request and custom model rules) and decides each pull request's verdict with a decision model: TypeSafe's Jev, a System One model. Every other rule is a hard rule over host data. This page describes how Triage asks the model, how it reads the answers, the limits it works within, what it costs and how accurate it has measured. TypeSafe's docs: [index (llms.txt)](https://docs.typesafe.ai/llms.txt), [System One](https://docs.typesafe.ai/concepts/system-one.md), [models and limits](https://docs.typesafe.ai/models.md), [primitives](https://docs.typesafe.ai/primitives.md), [confidence](https://docs.typesafe.ai/confidence.md), [jev-1.13 jagged edges](https://docs.typesafe.ai/model-jaggedness/jev-1.13.md). diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index ede0bbd3c..8839d315c 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -91,14 +91,16 @@ A WXT browser extension that checks the open GitHub pull request locally; see | Package | Holds | | --- | --- | -| `triage-workflows` | The triage steps, `pr-triage`, the definition that runs them locally, `repo-scan`, the judge's director, the actions under `actions/`, the deterministic logic under `logic/`, and the decision-model eval. | +| `triage-workflows` | The shared triage steps, `pr-triage`, the definition that runs them locally, `repo-scan`, the judge's director, the actions under `actions/`, the deterministic logic under `logic/`, and the decision-model eval. | +| `triage-manager` | The listener and the manager: each host's listener deploys a manager per pull request, and the manager runs the shared triage steps, deploys the judge and code review, and makes every post; see [its README](../packages/triage-manager/README.md). | +| `triage-judge` | The judge a manager deploys when the rules need the decision model; it reports the verdict and posts nothing. | | `triage-contracts` | Policy schema and parsers, the policy file, resolving a file over the one it extends, repository settings, triage state, labels, trigger mail, the manager chain's workflows and the `release.json` each publishes (`workflow-release.ts`), and shared types; see [its README](../packages/triage-contracts/README.md). | | `github-reads`, `bitbucket-datacenter-reads` | Host read helpers, both returning the GitHub shapes. | | `github-tool`, `bitbucket-datacenter-tool` | The sidecar tool bundles: reads, the mirror, and actions. | | `repo-read-tool` | `read_file`, `grep` and `list`, read-only and confined to a checkout folder under the sidecar data dir's `checkouts` (no `..`, absolute path or symlink out); the host tool packages export `checkout` and their read bundles add `*_read_file`. | | `pr-code-review`, `pr-code-review-anthropic` | The provider-neutral code review workflow and the Anthropic provider's package and eval; see [its README](../packages/pr-code-review/README.md). | | `policy-check-action` | The GitHub Action that checks `.corbits-triage.yml` as the hub reads it, and runs the policy's rules that need no model on a pull request through pr-triage's `runLocal`. | -| `provisioner-docker`, `provisioner-template`, `provisioner-test` | The Docker provisioner, a template for a new backend, and the contract tests; see [provisioners/](provisioners/README.md). | +| `provisioner-docker`, `provisioner-e2b`, `provisioner-railway`, `provisioner-template`, `provisioner-test` | The Docker, E2B and Railway Sandboxes provisioners, a template for a new backend, and the contract tests; see [provisioners/](provisioners/README.md). | | `openai-null-delta` | Inference adapter fix for null stream deltas. | | `common` | Dependency-free helpers shared across apps and packages. | diff --git a/docs/PRODUCT.md b/docs/PRODUCT.md index 66e2ae4a4..d697f84d0 100644 --- a/docs/PRODUCT.md +++ b/docs/PRODUCT.md @@ -66,6 +66,12 @@ A rule answers one question about a pull request: pass, fail, or unconfirmed. | Rule | Host data only: files, size, linked issue, mergeability, reviews, CI, text. No inference. | Host API calls. | Anything the host already knows. | | Decision model | Fixed-shape questions to a System One model (Jev), citing the pull request as evidence. | One model request per batch of questions, billed by the model provider. | Judgment a rule cannot make: is the change focused, does it need docs. | +Rules over host data, decision-model rules and [code review](#code-review) are +three separate parts, each with its own limits. Rules over host data read +every changed file's patch up to a ceiling of their own, however much of the +diff the decision model's budget leaves out, and code review cuts its own +diff. A limit one part reaches never changes what another reads. + ### Rules over host data The same pull request and policy give the same result. A rule is unconfirmed @@ -231,6 +237,10 @@ request from the portal lists the actions that will wait. Turning triage off for a repository takes back everything Triage did there on its own. Close never runs on its own on Unsure, and merge never on anything but Pass. +That switch is a brake for the whole workspace. With it off, a repository +earns more one action at a time: **Always, in this repository** on a held +action lets Triage take that kind of action there on its own from then on. + Approve runs only on a Pass with CI green. A merge waits until CI passes on the head (or runs without CI only when the policy switches CI green off), and refuses while anyone has requested changes; Triage's own request for changes @@ -419,23 +429,27 @@ lines and breakage the change causes elsewhere in the repository. Each defect names the line, why it is wrong, what triggers it and a fix, most severe and most agreed-on first. It is separate from the verdict and optional. -A workspace turns it on by saving an Anthropic key under Settings → Code review and -switching it on per repository; each reviewed commit is billed to that -Anthropic account. Every new commit of a non-draft pull request is reviewed, -and **Review again** reviews the current one once more. A diff over 100,000 -characters is cut; the review says how much it read and names the files it left -out, and so does the review sent to GitHub. +A repository's policy file turns it on with its `code-review` block, which +also says whose pull requests (`for`) and which verdicts and paths (`when`) it +reviews. It needs an Anthropic key saved under Settings → Code review, and each +reviewed commit is billed to that Anthropic account. A commit is reviewed once +the rules that fail a pull request without the decision model have passed, or +whatever they say with `when: { rules: any }`; drafts are not reviewed. +**Review again** reviews the current commit once more, whatever the file says. +A diff over 100,000 characters is cut; the review says how much it read and +names the files it left out. Each review checks out the pull request's code, so it runs in an isolated -place of its own; until the server has one, the repository page links +place of its own; while the server has none, the repository page links Settings → Code review, which lists the places this server knows and how to -set each up ([provisioners](provisioners/README.md)), in place of the switch. -Findings show in the reading pane. On GitHub, -**Send to GitHub** posts the newest review as one review from the Triage App, -inline where the diff shows the lines, once per commit and never on its own. -On Bitbucket Data Center, **Send to Bitbucket** posts each finding the diff -shows as a comment on its first shown line and one summary comment with the -rest, once per commit. Sending needs only a finished review, so it works without an isolated place. +set each up ([provisioners](provisioners/README.md)). +Findings show in the reading pane, and Triage posts the review with its +verdict: on GitHub as one review from the Triage App, inline where the diff +shows the lines; on Bitbucket Data Center as a comment per finding the diff +shows, on its first shown line, and one summary comment with the rest. A +finished review that is not on the pull request yet is posted with **Send to +GitHub** or **Send to Bitbucket**, once per commit. Sending needs only a +finished review, so it works without an isolated place. ## Triage over time @@ -443,8 +457,9 @@ Turning triage on triages a GitHub repository's open pull requests; after that, events are triaged as they arrive, and the hub re-queues any open pull request whose latest commit has no verdict. A pull request merged into the default branch makes the repository's other open pull requests due again, each -at most once per 15 minutes. A re-check reuses the decision model's earlier -answers for anything unchanged. +at most once per 15 minutes. A re-check of a commit whose verdict nothing on +the host can still change posts that verdict again without asking the decision +model; a new commit is judged afresh. ## Limits diff --git a/docs/provisioners/docker.md b/docs/provisioners/docker.md index 483641ac0..cf35b0174 100644 --- a/docs/provisioners/docker.md +++ b/docs/provisioners/docker.md @@ -35,7 +35,7 @@ Measured on Docker Desktop 29.5.3 (2026-10-17): a deployed, idle code review con Each sidecar has its own container, processes, filesystem and data volume, with a read-only root, the image's unprivileged `bun` user (never root, and the data volume is created owned by it), no Linux capabilities, no new privileges and no Docker socket. It holds only its own hub token and a credential key of its own. It shares the host's kernel and has outbound network. -A sandbox for repository commands inside the container needs privileges the container does not have today; they come with the tool that runs those commands. +The container has no sandbox for a repository's own commands, and no workflow runs them. Containers are labelled with a hub id derived from the hub's Postgres cluster, database and schema, so hubs sharing a daemon never touch each other's containers. At boot the hub removes its containers that no allocation or probe still holds. Pointing the hub at another daemon or database, or a new sidecar image, changes the provisioner's binding, and sidecars placed through the old one are not cleaned up through the new one. diff --git a/packages/triage-contracts/README.md b/packages/triage-contracts/README.md index 9e8742bae..793ac4bcc 100644 --- a/packages/triage-contracts/README.md +++ b/packages/triage-contracts/README.md @@ -11,7 +11,7 @@ and trigger mail. Every app and most packages import it. A policy sets which rules run on a repository's pull requests and what happens after. Its rules are stored under `checks` (built-in) and `custom`. `GET /api/integrations/pack-schema` returns its JSON Schema as `checkPack`, the part of a policy file that extends another repository's file as `policyOverride`, the repository settings as `repoPolicy`, and `.corbits-triage.yml` as `policyFile`: schema 3, a list of `rules` (each `text`, `check`, `reason` and `evidence`), `actions` (`on` and steps per verdict) and `labels`, which `readPolicyFile` maps onto the stored shape; `checkPolicyFile` answers the same or each problem with its line, and the file's v4 keys as `workflow`. Those keys (`policy-workflow.ts`) are `groups` (each `association`, `teams` or `users`), one key per block (`code-review`, `security`, `deslop`; each `on`, and optionally `for`, `when: { triage, paths }`, `clean: { below }` and `provider`), `pace`, `pauseLabel` and `decide` (rules `{ if, do }`, `if` on `all`, `triage`, `event`, `author` and each block's outcome); they are validated and returned as written, and nothing acts on them yet except steps naming a group and, on GitHub, `pauseLabel`, whose label on a pull request stops the hub starting runs for it. A file that sets `extends` holds none of them. Groups are matched first match wins, in the file's order: an author is in at most one of the file's groups, then in each built-in group (`maintainers`, `external`, `first-timers`, `bots`) their author kind puts them in, which are facts matched independently, then in `everyone`, which always matches; a file group with a built-in group's name replaces it. `authorGroupsOf(groups, author)` answers that set, failing closed: it answers none, so no `{ group }` but `everyone` holds, when the author's kind is not known or teams that can't be read would decide their file group. A step's built-in group reads as author kinds, so it still holds when teams can't be read, and is not known only when the author's kind is. The teams read for an author are every team the policy names, in a step or a group (`policyTeams`). A step's `when.author` names groups as the policy holds them, `{ group }` for a file group or `everyone` and a built-in group's kinds otherwise, and holds when the author's set includes it; approving or merging must never rest on `everyone` alone. `{ all: clean }` holds when at least one block ran and every block that ran is clean; skipped blocks never count, so it fails when every block was skipped or none is on. A rule that approves or merges without `triage` matches only Pass (`ruleVerdicts`). Incomplete is never clean: a rule that approves or merges can't name `incomplete`. A file that sets `actions` and no `decide` gets its `decide` from `decideOf`: one rule `{ if: { triage: }, do: }` per verdict with steps, `do: nothing` for a Pass or Fail with none, and no rule for an Unsure with none, each limited to `event: ` when the actions wake on fewer than every event; with a block on, a verdict whose steps approve or merge gets two rules, all its steps under `all: clean` and then the same steps with approve and merge as `nothing`, each in its place; a file that extends another has no `decide`. A `decide` rule can't use `review` yet. A file setting any v4 key is read as schema 3 and needs `rules`. Problems name their line from the YAML parser's node positions (`yaml`, core schema, aliases and the keys `__proto__`, `constructor` and `prototype` refused); schema errors are said in the file's terms by `policy-file-problems.ts`. A file without `rules` or a v4 key is read as a policy or override without `kind`, `schemaVersion` and `repo`. Constraints the schema cannot state are checked when the policy is read: a custom rule's `options` and `failOn` must not repeat and `failOn` must be among `options`, a score's `failBelow` must be one of its levels after the first, and a custom id must not be a built-in rule's or read as a judge question's. A rule that fails any of these rejects the policy with its reason. Custom rules are decision-model questions (`kind: "model"`): `is-true`, `score` or `choose`, or text rules (`kind: "text"`) that fail when any of `fields` (`title`, `description`, `commits`) contains one of `phrases`, or with `failWhen: "not-contains"` when one contains none, ignoring case; a stored rule of the retired `kind: "rule"` rejects the policy. The reader ignores unknown built-in rows. Built-in rule ids renamed earlier (`size`, `issue`, `conflicts`, `ci`, `paths`, `draft`, `review`) are still read and mapped to their new ids by `RENAMED_RULE_IDS`; an old `size` row loads as both size limits. A new custom rule's id is its name as lowercase words joined by hyphens, numbered past any taken id; it never starts with `docs-` or `focused-` or ends in `-file`, `-hunk` or `-slice` and numbers, as judge question ids do. -`actions` holds one `when` (the events it wakes on) and an ordered list of steps per verdict. Each step is `{ do, target?, when? }`; its `when` holds conditions that must all hold for it to run (`whenHolds`): `author` (any of GitHub's `author_association` values `OWNER`, `MEMBER`, `COLLABORATOR`, `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, `FIRST_TIMER`, `NONE`, `MANNEQUIN`, or `BOT` for a bot whatever its association, `{ team }`, `{ users }`; a stored or file policy naming a group of `AUTHOR_GROUPS`, `maintainers`, `external`, `first-timers` or `bots`, reads it as exactly that group's kinds), `linkedIssue`, `ci` (any of `passing`, `failing`, `pending`, `none`), and `files` and `lines` as `{ over?, atMost? }`. A stored step's `for` reads as `when: { author: [] }`, and `for: anyone` as no condition. Every rule row may carry `text`, `reason` and `evidence` (`#44`, `owner/name#44`, https links or `maintainer-stated`). An action runs on its own only when the workspace's `pr-triage` system role holds `invoke` on `triage-do::/`; otherwise it waits for a person holding that grant, and `close` on `unsure` and `merge` on anything but `pass` always wait. `labels` names the label the mirror writes for each verdict; a verdict it leaves out keeps its default (`Triage: Ready`, `Triage: Not Ready`, `Triage: Needs Human Review`). The published schema validates `schemaVersion` 2. A version 1 policy still reads, as version 2: its actions become steps for anyone, and actions that no longer run, custom rules over GitHub data and text longer than version 2 allows are left out or cut and named in `dropped`; the next save writes version 2. Repository roles were removed, so a step whose target is a role is left out and named in the pack's `dropped`. +`actions` holds one `when` (the events it wakes on) and an ordered list of steps per verdict. Each step is `{ do, target?, when? }`; its `when` holds conditions that must all hold for it to run (`whenHolds`): `author` (any of GitHub's `author_association` values `OWNER`, `MEMBER`, `COLLABORATOR`, `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, `FIRST_TIMER`, `NONE`, `MANNEQUIN`, or `BOT` for a bot whatever its association, `{ team }`, `{ users }`; a policy naming a group of `AUTHOR_GROUPS`, `maintainers`, `external`, `first-timers` or `bots`, reads it as exactly that group's kinds), `linkedIssue`, `ci` (any of `passing`, `failing`, `pending`, `none`), and `files` and `lines` as `{ over?, atMost? }`. Every rule row may carry `text`, `reason` and `evidence` (`#44`, `owner/name#44`, https links or `maintainer-stated`). An action runs on its own only when the workspace's `pr-triage` system role holds `invoke` on `triage-do::/`; otherwise it waits for a person holding that grant, and `close` on `unsure` and `merge` on anything but `pass` always wait. `labels` names the label the mirror writes for each verdict; a verdict it leaves out keeps its default (`Triage: Ready`, `Triage: Not Ready`, `Triage: Needs Human Review`). Repository roles were removed, so a step whose target is a role is left out and named in the pack's `dropped`. ```json { diff --git a/packages/triage-judge/AGENTS.md b/packages/triage-judge/AGENTS.md new file mode 100644 index 000000000..7bbf2c3ee --- /dev/null +++ b/packages/triage-judge/AGENTS.md @@ -0,0 +1,5 @@ +Follow the [root AGENTS.md](../../AGENTS.md). + +- The judge only infers: it runs the shared judge, parts and evaluate steps from `@corbits/triage-workflows` and reports the verdict through the hub. It never writes to the pull request. +- Its budget is its own. A limit here never changes what the manager's rules read or what code review sees. +- Takes mail only from the manager that deployed it, and declares no grant beyond reporting a result and stopping itself. diff --git a/packages/triage-manager/AGENTS.md b/packages/triage-manager/AGENTS.md new file mode 100644 index 000000000..1e07cc867 --- /dev/null +++ b/packages/triage-manager/AGENTS.md @@ -0,0 +1,6 @@ +Follow the [root AGENTS.md](../../AGENTS.md). + +- The manager is the only workflow that writes to a pull request. It posts at once, waits for the judge and code review itself, and ends after its final post or while its head waits only on CI, which the reconciler wakes again once CI settles ([ARCHITECTURE.md](../../docs/ARCHITECTURE.md#lifecycle-of-a-pull-request)). +- The judge and code review are workflows the manager deploys and waits for, never steps inside it; they report through the hub and post nothing. +- Logic the judge or the local `pr-triage` definition also needs belongs in `@corbits/triage-workflows`; this package holds only what the listener and the manager alone do. +- The listener holds no state of its own: the hub's claim decides which manager a pull request has. diff --git a/packages/triage-manager/README.md b/packages/triage-manager/README.md index c19bb3244..a82629af9 100644 --- a/packages/triage-manager/README.md +++ b/packages/triage-manager/README.md @@ -8,9 +8,9 @@ A workspace's listener and a pull request's manager. mail fails too, it is cancelled, and the pull request's next event, or the reconciler re-queuing its head, deploys another. - **Manager** (`triageManager`, `triage-manager-`): one live run per pull request, which lives until its head has - its final verdict, and the only workflow that writes to the pull request. A mail starts a loop of passes: `take` claims the pull - request and its pending events; each held Do approved since runs as pr-triage runs an approval; then pr-triage's own - steps run on the newest mail the hub recorded for the head. A policy of only deterministic rules posts on that pass. + its final verdict or waits only on CI, and the only workflow that writes to the pull request. A mail starts a loop of passes: `take` claims the pull + request and its pending events; each held Do approved since runs through the shared approval step; then the shared + triage steps run on the newest mail the hub recorded for the head. A policy of only deterministic rules posts on that pass. One whose rules need the judge, or that asks for code review, posts once, after each of those has reported or given up. The next pass runs at once while an event is pending. While a judge or code review it mailed is out and within its deadline, the next runs 30 seconds later, taking what arrived meanwhile: a check finishing, a reviewer's result, or a diff --git a/packages/triage-workflows/AGENTS.md b/packages/triage-workflows/AGENTS.md index 3c509401e..8300b9ece 100644 --- a/packages/triage-workflows/AGENTS.md +++ b/packages/triage-workflows/AGENTS.md @@ -1,6 +1,7 @@ Follow the [root AGENTS.md](../../AGENTS.md). -- pr-triage is stock primitives: `onTrigger` for mail, `action` for every deterministic step (rules, evaluation, dispatch), `loop` or `childWorkflow` for repetition. -- An agent exists only where inference happens: the judge step. Never wrap a deterministic step in `defineAgent`. A step that reaches the pull request's host (facts, mirror, execute) is an `action` that declares the host's credential handle and an `effect.requires` capability, and makes each call inside `ctx.perform`'s `run` ([ARCHITECTURE.md](../../docs/ARCHITECTURE.md)). +- This package holds the triage steps the manager and the judge share (`triage-steps.ts`, `actions/`, `logic/`), `repo-scan`, and the `pr-triage` definition that runs the steps locally for the tests, the evals, the policy check and the extension. The hub deploys nothing defined here. +- Every step is a stock primitive: `action` for every deterministic step, an agent only for the judge step. Never wrap a deterministic step in `defineAgent`. A step that reaches the pull request's host (facts, mirror, execute) is an `action` that declares the host's credential handle and an `effect.requires` capability, and makes each call inside `ctx.perform`'s `run` ([ARCHITECTURE.md](../../docs/ARCHITECTURE.md#triage-pipeline)). +- The rules that run in code read every patch up to their own ceiling; the judge's budgets apply only to what the judge is sent. - Verdicts: a failed rule decides Fail without the verdict question, unless its `onFail` is `ask` (Unsure) or `label`; an unconfirmed judged rule makes a pass Unsure; a policy with no decision-model rule passes when every rule passes; otherwise one Choice question decides. - `bun run eval` calls a paid model ([DECISION_MODEL.md](../../docs/DECISION_MODEL.md)).