Skip to content

chore: update tend workflows (0.1.24 → 0.2.0) - #588

Open
dormouse-bot wants to merge 2 commits into
mainfrom
tend/update-workflows
Open

chore: update tend workflows (0.1.24 → 0.2.0)#588
dormouse-bot wants to merge 2 commits into
mainfrom
tend/update-workflows

Conversation

@dormouse-bot

Copy link
Copy Markdown
Collaborator

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

Full upstream compare: max-sixty/tend@0.1.24...0.2.0

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 596b188
Status: ✅  Deploy successful!
Preview URL: https://eaf25151.mouseterm.pages.dev
Branch Preview URL: https://tend-update-workflows.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 init in a detached worktree at d2eda84, against that commit's own .config/tend.yaml, leaves git status --porcelain -- .github/workflows/ empty. .github/actionlint.yaml also regenerates byte-identical, so nothing is missing from the PR. workflow-audit.yaml's is_tend_regen classifier should skip this commit: single commit, only tend-[a-z-]+\.yaml paths, 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 no enabled: key, so the default-true path is what runs. Ruby 3.2 is in the ubuntu-24.04 image. abort and warn both go to stderr, so only enabled=… reaches $GITHUB_OUTPUT.
  • No behavior regression in the Bash→Python port. Traced tend-mention's verify branch by branch against the deleted shell: dispatch id validation, the issues fast path, the self-comment skip, @-mention on body and on inline comments, the Bot-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's raw_decode loop is the right shape for gh api --paginate's concatenated arrays, and a failing gh stays fatal (CalledProcessErrorSystemExit) the way the shell's set -e command substitution did. Same for tend-notifications: the subscription PUT, the 10-minute cutoff, the HTML-error-page guard (now JSONDecodeError), and the tend-conflict-deferred marker 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 new permissions: contents: read on verify is a narrowing. All eight files parse as YAML.
  • Gating on the disabled path. tend-notifications guards both checkout and the action with (steps.tend_enabled.outputs.enabled == 'true') && (…), so the empty-steps.check.outputs.count case can't fall through to a boot. tend-mention's handle is gated on needs.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.
@dormouse-bot

Copy link
Copy Markdown
Collaborator Author

Pushed 596b188 addressing both review findings — the spec drift this PR introduces, no change to the generated workflows.

  • docs/specs/security-ci.md -> "Upstream compromise" now names the second publisher: tend-mention's verify (line 254 / token env at line 446) and tend-notifications' check (line 98 / token env at line 107) run astral-sh/setup-uv@v10.0.1 immediately before a run: step whose env carries TEND_BOT_TOKEN. The existing FAIL IF at "Tag pins are accepted inside tend-*.yaml alone" already covers the pin itself, so this is prose only.
  • docs/specs/security-ci.rationale.md -> "Why the mutable upstream tag is accepted" picks up why the old acceptance argument only half-covers it: astral-sh is not a publisher this repo had otherwise trusted with the PAT, so the "same trust the harness already has" clause does not carry, and only the generated-file clause does.
  • Stale versions in the rationale: 0.1.240.2.0 for the checked-in workflows, and the generator link retargeted to 0.2.0 after confirming init there still writes with Path.write_text following symlinks.

node scripts/spec-lint.mjs is green; security-ci.md lands at 2474/2500 words, so no re-ratchet. Checks on 596b188 are green.

The non-blocking note about the unretried gh api .../contents/.config/tend.yaml call in the new enable gate stands as written — nothing filed upstream, since it hasn't been observed failing.

Verification
  • Both setup-uv call sites and their token-bearing run: steps read from the tree at d2eda84; verify spans lines 160–459 of tend-mention.yaml, so the GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} at line 446 is in the same job as the setup-uv at line 254.
  • 0.2.0 generator checked at generator/src/tend/cli.pypath.write_text(wf.content, ...) and _update_actionlint_config's path.write_text(updated, ...), same shape the rationale describes.
  • scripts/workflow-audit.test.mjs's 0.1.24 strings are fixture headers the test constructs, not a claim about the checked-in pin — left alone.

@nedtwigg
nedtwigg marked this pull request as ready for review September 7, 2026 18:30

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.1 at tend-mention.yaml:254 sits inside verify (jobs at 160 / 460), whose Verify bot engagement step at 446 carries GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} and runs uv run --script; same shape in tend-notifications.yaml at 98 / 107. The FAIL IF at "Tag pins are accepted inside tend-*.yaml alone" still covers the pin, so prose-only was the right scope.
  • The rationale's new clause is accurate on the trust distinction. workflow-audit.yaml is the repo's only other setup-uv consumer and it SHA-pins (20cfd1bf…) and runs on the default GITHUB_TOKEN — no TEND_BOT_TOKEN anywhere in that file — so "a publisher this repository had not otherwise trusted with the PAT" is exactly right.
  • Version strings. 0.1.24 survives only in scripts/workflow-audit.test.mjs, where both hits are fixture constants the test constructs and its fake uvx asserts against — correctly left alone. node scripts/spec-lint.mjs is 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant