Skip to content

ci: land the changelog as soothfast-bot via the broker - #161

Merged
Verdenroz merged 2 commits into
masterfrom
feat/soothfast-bot-land
Sep 6, 2026
Merged

Verdenroz merged 2 commits into
masterfrom
feat/soothfast-bot-land

Conversation

@Verdenroz

@Verdenroz Verdenroz commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What changed

changelog.yml is the first workflow off the App private key. It now asks the broker for a soothfast-bot token after the build has finished, and lands the regenerated file with action/land.sh instead of peter-evans/create-pull-request. The same script will serve the adopter action, so the bot flow is exercised on this repo first.

  • Added action/land.sh. Stages PATHS, exits cleanly when nothing changed, commits as APP_SLUG[bot] with a per-command identity, force-pushes to BRANCH with the token on the push URL only, opens or refreshes the PR, and merges with --auto when the default branch has a required-check, review, or merge-queue rule, or immediately when it has none. The push token travels through a credential helper, never a URL, and HEAD is restored after the push. The token is revoked on every exit path.
  • Rewrote the changelog job: no App mint, no user-id lookup, checkout with persist-credentials: false, environment: soothfast-bot, permissions contents: read and id-token: write.
  • Added concurrency: {group: changelog, cancel-in-progress: true} so only the newest regeneration lands.

Closes #145. Part of #158 and #14.

Why

The changelog bot fires on every merge to master, so it is the cheapest place to prove the broker path on a real ruleset before the other workflows follow. The token is minted after the build on purpose: no step that compiles the tree ever holds a write credential. A cancel between mint and land leaves a one-hour token unrevoked; that is accepted, since the alternative is a stale changelog winning the race #145 describes.

How was this tested

  • shellcheck 0.11.0 on action/land.sh and action/bot-token.sh: clean.
  • zizmor 1.29.0 on changelog.yml, bot.yml, bot-smoke.yml: no findings.
  • bash -n on both scripts.
  • Not run end to end: that happens when this merges and the first bot PR fires. If it misbehaves, merge that PR by hand; the revert is this one file.
  • make check and make gate were not run for this layer. It changes no Rust.

Checklist

  • make check passes (fmt, clippy -D warnings, cargo test --workspace)
  • make gate BASE=master passes, or any intentional cost change is explained above
  • Tests added/updated for new behavior (not required for docs/CI-only changes)
  • Public API items have /// doc comments
  • No new dependency, or its justification is included above (see Dependency Policy)
  • Docs (README.md, docs/, soothfast:bind/soothfast:claim markers) updated if behavior changed

@soothfast-bot

soothfast-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

soothfast gate

gate: measuring merge-base of origin/master in worktree (interleaved rounds)
gate: bench binaries identical (code and data match) — no measurable change possible
gate: gating backend = walltime
gate: build=08f03a noise_floor=0.06% thresholds: instructions +5% ir +5% walltime +10.0% alloc/size +5% polls/wakes +5%
ok    soothfast_spec::bench_openapi_diff walltime_median_ns 5177120.0 -> 5177120.0 (+0.0%)
ok    soothfast_spec::bench_openapi_diff allocs 77722 -> 77722 (allowed <= 81608)
ok    soothfast_spec::bench_openapi_diff alloc_bytes 10815487 -> 10815487 (allowed <= 11356261)
ok    soothfast_spec::bench_openapi_document walltime_median_ns 1898703.0 -> 1898703.0 (+0.0%)
ok    soothfast_spec::bench_openapi_document allocs 30152 -> 30152 (allowed <= 31659)
ok    soothfast_spec::bench_openapi_document alloc_bytes 4715065 -> 4715065 (allowed <= 4950818)
ok    soothfast_spec::bench_serialize_yaml walltime_median_ns 6564428.0 -> 6564428.0 (+0.0%)
ok    soothfast_spec::bench_serialize_yaml allocs 74277 -> 74277 (allowed <= 77990)
ok    soothfast_spec::bench_serialize_yaml alloc_bytes 8533615 -> 8533615 (allowed <= 8960295)
ok    soothfast_spec::bench_openapi_diff assert complexity: claimed O(n); growth drift x1.40 over sizes [16, 64, 256] (limit x2.5)
ok    soothfast_spec::bench_openapi_document assert complexity: claimed O(n); growth drift x1.48 over sizes [16, 64, 256] (limit x2.5)
ok    soothfast_spec::bench_serialize_yaml assert complexity: claimed O(n); growth drift x1.17 over sizes [16, 64, 256] (limit x2.5)
gate: passed (3 item(s))

@Verdenroz
Verdenroz force-pushed the feat/soothfast-bot-land branch 2 times, most recently from 00259f0 to 65d8256 Compare September 6, 2026 00:34
@Verdenroz
Verdenroz force-pushed the feat/soothfast-bot-land branch from 65d8256 to c37b1a6 Compare September 6, 2026 01:34
@Verdenroz
Verdenroz force-pushed the feat/soothfast-bot-land branch from c37b1a6 to 46e8e17 Compare September 6, 2026 01:35
Base automatically changed from feat/soothfast-bot-broker to master September 6, 2026 02:01
@Verdenroz
Verdenroz force-pushed the feat/soothfast-bot-land branch 2 times, most recently from 18732ed to e003b9d Compare September 6, 2026 02:02
- changelog.yml is the first workflow off the App private key; the
  token comes from action/bot-token.sh after the build finishes, so no
  step that compiles the tree ever holds it.
- land.sh replaces create-pull-request with plain git and gh so the same
  script serves this repo and the adopter action, and revokes the token
  on every exit path.
- The concurrency group makes the newest regeneration the only one that
  lands, which is the fix #145 asks for.

Closes #145
- History-only rules leave the PR clean and --auto refuses it; only
  status-check, pull-request and merge-queue rules mean there is
  something to wait for.
- The push token now travels through a credential helper, so a git error
  cannot print it, and HEAD is restored after the push for callers that
  run further steps.
@Verdenroz
Verdenroz force-pushed the feat/soothfast-bot-land branch 2 times, most recently from d30b95b to d6a7682 Compare September 6, 2026 02:12
@Verdenroz
Verdenroz merged commit 71b3e4e into master Sep 6, 2026
21 of 22 checks passed
@Verdenroz
Verdenroz deleted the feat/soothfast-bot-land branch September 6, 2026 02:36
Verdenroz added a commit that referenced this pull request Sep 6, 2026
## What changed

After this, no workflow in the repo mints an App token. `spec.yml` and
the `docs-regen` job in `ci.yml` land through `action/bot-token.sh` and
`action/land.sh` the same way `changelog.yml` does. `deploy-docs` and
the release step use `github.token`. `soothfast-gate.yml` drops the App
entirely and becomes something another repo can call.

- Rewrote the `generate` job in `spec.yml` and `docs-regen` in `ci.yml`:
`environment: soothfast-bot`, permissions `contents: read` and
`id-token: write`, checkout with `persist-credentials: false`, a
`concurrency` group each, and the token minted after the generation
step. `docs-regen` keeps its llms.txt churn filter.
- Switched `deploy-docs` (`peaceiris/actions-gh-pages`) and the `gh
release create` step in `release.yml` to `github.token`.
Attribution-only change: the gh-pages push and the release show
github-actions instead of soothfast-bot.
- Made `soothfast-gate.yml` callable cross-repo. The PR comment uses
`github.token` under `pull-requests: write`, the PR number reaches the
shell through `env`, `CHANGELOG_APP_PRIVATE_KEY` stays declared but
optional and unused for one release so existing callers keep validating,
and a new `cli-artifact` input selects a prebuilt CLI from the run or
falls back to the setup action.
- Pointed the `gate` matrix in `ci.yml` at `cli-artifact:
cargo-soothfast-bin` and stopped passing the secret.
- Rewrote the CI section of `CLAUDE.md` around the broker and added a
reusable-gate example to the README's "In CI" section.

Part of #158 and #14.

## Why

The App private key was minted in six places. Once the changelog proves
the broker path, keeping the other five on the key means two credential
paths to reason about and a secret that still has to exist. Moving them
together lets the `CHANGELOG_APP_*` variable and secret be deleted after
this merges. The release and gh-pages pushes never needed a bot
identity, only write permission, which `github.token` already grants. A
tag push is not a default-branch ref, so the broker would refuse it by
design, and that rule stays as is.

## How was this tested

- `zizmor` 1.29.0 across every workflow in `.github/workflows/`: no
findings.
- No App references remain in the workflows outside the unused secret
declaration in `soothfast-gate.yml`.
- Not run end to end: the spec and docs bot PRs fire on the first merge
to master after this lands. The gate matrix runs on this PR itself with
`cli-artifact` set, which exercises the artifact path; the setup-action
fallback is exercised by the first cross-repo caller.
- `make check` and `make gate` were not run for this layer. It changes
no Rust.

After merge, by hand: delete the `CHANGELOG_APP_CLIENT_ID` variable and
`CHANGELOG_APP_PRIVATE_KEY` secret from this repo. That can happen right
away; finance-query holds its own copy of the secret and the reusable
workflow now ignores it. The first master push after this lands runs
spec generate and docs-regen through the broker, so it needs the same
prerequisites as #161: broker deployed, `soothfast-bot` environment
created, smoke run green. Watch that push's `deploy-docs` too: it now
pushes gh-pages with `github.token`, and if the Pages build does not
fire the fallback is the official `upload-pages-artifact` +
`deploy-pages` pair in a separate PR.

## Checklist

- [ ] `make check` passes (fmt, clippy `-D warnings`, `cargo test
--workspace`)
- [ ] `make gate BASE=master` passes, or any intentional cost change is
explained above
- [ ] Tests added/updated for new behavior (not required for
docs/CI-only changes)
- [ ] Public API items have `///` doc comments
- [x] No new dependency, or its justification is included above (see
[Dependency Policy](../CONTRIBUTING.md#dependency-policy))
- [x] Docs (`README.md`, `docs/`, `soothfast:bind`/`soothfast:claim`
markers) updated if behavior changed
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.

changelog.yml has no concurrency group, so regenerations race and lose

1 participant