Skip to content

build: staging moves itself every Friday after the full check, and stageaf installs it - #1438

Merged
AbirAbbas merged 7 commits into
devfrom
feat/weekly-staging
Sep 25, 2026
Merged

AbirAbbas merged 7 commits into
devfrom
feat/weekly-staging

Conversation

@AbirAbbas

Copy link
Copy Markdown
Collaborator

Summary

  • Weekly staging promotion. A new workflow, Promote to staging, runs every Friday. It picks the newest first-parent dev commit whose committer time is at or before 17:00 America/Toronto and runs the Full check on that exact commit. If the check passes, it fast-forwards staging to the commit and waits for the staging build to publish. Every outcome is posted to Slack and to the run summary.
  • Production signal. The same Friday run posts the commit staging held before its own move as the candidate for main, with the exact push command. main stays manual and runs one week behind staging.
  • stageaf. curl -fsSL https://agentfield.ai/get/stageaf | bash installs the staging build as stageaf beside codeaf, the staging twin of devaf. The proxy row is website2.0 PR multi-attach R1 / L2: home's held row opens through the host instead of being refused #64; this PR makes codeaf spell that address (a stageaf's own reinstall line, staging release notes, the manual, and GUIDE).

Why the cutoff is a commit time

Scheduled runs here start hours late: the nightly 0 9 * * * started between 12:59Z and 15:46Z this week. The cron (30 22 * * 5, which is 18:30 EDT / 17:30 EST) only has to fire some time after the cutoff. A law test checks that relation in both UTC offsets. dev only moves by squash merges that GitHub makes, so a commit's committer time is the moment it landed on dev.

Changes

  • ci-full.yml: takes workflow_call with a required ref, plus an optional ref on dispatch. Every checkout uses that ref, the concurrency group is keyed by it, and the nightly-issue page job ignores called runs. Push, pull-request and nightly behavior are unchanged. This also repairs the runbook's by-hand step: gh workflow run ci-full.yml --ref $SHA is refused for a sha, so it becomes --ref dev -f ref=$SHA.
  • promote-staging.yml (jobs plan, plan-failed, signal, full_check, finish) plus new cmd/codeaf-release promotion-* subcommands. The branching logic (cutoff math, candidate choice, outcome, release-run matching, Slack text and escaping) is in Go and tested with injected clocks and throwaway git repositories. The push is fast-forward only, rechecked immediately before it happens, and never touches main. If anything fails unexpectedly, an error trap still posts a message saying whether staging moved.
  • scripts/slack-post.sh: posts to SLACK_RELEASE_WEBHOOK. If the secret is missing, the message still lands in the run summary and the run only warns.
  • stageaf added to update.CurlLine, the staging release notes, running-from-the-terminal.md (new section + probes), and docs/GUIDE.md.
  • Docs: docs/rules/promotion.md (the weekly road, every message and what to do next, retry and dry run, one-time setup), docs/rules/ci.md, .github/rulesets/README.md, and CLAUDE.md Branches.

One-time setup (owner, before the first Friday that should push)

  1. PROMOTION_TOKEN: a fine-grained personal access token scoped to this repository, with Contents: read and write and Workflows: read and write, owned by an account that can bypass the live protection ruleset (check with gh api repos/Agent-Field/codeaf/rulesets/<id> --jq .current_user_can_bypass → always). GITHUB_TOKEN can't do this job: its pushes start no other workflow, so Release would never publish, and GitHub refuses its pushes when the commits touch .github/workflows/.
  2. SLACK_RELEASE_WEBHOOK: a Slack incoming webhook. The webhook's own configuration picks the channel.

Without the token, a Friday run still runs the check. It then reports that staging did not move, gives the exact by-hand push, and ends red.

Test plan

  • make pr-ready BASE=c26ea9c98: light gate plus fresh cmd/codeaf, cmd/codeaf-release, internal/manual, internal/release, internal/update, all ok.
  • actionlint v1.7.7 on every workflow; make test-laws; make test-packed-manual.
  • Workflow law tests were mutation-checked: removing a needs edge, a Full check checkout or its ref, or persist-credentials: false turns them red.
  • Ran the built tool against the real dev/staging/main with --now 2026-09-25T22:30:00Z. It picks c26ea9c98 (6 changes over 5ba989468), and every message renders as intended (moved, check failed or cancelled, token missing, build did not publish, current, dry run, and both production-signal shapes).
  • An adversarial review found two blockers (a failed plan reported nothing; a release run could match on sha alone) and six should-fix items. All are fixed, and each has a test.
  • After merge: gh workflow run promote-staging.yml --ref dev -f dry_run=true -f signal=true proves the reusable call, the permissions and the messages on GitHub without moving staging.

Rollout order

Merge website2.0 PR #64 first, so /get/stageaf answers before any staging release notes or Slack message hand it out.


🤖 Generated with Claude Code

AbirAbbas and others added 4 commits September 24, 2026 13:43
Accept a required ref from reusable callers and an optional ref from a manual
dispatch. Check out that ref in every job and give it a separate concurrency
group. Keep the nightly issue job off a called check so a promotion failure
cannot page with the caller's SHA. The runbook's by-hand step, which passed a
sha to --ref and was refused, becomes --ref dev -f ref=$SHA.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Choose the latest first-parent dev commit at the Toronto Friday 17:00
committer-time cutoff, run the reusable Full check on it, and fast-forward
staging only after a successful result. Recheck both refs immediately before
the token-backed push, wait for the release run created by this push, and send
every outcome to the run summary and the configured Slack webhook. Report a
failed plan and unexpected finish errors even when the release tool fails.

Send a separate production signal for the staging pointer captured before the
promotion. Keep main manual, and document the retry, dry-run, fine-grained
token, bypass, and fallback roads.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Route update and failed-install guidance for a stageaf executable through the
staging proxy (agentfield.ai/get/stageaf). Name the stageaf install line in
staging release notes, the terminal manual, and the guide. Cover the named
installer, update failure, release notes, and manual retrieval with focused
tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…#1438)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas and others added 3 commits September 25, 2026 10:01
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The plan-failed job ran scripts/slack-post.sh without checking the
repository out, and every job starts in an empty workspace, so a failed
plan reported nothing to the run summary or to Slack. Its shell-block test
copied the script into its own directory and could not see the gap.

A new law test walks every job in promote-staging.yml and fails when a
step runs a repository script or command before any checkout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A plan is current when staging already holds the cutoff commit, which
includes staging having moved past it; the message said "Nothing new on
dev" in both cases, though dev can carry commits after the cutoff. It now
says staging is already past the commit when the two differ.

An empty push, fetch or recheck log made the error reader print an empty
line, which the message quoted as "was refused: ." The reader now refuses
an empty log, and the message names a missing reason instead of drawing a
bare colon.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas
AbirAbbas merged commit 76148d5 into dev Sep 25, 2026
4 checks passed
@AbirAbbas
AbirAbbas deleted the feat/weekly-staging branch September 25, 2026 14:27
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