ci: wire the seidroid review workflow - #251
Conversation
A thin caller for sei-protocol/uci's seidroid-review.yml. Review logic and the reviewer binary live upstream; this file only wires triggers, scopes and secrets. Four deliberate divergences from this repository's other workflows, each commented in the file: the raw sha pin (the callee is on no uci tag and on no uci default branch), the absence of a `concurrency:` block (the callee serialises per mode, and a caller-level group would let a review cancel a sandbox reclaim), the empty top-level `permissions:` with per-job grants, and the lowercase `name:`. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The permissions comment said each job states "the four the callee's own jobs declare". The callee's guard declares pull-requests: read and issues: read; only its review job declares all four. Reword to the union, and give the actual reason the block is empty: a reusable workflow may only downgrade what its caller granted, and the default token grants contents, packages and metadata only. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment-only. The non-comment content of the file is byte-identical: the three `if:` expressions, `on:`, `permissions:`, `uses:`, `secrets:` and `with:` are unchanged. - The review job claimed a request "still reads mid-sentence". False: the callee requires a whole line reading `@seidroid review [close]`, so `please @seidroid review` is admitted here and rejected there. Replaced with the actual mismatch -- caller matches substrings, callee matches lines and never checks the parsed line against `mode` -- and a pointer to the PR description, which carries the case where that deletes a session. - The close job claimed anchoring stops a quoted teardown from destroying a session. It only stops the phrase occurring LATER in a body; a body that begins with it still routes there whatever follows. Says that now. - `approve-on-success` claimed a clean review "publishes a real approving review, not a comment". The callee attempts APPROVE and falls back to COMMENT when the API refuses the position, or records none at all. It also asserted `main`'s current policy, which dates; that evidence moved to the PR description. - The permissions comment asserted the default token grants contents, packages and metadata only. That is the restricted default; the effective one is an Actions setting we cannot read. Reworded to be independent of it. - The no-concurrency rationale said the callee's granularity is one "a caller cannot express". A caller can set job-level groups. The decision stands on judgement, not impossibility, and the argument now says so. - The trigger comment claimed a re-review otherwise needs a manufactured empty commit. A top-level issue_comment already works; the extra events add places to type the command, not capability. - The fork remedy told the fork author to post `@seidroid review close`. The guard admits a teardown only from an OWNER, MEMBER or COLLABORATOR, so an external contributor cannot. Now addressed to a maintainer, and the sandbox consequence is conditional on a session existing. Also records what the callee's concurrency is not: no exactly-once, no session lifecycle, no closed-PR check on the comment path. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment-only again; non-comment content byte-identical (same md5 as the two preceding commits). The concurrency note said cancellation stops a session and "only a close deletes it". Driver v0.17.0 createOrAdopt (internal/omni/host.go) deletes and replaces an existing session it finds that can no longer run a turn, so opening a review can delete one. Close is now described as the explicit end-of-work path that reclaims a sandbox, with the repair path named and marked as repair rather than teardown. Five residual inaccuracies from the previous pass, all comment prose: - The permissions note still said a caller "has to hand the callee its scopes explicitly". Sufficient inherited permissions serve too; the explicit grant is a choice not to depend on a configured default. - The no-concurrency argument justified itself against "workflow + ref, as this repository's other callers key". Those four callers use three different shapes, none uniformly that, and workflow+ref is not PR identity anyway (comments carry the default-branch ref, PR events a merge ref, closed events the target branch). Argues from a workflow-and-PR group without mode instead, which is the shape that would actually couple review to reclaim. - approve-on-success said the callee falls back to a comment "where the API refuses that position". The rung fires only on a reported 422; a 403 approval-policy refusal gets no fallback, and the fallback attempt can itself fail. - The close job claimed anchoring the review job too would trade a misroute for a silent no-op. In the documented case the review job already excludes itself, so re-anchoring would have prevented nothing. Now states fleet parity as the reason. - The fork caveat implied a routine leak. No routine path creates a session for a fork-originated PR -- the callee refuses a review on one from every trigger -- so the caveat covers a pre-existing or out-of-band session. Also corrected the authorization: the gate is on admitted association, and an outside COLLABORATOR qualifies, so it is not "every external contributor" who is excluded. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment-only; non-comment content still md5 c64a681798c7, unchanged across all five commits. The concurrency note said the driver "traps cancellation and stops its session, keeping the conversation". That came from the callee's own comment, and the callee is wrong at this pin. Against driver v0.17.0: the signal handler cancels only the local context (cmd/sei-agent-driver/main.go), a turn is explicitly described as still running after a cancelled context or expired deadline with the next prompt queueing behind it (internal/driver/driver.go), and the package doc says a run tears nothing down because stopping a session ends the agent process and runner but never the sandbox, the runner's idle timeout ending that process unasked (internal/driver/doc.go). So cancellation unwinds the driver without deleting the session: the conversation stays, the sandbox stays allocated until something reclaims it, and the remote turn may keep running. This strengthens the reason for omitting a caller concurrency block rather than weakening it -- a caller-level cancel-in-progress would buy latency, not reclamation -- and the bullet now says so. Also drops "predates the head becoming a fork" from the fork caveat. It invented a transition nobody has demonstrated, and contradicted the same paragraph's correct statement that retargeting, draft changes and synchronization leave the head repository unchanged. Now reads as a pre-existing session created outside these guarded review paths. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR SummaryMedium Risk Overview Review runs on PR open/sync/reopen and when The file intentionally differs from other uci callers here: no top-level Reviewed by Cursor Bugbot for commit ed28fd4. Bugbot is set up for automated code reviews on this repo. Configure here. |
| if: >- | ||
| github.event_name == 'issue_comment' && | ||
| github.event.issue.pull_request != null && | ||
| startsWith(github.event.comment.body, '@seidroid review close') |
There was a problem hiding this comment.
suggestion — Unchanged since the last review, and still worth the comment fix. Verified again against the callee at the pinned sha: the guard's parse step greps for a whole line matching ^\s*@?seidroid\s+review(\s+close)?\s*$ and writes should_run=true without ever comparing the line it matched to inputs.mode, so this expression alone decides review-vs-close. The destructive case the description records reproduces exactly as written, and the review job's !contains on line 99 is what makes the misroute exclusive rather than additive. That much the comment above already says.
It is short on two further axes, and both widen the trigger surface beyond what a reader of the comment or the description would expect.
CASE. GitHub's startsWith and contains are documented as case-insensitive; the callee's grep -E is case-sensitive, and the trigger-phrase input says so explicitly (@Seidroid review starts no review). A body opening @Seidroid review close ... therefore routes to this job, and if it also carries a properly-cased @seidroid review line the callee proceeds under mode: close. The mirror case is benign: a lone @SEIDROID REVIEW selects the review job and parses to nothing.
WORD BOUNDARY. startsWith tests a string prefix, not a command token, so @seidroid review closely at the retry logic and @seidroid review close-not-now both select this job. Alone each is a silent no-op — the whole-line grep rejects the line — but with a later bare @seidroid review line each is the destructive path, and the author has typed nothing resembling the teardown command anywhere in the body. The description's guidance ("don't begin a comment with the teardown phrase unless you mean it") does not prepare a reader for that: the phrase was never typed.
Agreed the fix belongs in the callee and that inventing caller-side grammar here would fight the next fleet-wide pin bump; no change to the expression is being asked for. What is worth changing is the comment at line 82, which presents the divergence as substring-vs-whole-line only. Naming all three axes would let the next person weighing a caller-side fix see the full shape of what they would be fixing.
1. BlockingNone. 2. Non-blockingNothing material has changed since my last review. The head is still Every note from my first review stands unchanged, so I will not restate them at length — they are carried in full in the JSON block below:
One thing is worth adding to the routing finding rather than opening beside it. 3. SummaryThe pull request has not moved: same head, same merge commit, same 181-line diff, same blob, and the base standards are unchanged, so this is a re-review of identical material and every earlier finding carries forward untouched. One scout ran: codex raised the close-job routing hazard again, this time with a seidroid review · decision Findings: 0 blocking | 7 non-blocking | 1 posted inline |
Comment-only; non-comment content still md5 c64a681798c7, unchanged across all six commits. The review job's comment described the divergence as substring-vs-whole-line. It has three axes, and the two that were missing both widen the destructive surface: - CASE. GitHub documents contains() and startsWith() as "not case sensitive". The callee's parse is `grep -m1 -E` with no -i, and its trigger-phrase input says so outright: "Matched case-sensitively. `@Seidroid review` starts no review." So a body opening `@Seidroid review close ...` selects the close job here while the callee skips that line, and a properly-cased `@seidroid review` line later in the body then runs under mode: close. - WORD BOUNDARY. startsWith tests a string prefix, not a command token, so `@seidroid review closely at the retry logic` selects the close job. The author has typed nothing resembling the teardown command. No expression changed: a caller cannot grade this reliably, and the callee is the only reader that can compare a parsed line to mode. The comment now names all three so the next person weighing a caller-side fix sees the whole shape of it. Found by seidroid[bot] reviewing the pull request that adds this file -- an inline suggestion on this file, which also approved the change. The PR description carries the reproducers and the revised guidance; the previous guidance was insufficient, since it told readers not to open a comment with the teardown phrase and the word-boundary case never types that phrase at all. Co-authored-by: omnigent <noreply@omnigent.ai> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@seidroid review close Reclaiming the sandbox left behind by the merge. The Same transport unreachability that made the first review attempt on this PR expire its |
What this is
One new file,
.github/workflows/seidroid.yml— a thin caller forsei-protocol/uci/.github/workflows/seidroid-review.yml. Pure wiring: triggers, tokenscopes, secrets, and four
with:inputs. The review logic lives in uci and the reviewerbinary in
sei-protocol/sei-internal-skills; nothing in this repository implements anyof it. No Go file changes.
Three jobs, mirroring the four repositories already wiring this reviewer
(
platform,sei-k8s-controller,sei-internal-skills,sei-load):seidroid-reviewclosedpull_request, or@seidroid reviewin a top-level comment / inline review comment / review bodyreviewseidroid-review-close@seidroid review closeat the start of a top-level commentcloseseidroid-review-reclaimpull_request: closedclosemode: closedeletes the managed review session and reclaims its sandbox. It does notclose the pull request.
The inline-review-comment and review-body triggers follow
platformspecifically,which added them first;
sei-k8s-controller,sei-internal-skillsandsei-loadwireissue_commentandpull_requestonly. They add places a person can type the command,not a capability — a top-level comment already asks for a re-review of the current
tree.
Four deliberate divergences from this repository's conventions
Each is commented in the file so it does not read as an oversight.
Raw sha pin, where
uci-lint.yml,uci-release-check.yml,uci-release-publish.ymlanduci-stale-check.ymlall pin a uci tag(
@v0.0.3,@v0.0.11). This is availability, not preference: the callee is on nouci tag and on no uci default branch — the workflow file has no history on uci's
main, and uci's latest release does not contain it. There is no tag to name. Allfour existing callers pin this same sha, and the file is bumped across repositories
by path, so the three occurrences here are kept identical.
No
concurrency:block, where all four uci callers in this repository set onewith
cancel-in-progress: true. A caller could express per-job groups here — thisis a judgement that it should not, not a claim that it cannot. The callee already
carries its own job-level
concurrency, keyed on mode + repository + PR number withcancel-in-progress: true, so a newer review cancels an in-flight review while ateardown for the same PR sits in a separate group and survives; a caller group on top
could only narrow that. And the group a caller would reach for first is the wrong
one: a workflow-and-PR-number group with no mode in the key would couple the review
job to the reclaim job, so a review arriving on a just-closed PR could cancel an
in-flight reclaim, and
mode: closeis the path that reclaims a sandbox.Cancelling a review also frees nothing, and stops less than the word suggests.
Cancellation unwinds the driver without deleting the session: the conversation stays,
the sandbox stays allocated until something reclaims it, and the remote turn may
keep running — the next request queues behind that turn rather than racing it. A
caller-level
cancel-in-progresswould therefore buy latency, not reclamation, whichmakes the case for omitting it stronger rather than weaker.
To be precise about deletion, since an earlier draft of this PR overstated it: close
is the explicit end-of-work path, not the only code path that can delete a session.
Driver v0.17.0
createOrAdopt(internal/omni/host.go) adopts a live or revivablesession, but deletes and replaces one it finds that can no longer run a turn — repair
of an already-dead session rather than a teardown a caller can schedule. Reviews do
not routinely destroy sessions.
Worth stating plainly, since it is easy to read more into it: the callee's grouping is
not an exactly-once or session-lifecycle guarantee. A push and a comment can each
admit a review of the same tree; overlapping runs cancel, sequential requests are not
deduplicated; review and close sit in separate groups by design; and the comment path
does not test whether the PR is already closed.
permissions: {}at the top level with per-job grants, where this repository'sother workflows grant concrete scopes at the top level. A reusable workflow may only
downgrade what its caller granted, so the caller sets the ceiling. Sufficient
inherited permissions would serve as well — the choice here is to grant the required
scopes explicitly rather than depend on a configured default. Empty at the top means
no job inherits one, and each job then states exactly the scopes the callee's two jobs
declare between them (
contents: read,pull-requests: write,checks: write,issues: write), so what this caller grants does not move with whicheverGITHUB_TOKENdefault the org or the repo has set — a setting we cannot read fromhere (403).
Lowercase
name: seidroid review, where this repository Title-Cases (Go Linter,Releaser,Stale Checker).seidroid reviewis the literal command aperson types, and the callee sets
run-name: UCI / seidroid review / #<n>;Title-Casing it would put this workflow's name at odds with its own trigger phrase
and with every other caller.
Filename, relatedly:
seidroid.yml, notuci-seidroid-review.yml. Thisrepository's
uci-prefix denotes the uci boilerplate-CI family, where the callerbasename mirrors the callee basename (
uci-lint→go-lint.yml,uci-stale-check→stale-check.yml) — it does not mean "calls sei-protocol/uci" generally(
sei-internal-skillsholds bothuci-release-publish.ymlandseidroid.yml, both ucicallers).
seidroid.ymlis what all four existing callers use, and since the pin isbumped fleet-wide by path, a repository-local spelling would be missed by the next
bump. The header comment's first line carries the provenance the prefix would have.
Inputs, and what is left unset
mode,approve-on-success: true,allowed-team: 'sei-protocol/sei-core', andguidelines-file: CLAUDE.md. Every other callee input is left at its default for fleetparity — deliberately, not by omission.
approve-on-success: true— on a clean conclusion the callee attempts anapproving review. It is an attempt, not a guarantee, and the fallback is narrower than
"falls back to a comment": the rung fires only where the API reports HTTP 422, in
which case it attempts a COMMENT review. A refusal arriving any other way — a 403
from an Actions approval policy, or a response carrying no
status— gets no secondattempt, and a post that does not succeed leaves a warning. Whether an approval that
does land counts toward a required approval depends on branch policy and reviewer
eligibility, neither of which this file decides. Revisit when that policy changes.
Dated observation, as of this PR and deliberately kept out of the file's comments
because it will age:
rules/branches/mainreturns[], and PR feat: select storage performance by its (IOPS, throughput) pair #249 merged with zeroreviews (
pulls/249/reviews→[]), so no approving review is required onmaintoday. The classic branch-protection API is 403 to the token used here, so this is
strong indirect evidence rather than a direct read.
guidelines-file: CLAUDE.md— unset readsREVIEW.md, which this repository doesnot keep (there is no file with "review" in its name anywhere in the tree), so the
callee would fetch a 404 and fall back to a generic Go checklist.
CLAUDE.mdlines19–44 are an enforceable
## Code Standardssection, and lines 42–44 carry across-repo invariant a generic checklist cannot know: the sidecar HTTP contract lives
in
sei-k8s-controller/sidecarapi, and a change there means bumping thego.moddependency after it lands. This follows the considered precedent rather than the
majority one: of the four existing callers only
sei-k8s-controllerwiresguidelines-file.platformandsei-loadkeep no CLAUDE.md at all, so they havenothing to point at;
sei-internal-skillshas one and still passes nothing.Restricting to callers that could wire it, it is 1 of 2, and this PR makes it 2 of 3.
Fork safety, and the one fork caveat
An automatic
pull_requestrun from a fork is denied by the callee's guard(
origin=fork→deny "…fork-originated; not reviewing it"), so no review session andno model turn start for fork code. A non-member
issue_commentis filtered by authorassociation. This repository being public is therefore not a new exposure.
The caveat, which is commented on the reclaim job: GitHub withholds secrets from a
pull_requestrun whose head is a fork, so the automatic reclaim cannot authenticate.It is narrower than it first reads, and that is worth stating. At this pin there is
no supported path that creates a session for a fork-originated PR at all: both review
paths require equal head/base repo ids and deny an unreadable origin; an outside
collaborator pushing to a branch inside this repository yields a same-repo PR, since
their identity does not make the head a fork; retargeting the base, toggling draft and
synchronizing the head all leave the head repository unchanged; and
mode: closebypasses the origin checks but only tears down — driver v0.17.0's
Close(
internal/omni/host.go) searches for sessions carrying the run key and deletes what itfinds, creating nothing, and its close path returns before review execution
(
cmd/sei-agent-driver/main.go). So the caveat covers a pre-existing session createdoutside these guarded review paths — not a routine leak this wiring causes.
Where such a session does exist, the teardown has to come from an author the guard
admits. The guard job's
if:requiresauthor_associationinOWNER,MEMBERorCOLLABORATOR; the team-membership check exempts a teardown, but this association checkis in the guard's own
if:and is not mode-aware. Note that an outside collaboratorholds
COLLABORATORand so qualifies — the excluded case is an author with no admittedassociation, not "every external contributor". If your association is not one of those,
ask an admitted collaborator or a maintainer to post it before merging.
The four existing callers all carry
"On a fork pull request, comment@seidroid reviewclose
before merging"verbatim (platform:90,sei-k8s-controller:63,sei-internal-skills:62,sei-load:61). That line does not name an audience, so amaintainer reading it can follow it correctly; what it omits is the authorization
requirement, which is what makes it misleading to a contributor who cannot act on it.
Worth a fleet-wide comment fix, as an omission rather than a wrong instruction.
Known limitation — inherited, not introduced here
The caller and the callee grade the same comment body by different rules, and the callee
never checks the line it parsed against the
modethe caller routed. When they disagree,the caller picks the job while the callee picks whether to run at all. They diverge on
three axes, not one:
if:expressions)grep -E)@seidroid review [close], nothing else on itcontainsandstartsWithare documented "not case sensitive"-i; thetrigger-phraseinput states "@Seidroid reviewstarts no review"startsWithhas no notion of where a token endsclosehas to be a whole wordOnly the first axis was described here until seidroid's review of this pull request
flagged the other two; both widen the destructive surface, and one of them defeated the
guidance this section used to give. Credit at the end of the section.
The case that matters, because it is destructive:
The body begins with the teardown phrase, so
startsWithselects the close job (andthe review job's
!containsexcludes itself). The callee then scans for a wholematching line: line 1 has trailing words and does not match, but line 3 does, so it
proceeds — with
mode: close. The session is deleted. The author asked for a reviewand lost their session.
Lesser variants:
please @seidroid reviewis admitted by the caller and rejected by thecallee (no whole line), so it silently does nothing;
@seidroid reviewfollowed by ablockquoted
> @seidroid review closeskips both jobs; a standalone@seidroid reviewinside a fenced code block still starts a review.
Two further routes to that same destructive path, neither of which requires typing
the teardown command as written.
Case. The caller's
startsWithignores case; the callee's grep does not:startsWithmatches the capitalised first line, so the close job is selected and thereview job excludes itself. The callee then skips line 1 (wrong case for its grep) and
matches line 3, and proceeds under
mode: close. Session deleted. The mirror direction isharmless: a lone
@SEIDROID REVIEWselects the review job and then parses to nothing, soit silently does nothing.
Word boundary.
startsWithtests a prefix, not a token, socloseneed not be a wholeword:
@seidroid review closeis a prefix of@seidroid review closely, so the close job isselected. The callee rejects line 1 (trailing words) and matches line 3. The session is
deleted, and the author never typed the teardown command anywhere in the body.
@seidroid review close-not-nowgoes the same way. This is the least intuitive of thethree axes and the reason the guidance below needed rewriting.
This is a property of the shared caller/callee pair and this PR neither introduces nor
worsens it. The parser/
modemismatch and the destructive reproducer are shared by everycaller, but close-job routing is not identical across them: only
platformanchorswith
startsWith(:73);sei-k8s-controller(:46),sei-internal-skills(:45) andsei-load(:44) all usecontains. That changes the lesser variants there — theblockquoted
> @seidroid review closedoes not skip both jobs in those three, becausetheir close job is selected by the substring and can then act on a separate valid review
line. This file follows
platform. It is not fixable in a caller:GitHub expressions have no regex and no newline literal, so any caller-side grammar we
invented would produce a different set of wrong answers rather than fewer, and divergent
if:logic here would fight the next fleet-wide pin bump. The fix belongs in the callee,which is the only reader that can compare the line it parsed to
mode—platform'scopy carries a
PLT-1182reference saying exactly that, so upstream already knows.Practical guidance until then. This replaces an earlier version of this paragraph
that said only "don't begin a comment with the teardown phrase unless you mean it" —
which is insufficient, because the author of
@seidroid review closely at the retry logichas typed no such phrase and can still lose their session:the callee actually matches, and it is the only form that reliably means what it says.
@seidroid reviewfollowed by anything startingclos,in any casing, unless you intend a teardown.
close,Close,closelyandclose-not-nowall select the teardown job.mention later in the body cannot select the close job in this caller.
@seidroidat all unless the command is theentire line.
Provenance: the case and word-boundary axes were found by
seidroid[bot]reviewing thispull request — an inline suggestion on this very file (comment
3983292798, on.github/workflows/seidroid.yml). The tool being installed found a real gap in thedocumentation of its own wiring, on its own installation PR, and asked for no change to
the expressions. Its verdict on the change was
review found nothing blocking(
APPROVED, 2026-09-10T20:39:40Z).PREREQUISITE — now satisfied, observed on this pull request's own run
The org secrets
OMNIGENT_MACHINE_CLIENT_SECRET,PLATFORM_CODE_AGENT_APP_IDandPLATFORM_CODE_AGENT_APP_PKmust includesei-protocol/seictlin their repositoryaccess. This repository referenced no secrets in any workflow before this change,
so it was not safe to assume it appeared in a "selected repositories" list, and every
secrets API returns 403 to the token used to prepare this change. It was written up here
as unverified.
It is now verified empirically, because this workflow ran on the pull request that
adds it. The
pull_requestrun exercised the added workflow through the PR mergeref, so opening this PR tested the real wiring rather than a fake. Run
34523934052, jobGuard, reports by step name:Step 6 is the callee's explicit presence check, so its success is direct evidence that
OMNIGENT_MACHINE_CLIENT_SECRETreaches this repository. Step 4 running rather thanbeing skipped, plus the
Reviewjob'sMint the reviewing identitystep succeeding,indicates the App credentials are present too. The
closeandreclaimjobs skippedcorrectly on a
pull_requestevent, which is the three-job routing behaving as intendedagainst a real payload.
One limit on that evidence, stated rather than glossed: it demonstrates the secrets are
reachable from this repository now. It does not show which mechanism grants them
(org-wide, selected-repositories, or repository-level), so a reviewer who wants that
recorded should still check the settings. Nothing about the merge is blocked on it.
For reference, had it NOT been satisfied, missing credentials would cause the following
failures or degraded behaviour. The two credentials fail in different places and to different degrees, so both
are worth stating separately:
Missing
OMNIGENT_MACHINE_CLIENT_SECRET— the callee's guard has an explicitpresence check that exits 1 with
OMNIGENT_MACHINE_CLIENT_SECRET is not set on the calling repository, before the driver attempts to mint the machine-client bearer. (Notbefore any token: where the App credentials are configured the guard mints an App token
earlier, to read team membership and labels.) That check is gated on the request
already being admitted, so it fires on reviews that would otherwise have run and not on
requests already denied (fork-originated, draft, skip-labelled, unauthorised commenter)
— those refuse for their own reasons and stay green. The
pull_request: closedreclaimskips the guard entirely, so it has no presence check to fail and would instead fail
later, in the driver, at minting. So "fails on every pull request" is too broad: it fails
every pull request that reaches a review, which on this repository is expected to be most
of them.
Missing
PLATFORM_CODE_AGENT_APP_ID/_APP_PK— not fatal, and a differentfailure. The review runs but posts as
github-actions[bot]rather than the bot, andevery
@seidroid reviewcomment is refused, because team membership cannot be readwithout the App identity. Automatic
pull_requestreviews and authorised@seidroid review closestill work. If only one of the two halves is set, the calleewarns and carries on under the workflow's own identity.
No action is required before merge. The paragraphs above are kept because they document
what each credential does and how each one fails, which is worth having written down the
first time this repository depends on any secret at all.
How this was verified
actionlintv1.7.7 (release binary, downloaded — this host has no Go toolchain),run over the whole workflows directory. Clean, exit 0, all six files:
a bogus activity type, which it caught:
github.event.no_such_field, and actionlint did not flag it —github.eventis aloosely typed payload, so the
github.event.*paths in theif:expressions are notmachine-validated. The two review events and the
github.event.review.bodyfallbackmatch
platform's live production usage specifically — it is the only existingcaller that wires them.
python3 -c 'import yaml; yaml.safe_load(...)', thenchecked the parsed keys rather than trusting the parse. To be exact about what came
back: the parsed top-level keys are
'name', booleanTrue,'permissions'and'jobs'— PyYAML (YAML 1.1) coerces the bareonkey to a boolean, so there is no'on'string key at all. That is a parser artifact, not a defect: every existingworkflow in this repository and all four sibling callers parse identically, and
GitHub's own parser reads it as a string, so
on:was deliberately left unquotedrather than turned into a divergence. Under that boolean key the four events and their
typesare as intended;permissionsis an empty dict;jobsholdsseidroid-review,seidroid-review-close,seidroid-review-reclaim; there is noconcurrencykey.at the pinned sha, or the driver at its pinned version — the job-level
concurrencygroup, the fork denial, the whole-line command grammar, the 422 fallback rung, the
session adopt/replace logic, and the effective
REVIEW.mddefault.an earlier draft described cancellation as stopping the session. That came from the
callee's own comment, not from code, and the driver contradicts it — see item 4
below. Reading a callee's prose is not the same as reading its behaviour, and the
cancellation wording now follows the driver.
No Go file changed, so the Go gates do not apply and were not run — this host has
neither
gonormake. This repository's ownGo Linterworkflow will run on this PRregardless.
No pre-existing gate in this repository validates workflow YAML.
uci-lint.ymlcalls uci's
go-lint.yml, which lints Go, not Actions. Theactionlintrun above wasdone by hand and is not enforced by CI, so please do not read a green CI here as
validation of this file.
Six things worth fixing upstream, none carried across
platform'sapprove-on-successcomment says itsmain"carries no branchprotection today, so the approval satisfies no required-review rule". False now —
platform ruleset
11598654setsrequired_approving_review_count: 1. The commenthere is written for this repository, and phrased so it stays true when policy moves.
platform's close-job comment refers to the quoted teardown command as something"which the header above tells a fork author to do". The header says nothing about
forks, and the instruction is on the reclaim job below. Corrected here.
contributor without an admitted association can follow it and have nothing happen —
see the fork section above.
these in the callee rather than in a caller, so every repository wiring it inherits
the error. It says the driver "traps cancellation and STOPS its session, keeping the
conversation". At driver v0.17.0: the signal handler cancels only the local context
(
cmd/sei-agent-driver/main.go); a turn is explicitly described as still runningafter a cancelled context or an expired deadline, with the next invocation's prompt
queueing behind it (
internal/driver/driver.go); and the package doc says a runtears nothing down because stopping a session ends the agent process and the runner
but never the sandbox, the runner's idle timeout ending that process unasked
(
internal/driver/doc.go). This file's earlier wording came straight from thatcomment, which is the useful half of the finding: a callee's prose is load-bearing
for its callers, and this line of it is stale.
leaking sandboxes in the ordinary course. At this pin no supported path creates a
session for a fork-originated PR, so the caveat only ever covers a pre-existing or
out-of-band session. Worth rewording upstream so the caveat is not read as a routine
leak — and worth checking against a newer driver, since this rests on v0.17.0.
cmd/sei-agent-driver/main.go:206says "review deletes no session by design, andonly
--closedoes", whileinternal/driver/driver.go:80-81saysCloseends asession "-- though opening will delete a session it finds unable to run a turn at
all", repeated at
driver.go:150-151. The second is the one that matches the code(
internal/omni/host.goadopts when live or revivable and deletes only after thattest fails). Listed with narrower scope than the others: it is in the driver rather
than the callee, so a caller does not inherit it directly. No claim is made here
about how the same error reached this file's earlier draft — matching prose does not
establish provenance.
All six are comment-only and out of scope for this PR: four are in other repositories,
one is in the callee, and one is in the driver.
The caller/callee grammar mismatch is the one upstream problem that is not
comment-only. It is documented separately, under Known limitation above, rather
than as a seventh entry in this list.
platformtracks it asPLT-1182. What this PRadds to it: the mismatch has three axes, not the one the fleet's comments describe
— match unit, case, and token boundary — and the two beyond match unit are the ones that
let a body reach the destructive path without the teardown command ever being typed.
Whoever picks up
PLT-1182should address all three in the callee, since a callercannot. Note that comparing the parsed command line to
modeis necessary but notsufficient: it prevents executing the wrong operation, which is the destructive half,
but it does not align the two grammars. A review request followed by a blockquoted
teardown is never dispatched, while a lone uppercase request is dispatched and then
rejected by the callee — two different failure points. Both remain silent no-ops, and
fixing them needs the accepted grammar aligned as well. Both additional
axes came from
seidroid[bot]'s own review of this pull request.