Repository navigation
Spec: thirdshift release Command and Pass for scheduled and on-demand releases - #2162
Merged
Merged
Conversation
Fence release bump Passes and tag publication
A Release checkout and add ran inside a worktree of the launch repository, so its clean and smudge filters and core.fsmonitor ran even with hooks disabled. Release git now uses a temporary repository whose origin is that repository's effective URL, and launch-repository reads disable hooks and fsmonitor. The regression test plants a filter and fsmonitor in the launch repository and requires a successful release that does not run them.
The native Release happy-path test requires Bubblewrap and socat, but Rust CI did not prepare its sandbox dependencies or Ubuntu namespace permission. A sandbox startup failure correctly falls back to generated notes, causing the summary assertion to fail. Install the dependencies, grant bwrap its scoped AppArmor namespace permission, and probe startup before tests. Document the prerequisites and include command stderr in the unchanged summary assertion.
Add Release runs for a specified version
4 tasks
Add scheduled Release Passes and automatic minor/patch selection
This was referenced Nov 14, 2026
A release-* branch pusher can amend an exact-bump commit message. Discovery treated that trailer as authority to drop another live cut, publish the planted version, and close the other pull request. Two live cuts now stay blocked. A recut in the current process still records the version it replaced.
Resume native releases and add Experiment and review controls
Refuse a fresh cut whose tag is already on origin before candidate selection, name gh pr ready when a draft bump PR blocks the merge after publication, start over when a closed bump PR's branch is gone, keep the failed-workflow message when its job details cannot be read, and name the PR when its head cannot be fetched. Restore the ported tests' specific assertions, the polling and local-tag scenarios and the declined Experiment override; remove the fake gh's script-only jq branches; correct the manual, testing and navigation docs and the homepage override rule.
Complete the native release cutover
Spec axis: - A Release Pass without --with-experiment no longer publishes a version pinned for an Experiment; it skips naming the resume command. - A Pass waiting on pending main push CI keeps a live Command record, so it shows in Working and Stop reaches it; a later skip ends it skipped. - Release bump-PR, mergeability and release-workflow waits use a five-minute grace again, as the Spec and the retired script did. - An unavailable summary leaves the bump PR's markers empty, so the GitHub Release lists the generated notes once with no placeholder. - A refused release Experiment prints its per-suite results, Advisory suites included, on stderr before the override or failure. - The Day shift ruleset wizard and the workflow generator no longer make a scheduled release eligible. - Tests cover draft GitHub Release recovery, quiet skips with email on, and Cron jobs recognition of versionless release entries. Standards axis: - Correct the start-options scope, the start API's with_experiment field, the release grace and the Cron jobs note in the manual and site pages. - Record the Release summary sandbox exception in ADR 0013 and the Pass's shipped-path filter in docs/ci-performance.md. - Insert release.yml's tag guard above plan's own comment. - Run release tests under the fixture bounds, Experiment-bearing ones under the Experiment bound, and drop stale script-era wording, dead branches and the echoed Candidate previous tag. Refs #2054
JacobStephens2
marked this pull request as ready for review
November 14, 2026 16:09
Resolve conflicts with main: - Start work panel keeps main's compact `start-work` class and this branch's data-start-run / data-start-release attributes. - console/tests/start.test.cjs keeps both the Release start tests and main's closed Start work layout test. - The guide's `admin` scope row keeps Release starts; the `report` row takes main's plan-usage report route. Main gave command_limit::early_skip the Pass's Harness choice for its Plan usage gate (ADR 0031: every scheduled Pass skips when no route has Plan usage), so the Release Pass now passes its own choice.
The site test added for #2054 rejects retired scripts/release.sh mentions; two Fix and Review Pass sentences arriving via main still named it.
Owner
Author
|
Delivery completed on agent-authored or reviewed head df76d0f. |
JacobStephens2
marked this pull request as draft
November 14, 2026 22:17
- A resumed bump PR whose body still names its content base keeps its summary (including one edited with --review) instead of regenerating it. - The --with-experiment pin fetches from the run's isolated repository by path, so the Launch directory's origin settings never run, and is removed once the release branch is on origin, so an abandoned version no longer blocks later Passes. - Recovery hints from the bump-PR wait carry --with-experiment when the run asked for it. - The manual names the Plan usage skip and the kept summary. - Test hygiene: Console start requests use the 45 s helper, pending-CI waits use WAIT_BOUND, and the gh fake's unreachable jq branch is gone; doc comments on Request, run and Kind::ReleaseSummary, and a note on why Release Passes keep held lines.
Anyone can open a fork pull request whose head branch is named release-X.Y.Z. Release discovery treated such a PR as an unfinished release, so one outside PR blocked every Release run until it was closed. A fork PR now counts only when origin already holds that version's branch or unpublished tag.
JacobStephens2
marked this pull request as ready for review
November 15, 2026 02:25
This was referenced Nov 15, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This Spec adds
thirdshift releaseas a native Release run Command, scoped toJacobStephens2/thirdshift. It replacesscripts/release.sh,scripts/release-experiment.pyand the hidden--release-summaryshim;scripts/release-notes.shstays forrelease-notes.yml.<version>(bare, or with--patch,--minoror--major) it runs as a Pass.thirdshift release <version>it runs as a direct Command that cuts or resumes that version.Around the command:
Whole-Spec review fixes (c950a6f):
--review. Before this fix, every resume ran a new summary Session and overwrote the body.--with-experimentpin is fetched from the run's own isolated repository by path, so the Launch directory'soriginsettings (uploadpack,sshCommand, credential helpers) never run.release-X.Y.Zis on origin. An abandoned version no longer blocks every later Pass with "awaits its Experiment".… then thirdshift release X.Y.Z) keep--with-experimentwhen the run used it.WAIT_BOUND, and the gh fake's unreachable--jqbranch is removed.Earlier review fixes (c98a51f): a Pass without
--with-experimentnever publishes a pinned version; a Pass waiting on main CI keeps a live record; release waits have their 5-minute grace; a failed summary leaves the markers empty; a refused Experiment prints per-suite results.Tests sit at one seam, the
thirdshift releasebinary run against a bareoriginwith fakeghand Harnesses (tests/release/,tests/experiments/release.rs), plus the Console API (tests/console/start.rs) and the workflow generator unit tests.Closes #2054. This also resolves #336, and #653 for local-file reads. The remaining toolchain binds are tracked in #2230.
Evidence
Execution evidence
This host now has a C toolchain, so every run below was made here, directly.
tests/release/native.rsand run withcargo test --locked --test release -- resuming_an_open_bump_pr_keeps release_experiment_pin_does_not_run an_abandoned_experiment_cut_leaves_no_pin(plus the two declined tests listed under Unaddressed findings):cargo test --locked --test experiments -- native_release_resumes_its_experiment_and_refuses_an_unattended_inconclusive_verdict, with the new--with-experimenthint assertion:test result: ok, with 1 or 3 passed and 0 failed.cargo test --locked --test releasegave141 passed; 0 failed; 3 ignoredat df76d0f and144 passed; 0 failed; 3 ignoredafter the fixes.scripts/affected-tests refs/remotes/origin/mainchose the full suite:npm test --prefix site: PASS.cargo build --locked: PASS.npm test --prefix console: one failure,Fleet Inbox polls with page subscriptions, retains rows, and fits a phone, a 5-second locator timeout while the machine was loaded. On its own,node --test tests/fleet.test.cjsgivesℹ pass 2 ℹ fail 0.python3 -m unittest discover -s scripts -p test_generate_release.pyran 10 tests, OK, andtest_affected_tests.pyran 46 tests, OK.cargo test --locked --no-fail-fast: 4648 passed, 2 failed and 207 ignored.releaseexperiments::repaired_content_waits_for_its_own_experiment_before_taggingtimed out under full-suite load:Experiment at 520067a5… never reached waiting_on_rechecks. Run alone, it gives1 passed, and the release suite on its own gave 144 passed.plan_usageplan_usage_shows_configured_routes_in_order_and_omits_api_key_and_unused_harnessesasserts thatagyis not installed, but this host has~/.local/bin/agy. With that directory offPATHit gives1 passed. This diff doesn't touch it.cargo clippy --locked --all-targets -- -D warnings: clean.cargo fmt --all: applied.cargo test --locked --bin thirdshift prompts_page: 14 passed.npm run docs:check --prefix site: exits 0 after regenerating the manual.python3 scripts/generate-release.py --check: exits 0 with a clean tree.Visual evidence
The whole-Spec review fixes (c950a6f) change no rendered UI, so these captures, from the earlier round, still show the Console change.
Console Start work, signed in with full scopes, for a
JacobStephens2/thirdshiftLaunch directory, with the machine name redacted tohost.example. Before is the Base branchmainat 864c1b8; After is this branch, with Start Release run opened.Desktop 1440×900:
Mobile 390×844:
The hand-written site page changes are text only: release wording, the five-minute grace and the start-options scopes.
Review coverage
refs/remotes/origin/main...df76d0f1(104 files). Each axis had one follow-up pass for files it had not read.scripts/release.shin full.scripts/release.shlines 230-405, 470-590 and 720-1049;tests/release.rslines 1050-1799 and 1885-2199, read only as a test-name list;tests/release/recovery.rslines 60-150 and thetests/release/experiments.rshelpers, skimmed by name.site/docs/index.html, generated fromdocs/guide.md;npm run docs:check --prefix siteexits 0..github/workflows/release.yml, generated byscripts/generate-release.pywithscripts/release-tag-guard.py;python3 scripts/generate-release.py --checkexits 0. Both reviewers checked that its guard job gatesplan,hostandcustom-publish-crates.prompts/README.mdandsite/prompts/index.html, generated fromsrc/prompt.rsandsrc/prompts_page.rs;cargo test --bin thirdshift prompts_pagepasses 14 tests.prompts/release-*.mdwere read directly.Unaddressed findings
Standards
All of these are judgement calls under the smell baseline, with no documented standard behind them and no defect shown.
Stringversions, string workflow conclusions, merge-base ancestry checks, repeatedCutliterals). These are refactors with no behaviour change, and the release suite pins the behaviour. They are left for an Architecture review rather than churned into this Spec's last pass.LiveLabelvsreview::reservation::Reservation.Reservationalso owns the Review run's acquisition and stale-disposal rules (ADR 0014), which a Release run must not inherit.LiveLabelonly adds and removesin-progressfor ADR 0029.src/console/start_controls.rs:111.crate::releaseexists only on Linux (src/main.rs,#[cfg(target_os = "linux")] mod release;), while the Console builds on every platform.tests/console/start.rs(release_start_schema_exposes_pass_bumps_and_standard_options) checks the published OpenAPIx-command-scopes,x-scopeandx-any-scopes. UnifyingPageRoutescopes is a Console-wide refactor beyond this Spec.fix::targeted_at. These are judgement calls on existing interfaces this Spec extended.run, andOk(None)is documented. The repeated--reviewand session checks are deliberate layers:main.rsrefuses before Setup, as the Spec requires ("validated up front before expensive work"), andrelease::runguards the in-process entry used by the Console.THIRDSHIFT_POLL_MS=1. Raising it would slow every polling test in the suite. The waits are bounded bypending_pollsand are not deadlines, so the Deadlines rule does not apply.RELEASE_GRACEtest is a one-line guard. Both are judgement calls.RELEASE_PATHS(scripts/generate-release.py:22) decides which PRs exerciserelease.yml. The Pass filter is the Spec's list of shipped paths, plus.cargo/andaskama.toml, which change the compiled binary. The two answer different questions, anddocs/ci-performance.mdnames both.docs/research/eval-build-vs-buy.mdis fixed. Restoringscripts/release.sh:NNNreferences would point at a file this Spec deletes.release-tag-guard.py.native_release_publishes_green_content_without_reading_the_launch_tree(tests/release/native.rs) runsscripts/release-tag-guard.pyagainst the tag the Release run actually pushed, so the two implementations are checked against each other end to end, though on fixture manifests. A check against this repository's own manifests is a reasonable follow-up, but no drift exists today.Spec
Done row does not link the published Release: {… "pull_request":"https://github.com/JacobStephens2/thirdshift/pull/4" …}). This is already filed as Release run Done rows do not link the published GitHub Release #2228 (needs-triage): it needs a new public Command record field served through the Console API (ADR 0026), which is a Day shift decision. The test is left out of the branch and quoted in Release run Done rows do not link the published GitHub Release #2228.summary Session read a host toolchain file). Filed as Release summary sandbox can read host toolchain directories #2230 (needs-triage), with the test. Narrowing the binds would break Harnesses installed through npm:harness_intobinds only Kimi Code's package and relies onnodefrom the bound~/.nvm. Choosing between that, a per-Harness runtime bind and amending ADR 0013 is the Day shift's call. Cargo credentials stay blanked.command_limit::early_skipwith every other Pass. The undocumented part is fixed: the#releasingskip list now names Plan usage.Security
release-X.Y.Zblocked every Release run (low, introduced by this change).recovery::inspect(src/release/recovery.rs) treated any open PR whose head is namedrelease-*as a live unfinished release. That included a fork PR from any GitHub account, even when origin had no branch or tag for that version.tests/release/native.rsan_open_fork_pr_on_a_release_branch_name_does_not_block_releasesopens such a fork PR and then runsthirdshift release 0.2.0.scripts/release.shpassed.a_resumed_bump_pr_from_another_repository_is_refused).Security review outcome
No unaddressed introduced findings.
Merge Danger
Door: two-way for the code. Each release a scheduled Pass publishes is a one-way door: tags on GitHub Releases are never moved.
Blast Radius: release pipeline.
main.release.ymltag guard refuses tags not pushed byJacobStephens2or not an exact version bump of amainancestor.release-*PRs.releasestarts needadmin;/api/v1/start-optionsnow acceptsrunoradmin.scripts/setup-release-tags.sh.Tickets
thirdshift release <version>: Release run Command, Consoleadminstart, Cron jobs, and happy-path release #2157 doneminor/patchselection #2158 done--with-experiment, and--review#2159 doneBuilt with claude 2.1.317 · claude-opus-5-5 · high