chore: update tend workflows (0.1.24 → 0.2.0) - #588
Conversation
Deploying mouseterm with
|
| Latest commit: |
596b188
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://eaf25151.mouseterm.pages.dev |
| Branch Preview URL: | https://tend-update-workflows.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Reviewing as a draft — flagging anything that looks worth a quick fix. Mark ready for a full review.
Two findings, both in docs/specs/, not in the generated files themselves.
1. 0.2.0 introduces a new third-party publisher into two secret-bearing jobs, and security-ci.md still describes the upstream residual as tend alone. tend-mention (verify) and tend-notifications (check) now run astral-sh/setup-uv@v10.0.1 — a mutable tag — immediately before a plain run: step whose env carries GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }}. That is the bot's PAT (repo, workflow, …) in the environment of a step whose interpreter comes from a third-party action at a ref its owner can move. The credential-isolation control in docs/specs/security-ci.md -> "Credential isolation bounds an injection" covers the agent step behind the proxy, not these run: steps.
docs/specs/security-ci.md -> "Upstream compromise" currently reads: "Every generated workflow references tend's action as max-sixty/tend/claude@<version> — a tag, not a commit SHA, and mutable by whoever owns that repository." After this PR that names one of two non-first-party publishers able to change what these workflows execute. AGENTS.md: "When code covered by a spec changes, change the spec." The sharp edge is that this repo already resolved this exact action at this exact version — workflow-audit.yaml pins astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 under the "pin every action by commit hash" rule — so the SHA is sitting in the tree while the generated copy takes the tag. The FAIL IF at "Tag pins are accepted inside tend-*.yaml alone" still passes; it's the prose residual that's now understated.
2. docs/specs/security-ci.rationale.md still says the checked-in workflows are at 0.1.24. Under "The 0.1.18 gap, reported from this audit and now fixed": "The checked-in workflows use 0.1.24 as inspected in September 2026; 0.1.19 remains the minimum security floor." This PR falsifies the first clause. The 0.1.19 floor claim is unaffected — 0.2.0 clears it.
Both are outside the diff. Happy to push a commit updating the two spec files once this is out of draft — say the word and I'll do it, or take them yourself if you'd rather word the security prose.
One non-blocking note on the upgrade itself: the new Check whether tend is enabled step makes one unretried gh api .../contents/.config/tend.yaml call per job. A transient failure there now fails the whole job — for tend-review that is a red check on the PR — where previously the job would have run. Nothing to fix here (generated file); worth knowing before merging, and a candidate for an upstream issue if it ever bites.
What I verified
- Regeneration is faithful.
uvx --no-config --no-env-file tend@0.2.0 initin a detached worktree atd2eda84, against that commit's own.config/tend.yaml, leavesgit status --porcelain -- .github/workflows/empty..github/actionlint.yamlalso regenerates byte-identical, so nothing is missing from the PR.workflow-audit.yaml'sis_tend_regenclassifier should skip this commit: single commit, onlytend-[a-z-]+\.yamlpaths, config untouched. - The enable gate works against this repo's config. Extracted the Ruby heredoc and ran it against
.config/tend.yaml:enabled=true, exit 0. The config has noenabled:key, so the default-true path is what runs. Ruby 3.2 is in theubuntu-24.04image.abortandwarnboth go to stderr, so onlyenabled=…reaches$GITHUB_OUTPUT. - No behavior regression in the Bash→Python port. Traced
tend-mention'sverifybranch by branch against the deleted shell: dispatch id validation, theissuesfast path, the self-comment skip,@-mention on body and on inline comments, theBot-author skip, the contentless-approval skip,fresh_inline, the issue-vs-PR split, and all three participation heuristics land on the same verdicts.gh_paginated'sraw_decodeloop is the right shape forgh api --paginate's concatenated arrays, and a failingghstays fatal (CalledProcessError→SystemExit) the way the shell'sset -ecommand substitution did. Same fortend-notifications: the subscription PUT, the 10-minute cutoff, the HTML-error-page guard (nowJSONDecodeError), and thetend-conflict-deferredmarker comparison are equivalent. Both scripts compile under 3.12. - Permissions. Every job stays inside
docs/specs/security-ci.md->FAIL IF … beyond contents: write, pull-requests: write, issues: write, id-token: write, actions: read, or any read permission. The newpermissions: contents: readonverifyis a narrowing. All eight files parse as YAML. - Gating on the disabled path.
tend-notificationsguards bothcheckoutand the action with(steps.tend_enabled.outputs.enabled == 'true') && (…), so the empty-steps.check.outputs.countcase can't fall through to a boot.tend-mention'shandleis gated onneeds.verify.outputs.should_run == 'true', which is empty when disabled. Eyes-on/eyes-off conditions stay symmetric.
tend 0.2.0 adds astral-sh/setup-uv@v10.0.1 to tend-mention's verify job and tend-notifications' check job, immediately before run: steps whose env carries TEND_BOT_TOKEN. "Upstream compromise" described the mutable tag residual as tend's action alone, so name the second publisher and its broader trust; the rationale explains why the existing acceptance reasoning covers only half of it. Also refresh two stale version references in the rationale: the checked-in workflows are at 0.2.0, and the generator link now points at 0.2.0, whose init still writes with Path.write_text following symlinks.
|
Pushed
The non-blocking note about the unretried Verification
|
dormouse-bot
left a comment
There was a problem hiding this comment.
Full non-draft review — no new findings. Both draft findings are resolved by 596b188, verified independently rather than taken from the fix comment.
security-ci.md-> "Upstream compromise" now names the second publisher, and the claim holds against the tree:astral-sh/setup-uv@v10.0.1attend-mention.yaml:254sits insideverify(jobs at 160 / 460), whoseVerify bot engagementstep at 446 carriesGITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }}and runsuv run --script; same shape intend-notifications.yamlat 98 / 107. The FAIL IF at "Tag pins are accepted insidetend-*.yamlalone" still covers the pin, so prose-only was the right scope.- The rationale's new clause is accurate on the trust distinction.
workflow-audit.yamlis the repo's only othersetup-uvconsumer and it SHA-pins (20cfd1bf…) and runs on the defaultGITHUB_TOKEN— noTEND_BOT_TOKENanywhere in that file — so "a publisher this repository had not otherwise trusted with the PAT" is exactly right. - Version strings.
0.1.24survives only inscripts/workflow-audit.test.mjs, where both hits are fixture constants the test constructs and its fakeuvxasserts against — correctly left alone.node scripts/spec-lint.mjsis green.
Nothing else in the regeneration moved a boundary: no trigger changed on any of the eight workflows, and the only permissions delta is verify gaining an explicit contents: read (a narrowing from the inherited default). Every step in every job sits after the Check whether tend is enabled gate and carries the steps.tend_enabled.outputs.enabled == 'true' condition, so there is no ungated path to an outward action. The gate fetches .config/tend.yaml through the contents API with no ref, i.e. from the default branch — the right choice under pull_request_target, since a PR branch cannot flip its own kill switch. The regeneration commit also keeps its paths clean of docs/specs/, so workflow-audit.yaml's is_tend_regen classifier can still take it.
The unretried gh api note from the draft review stands as written; nothing to add. Not approving because GitHub rejects self-approval, not because anything is outstanding.
Nightly regeneration of tend's generated workflow files, picking up the 0.2.0 release.
tend version: 0.1.24 → 0.2.0
Notable changes
Check whether tend is enabledstep that readsenabled:out of.config/tend.yamland gates the checkout and the action on it, so tend can be turned off repo-wide without deleting workflows (Add runtime switch for Tend workflows max-sixty/tend#1132). This is most of the diff volume.claudeeffort and harness arguments are now settable from the workflow (Add Claude effort and harness arguments max-sixty/tend#1161), alongside experimental Codex subscription auth (Add experimental Codex subscription auth max-sixty/tend#1159) and a security fix that shares repository skills with Codex.tend-notificationsacknowledges each resolved thread instead of the whole repository (fix(notifications): acknowledge each resolved thread instead of the whole repository max-sixty/tend#1121), andresolve-conflictslets the bot's own commit unwedge a dependency-bot PR (fix(resolve-conflicts): let this bot's own commit unwedge a dependency-bot PR max-sixty/tend#1140).tend-ci-fixgained a concurrency group per branch and watched workflow (Give ci-fix a concurrency group per branch and watched workflow max-sixty/tend#1148), sweep-found fixes are gated on reachability (skills: gate sweep-found fixes on reachability, and stop tests pinning undefined behavior max-sixty/tend#1134), and posted bodies resolve their SHAs before shipping (fix(running-in-ci): resolve the SHAs in a body before posting it max-sixty/tend#1143).Full upstream compare: max-sixty/tend@0.1.24...0.2.0