From 5bbefe6f4cf13b9d0af875e79ab187e2719ced4a Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 20:34:29 -0800 Subject: [PATCH 1/6] quest: plan a release per user-facing merge and pin bumps that follow it Co-Authored-By: Claude Opus 5.5 --- quest/m0/mistake-audit.md | 2 +- quest/m0/pin-bumps/README.md | 39 ++++++++++++++++++++ quest/m0/pin-bumps/discover.md | 26 ++++++++++++++ quest/m0/pin-bumps/follow-release.md | 22 ++++++++++++ quest/m0/pin-bumps/release.md | 34 ++++++++++++++++++ quest/m1/release-cut.md | 53 ---------------------------- quest/m1/release.md | 30 ---------------- 7 files changed, 122 insertions(+), 84 deletions(-) create mode 100644 quest/m0/pin-bumps/README.md create mode 100644 quest/m0/pin-bumps/discover.md create mode 100644 quest/m0/pin-bumps/follow-release.md create mode 100644 quest/m0/pin-bumps/release.md delete mode 100644 quest/m1/release-cut.md delete mode 100644 quest/m1/release.md diff --git a/quest/m0/mistake-audit.md b/quest/m0/mistake-audit.md index 4e4f792c..68c88672 100644 --- a/quest/m0/mistake-audit.md +++ b/quest/m0/mistake-audit.md @@ -29,7 +29,7 @@ and `/reflect`): - Rejected: forge records only; transcripts through a script each run - What gates each run? - ✅ A week since the last run, the way - [the release cut](/quest/m1/release-cut.md) waits. The decision named a + the release cut waited. The decision named a `## Waiting` item because quests could not yet be held until a date; since `## Scheduled` exists, the run holds the date there (agent, 2026-11-11). diff --git a/quest/m0/pin-bumps/README.md b/quest/m0/pin-bumps/README.md new file mode 100644 index 00000000..6cdc424e --- /dev/null +++ b/quest/m0/pin-bumps/README.md @@ -0,0 +1,39 @@ +# Every merge reaches every repository that pins quest + +## Goal + +A user-facing merge to quest's `main` ships as a release, and within the hour +every repository that pins quest and has moq-bot installed gets a bump PR to +that release, which lands on its own. No person or agent hand-bumps a pin. + +The epic finishes with an end-to-end run: a user-facing merge cuts a release, +and the watcher opens and lands a bump to it in an enrolled repository whose +stubs that release changes, with `quest init`'s refreshed stubs in the PR. + +## Plan + +Decided 2026-11-13 (maintainer, /quest-plan), after moq-dev/moq sat on +`93c9c2ef` past [moq-dev/quest#646](https://github.com/moq-dev/quest/pull/646), +so quest spawns there kept writing XS quests on Haiku 5.5 xhigh and hit its +rate limits until a hand bump +([moq-dev/moq#8623](https://github.com/moq-dev/moq/pull/8623)). The bump pass +from [moq-dev/quest#550](https://github.com/moq-dev/quest/pull/550) never ran: +the host's watcher has no `--bump` flags, since its wiring sits in the stale +draft [kixelated/nixos#36](https://github.com/kixelated/nixos/pull/36). + +- What do consumers pin? ✅ A release, cut on every user-facing merge + (rejected: quest's `main`, which the bump pass follows today). +- What bumps them? ✅ The watcher's `--bump`, following the latest release + (rejected: Renovate with a stale-stub check, since hosted Renovate cannot run + `quest init` or `quest check --fix` and most bumps need them: 48 quest + commits touched skills from 2026-11-01 to 2026-11-12; Renovate with a + fixup workflow in each consumer; Kixel Code, which runs no host jobs until + its m1). Kixel Code retires the watcher later and takes bumps with it. +- Do bumps wait for a merge click? ✅ No, they land once checks pass, as the + bump pass does today (rejected: a click, which Kixel Code requires of + dependency bumps; a quest bump is the exception). +- Which repositories? ✅ Every repository moq-bot is installed on whose + `flake.lock` locks quest (rejected: a `--bump` list in the host's + configuration, which #36's went stale within five days). + +Turning it on is the host's work, planned in kixelated/nixos's tree. diff --git a/quest/m0/pin-bumps/discover.md b/quest/m0/pin-bumps/discover.md new file mode 100644 index 00000000..37dc8ee6 --- /dev/null +++ b/quest/m0/pin-bumps/discover.md @@ -0,0 +1,26 @@ +# [S] The bump pass finds its own targets + +## Goal + +`quest land watch` bumps every repository its App is installed on whose +`flake.lock` locks the watched repository as a root input, with no list of +repositories on the host: installing moq-bot on a repository enrolls it, and +uninstalling it stops its bumps. + +## Plan + +Decided 2026-11-13 (maintainer, /quest-plan; see the +[epic](/quest/m0/pin-bumps/README.md)): ✅ discover the targets (rejected: +`--bump ` flags in the host's configuration, which went stale). +Replace the repeatable `--bump ` with one switch; nothing is in +production, so drop the list form. + +A pass lists the App's installations and their repositories, reads each +default branch's `flake.lock`, and treats the repositories that lock the +watched repository as targets. Skip the watched repository itself. Rereading +every repository each pass costs API quota: keep it to what a pass needs, such +as only when the base moved. + +Tests against a fake GitHub: a repository that locks quest is bumped, one +that doesn't is never cloned, and one dropped from the installation is not +bumped again. diff --git a/quest/m0/pin-bumps/follow-release.md b/quest/m0/pin-bumps/follow-release.md new file mode 100644 index 00000000..dbccfba5 --- /dev/null +++ b/quest/m0/pin-bumps/follow-release.md @@ -0,0 +1,22 @@ +# [S] The bump pass follows the latest release + +## Goal + +A bump moves each target to the commit of the watched repository's latest +release instead of its base's head, so consumers run only released quest, and +the PR names the release it moves to. + +## Plan + +Decided 2026-11-13 (maintainer, /quest-plan; see the +[epic](/quest/m0/pin-bumps/README.md)): ✅ consumers pin releases (rejected: +quest's `main`). + +Keep the pin lock-only, as the pass does today: `flake.nix` names no revision +or tag, and `flake.lock` locks the release's commit, so a bump stays a +lock-only diff. A repository that wants a slower cadence leaves moq-bot off +and pins by hand. + +## Required + +- [Release every user-facing merge](/quest/m0/pin-bumps/release.md) - the releases a bump follows diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md new file mode 100644 index 00000000..01370109 --- /dev/null +++ b/quest/m0/pin-bumps/release.md @@ -0,0 +1,34 @@ +# [M] Release every user-facing merge + +## Goal + +Each push to `main` with a user-facing change publishes a new quest release +with no person involved: the Release workflow picks the next version, tags +it, and publishes the binaries, so the biweekly cut and the maintainer's tag +push are gone. `quest --version` names the release in its binaries, and a +flake build names its release or commit. + +## Plan + +Decided 2026-11-13 (maintainer, /quest-plan; see the +[epic](/quest/m0/pin-bumps/README.md)): ✅ the workflow cuts every release +(rejected: each user-facing PR bumps `package.json` and a merge-time workflow +tags it, since PRs racing through the merge queue would conflict on that +line). This replaces the biweekly cut decided 2026-11-09 on +[moq-dev/quest#587](https://github.com/moq-dev/quest/pull/587) and lifts +CONTRIBUTING.md's rule reserving release tags for the maintainer: no release +has shipped since v0.1.0, because v0.2.0's run failed and v0.2.1 was never +tagged. + +- A push with no user-facing change (only `quest/`, CI, tests, or chores) + cuts nothing. The next version follows semver's 0.x convention from the + conventional-commit subjects since the last tag: minor when one carries `!`, + patch otherwise. +- The version comes from the tag, not a commit: `package.json` stops carrying + one, so no release needs a PR. A tag pushed with `GITHUB_TOKEN` starts no + other workflow, so the run that tags also builds and publishes. +- The first release checks what [the release cut](https://github.com/moq-dev/quest/blob/2a80c902/quest/m1/release.md) + waited to check: the Windows ZIPs SETUP.md expects and every Install check + job. Then drop SETUP.md's v0.1.0 caveat and the "next release" notes in + README.md and docs/windows.md. +- Update CONTRIBUTING.md's Releases section and SETUP.md's pinning advice. diff --git a/quest/m1/release-cut.md b/quest/m1/release-cut.md deleted file mode 100644 index 40b35a77..00000000 --- a/quest/m1/release-cut.md +++ /dev/null @@ -1,53 +0,0 @@ -# [S] Cut a release - -## Goal - -Every two weeks, when `main` has user-facing changes since the last tag, a -new quest release ships with its version bumped, so adopters on the release, -mise, and installer routes stay current. Recurring; nothing ends it. - -## Plan - -Decided 2026-11-09 (maintainer, /quest-complete of -[moq-dev/quest#587](https://github.com/moq-dev/quest/pull/587)): - -- How are later releases cut? ✅ This recurring quest, every two weeks, when - `main` has user-facing changes (rejected: a step in CONTRIBUTING.md's - Releases section; ad hoc). A quest resurfaces each time ready work is - triaged; a checklist line doesn't. -- Where is the two-week gate? ✅ A `## Waiting` item (rejected: a Plan line). - Quests can't be held until a date, so a Plan line would let a spawn claim - the quest only to find it too early, while coordinators check outside - conditions before starting and `quest waiting` lists them. Each run's PR - resets the item to the new release's date when it recreates the quest. - -Each run: - -- If the commits from the last tag to `origin/main` hold no - user-facing change (only `quest/`, CI, tests, or chores), skip the cut and - recreate the next run. -- Bump `package.json` by semver's 0.x convention: minor when the range has a - breaking (`!`) commit, patch otherwise. List the user-facing changes in the - PR body. -- The tag push stays a maintainer step, since CONTRIBUTING.md reserves - release tags for the maintainer; agents cannot push them. The PR rewrites - this quest to the checks that follow the tag, with a `Maintainer:` item to - push `vX.Y.Z` from `main` after merge, checked with - `gh release view vX.Y.Z -R moq-dev/quest`. -- After the tag, check that the release has every archive SETUP.md names and - that the Install check (`.github/workflows/install-check.yml`) passed all - its jobs on the Release run. Plan a quest for any failure. -- The PR that finishes those checks deletes this run and creates the next at - this path, or at the same path in the lowest open milestone once m1 - completes. It carries forward the last tag, its date, and anything to check - first, and resets the `Waiting` item to two weeks after the new release. - -## Waiting - -- moq-dev/quest: two weeks pass since the last release (v0.2.1 once it - ships); check `gh release list -R moq-dev/quest --limit 1` - -## Required - -- [Cut a release with the worktree hooks](/quest/m1/release.md) - v0.2.1 is - the first release this cadence follows diff --git a/quest/m1/release.md b/quest/m1/release.md deleted file mode 100644 index 8650ca87..00000000 --- a/quest/m1/release.md +++ /dev/null @@ -1,30 +0,0 @@ -# [S] Cut a release with the worktree hooks - -## Goal - -A tagged quest release includes `quest worktree hook`, so SETUP.md's release, -mise, and installer routes give adopters a `quest` whose Claude Code hooks -work. Later releases follow [Cut a release](/quest/m1/release-cut.md). - -## Plan - -`package.json` is at 0.2.1. The v0.2.0 tag stays a tag with no release: its -Release run failed on macOS, which -[moq-dev/quest#624](https://github.com/moq-dev/quest/pull/624) fixed. - -Once the v0.2.1 release exists: - -- Check it has the Windows ZIPs SETUP.md expects, and that the Install check - (`.github/workflows/install-check.yml`, which Release calls) passes all 12 - jobs; against v0.1.0 its 4 Windows jobs failed for lack of Windows assets - ([run 40144909978](https://github.com/moq-dev/quest/actions/runs/40144909978) - and [run 41660575051](https://github.com/moq-dev/quest/actions/runs/41660575051)). -- Then drop SETUP.md's v0.1.0 caveat and the "next release" notes in - README.md and docs/windows.md. - -## Waiting - -- Maintainer: once this bump merges, tag `main` as `v0.2.1` and push the tag, - since CONTRIBUTING.md reserves release - tags for the maintainer, so no agent pushes one; check with - `gh release view v0.2.1 -R moq-dev/quest` From 9624f49d04eec707f20b3c214ed369670c627cc5 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 21:19:05 -0800 Subject: [PATCH 2/6] quest: plan release recovery, version readers, tag unpinning, and the host wait Co-Authored-By: Claude Opus 5.5 --- quest/m0/pin-bumps/README.md | 15 ++++++++++--- quest/m0/pin-bumps/follow-release.md | 15 +++++++++---- quest/m0/pin-bumps/release.md | 32 +++++++++++++++++++--------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/quest/m0/pin-bumps/README.md b/quest/m0/pin-bumps/README.md index 6cdc424e..c693dcf7 100644 --- a/quest/m0/pin-bumps/README.md +++ b/quest/m0/pin-bumps/README.md @@ -3,8 +3,10 @@ ## Goal A user-facing merge to quest's `main` ships as a release, and within the hour -every repository that pins quest and has moq-bot installed gets a bump PR to -that release, which lands on its own. No person or agent hand-bumps a pin. +every repository whose flake locks quest and has moq-bot installed gets a bump +PR to that release, which lands on its own. No person or agent hand-bumps a +flake pin. Pins through mise or the installer stay out of scope: no moq-dev, +kixel-ai, or kixelated repository uses them. The epic finishes with an end-to-end run: a user-facing merge cuts a release, and the watcher opens and lands a bump to it in an enrolled repository whose @@ -34,6 +36,13 @@ draft [kixelated/nixos#36](https://github.com/kixelated/nixos/pull/36). dependency bumps; a quest bump is the exception). - Which repositories? ✅ Every repository moq-bot is installed on whose `flake.lock` locks quest (rejected: a `--bump` list in the host's - configuration, which #36's went stale within five days). + configuration, since #36's list went stale within five days). Turning it on is the host's work, planned in kixelated/nixos's tree. + +## Waiting + +- kixelated/nixos: the quest watcher runs the bump pass, planned as + `quest/m0/quest-bump.md` there + ([kixelated/nixos#70](https://github.com/kixelated/nixos/pull/70)); check + that the `quest-land` journal on the maintainer's host shows a `bump` line. diff --git a/quest/m0/pin-bumps/follow-release.md b/quest/m0/pin-bumps/follow-release.md index dbccfba5..882cf9fd 100644 --- a/quest/m0/pin-bumps/follow-release.md +++ b/quest/m0/pin-bumps/follow-release.md @@ -12,10 +12,17 @@ Decided 2026-11-13 (maintainer, /quest-plan; see the [epic](/quest/m0/pin-bumps/README.md)): ✅ consumers pin releases (rejected: quest's `main`). -Keep the pin lock-only, as the pass does today: `flake.nix` names no revision -or tag, and `flake.lock` locks the release's commit, so a bump stays a -lock-only diff. A repository that wants a slower cadence leaves moq-bot off -and pins by hand. +Keep the pin lock-only: `flake.nix` names no revision or tag, and +`flake.lock` locks the release's commit, so a bump stays a lock-only diff. +Today the pass strips only a revision from the input's URL (`questInput` and +`unpin` in `src/cli/land-bump.ts`); strip a tag too, or a later +`nix flake update` resolves the tag again and moves the pin back. A repository +that wants a slower cadence leaves moq-bot off and pins by hand. + +Key a target's recorded bump on the release it moved to, not the base head the +watcher passes in: otherwise a pass that runs before the Release workflow tags +the base records the target done, and the release that follows is skipped +until `main` moves again. ## Required diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md index 01370109..3f21380b 100644 --- a/quest/m0/pin-bumps/release.md +++ b/quest/m0/pin-bumps/release.md @@ -20,15 +20,27 @@ CONTRIBUTING.md's rule reserving release tags for the maintainer: no release has shipped since v0.1.0, because v0.2.0's run failed and v0.2.1 was never tagged. -- A push with no user-facing change (only `quest/`, CI, tests, or chores) - cuts nothing. The next version follows semver's 0.x convention from the - conventional-commit subjects since the last tag: minor when one carries `!`, - patch otherwise. +- Each run compares `main` with the last tag, not the push alone, and cuts + when that range holds a user-facing change (anything beyond `quest/`, CI, + tests, or chores). So a run that failed or was cancelled ships its change + with the next run, and runs on `main` queue rather than cancel each other, + unlike the PR runs Release's concurrency cancels today. The next version + follows semver's 0.x convention from the range's conventional-commit + subjects: minor when one carries `!`, patch otherwise. - The version comes from the tag, not a commit: `package.json` stops carrying - one, so no release needs a PR. A tag pushed with `GITHUB_TOKEN` starts no - other workflow, so the run that tags also builds and publishes. -- The first release checks what [the release cut](https://github.com/moq-dev/quest/blob/2a80c902/quest/m1/release.md) - waited to check: the Windows ZIPs SETUP.md expects and every Install check - job. Then drop SETUP.md's v0.1.0 caveat and the "next release" notes in - README.md and docs/windows.md. + one, so no release needs a PR. Every reader of it takes the version from the + build instead: the CLI's `--version` (`src/cli/main.ts`, `src/cli/hooks.ts`), + `nix/package.nix`, and Release's version check. A release binary embeds its + tag; a flake build, which cannot see tags, embeds its commit. A tag pushed + with `GITHUB_TOKEN` starts no other workflow, so the run that tags also + builds and publishes. - Update CONTRIBUTING.md's Releases section and SETUP.md's pinning advice. +- The first automated release only exists after the implementing PR merges. + That PR rewrites this quest to its checks, with a `## Waiting` item on the + release, rather than deleting it: the Windows ZIPs SETUP.md expects, every + Install check job, and `quest worktree hook --help` from a release binary, + which [the v0.2.1 cut](https://github.com/moq-dev/quest/blob/2a80c902/quest/m1/release.md) + waited to check. Then drop SETUP.md's v0.1.0 caveat and the "next release" + notes in README.md and docs/windows.md, and delete this quest. +- Milestone: ✅ m0 (rejected: m1), so those first-release checks, which the + deleted m1 quests held, move to m0 with it. From 163ce9ad07d14819d87c3e9584a13a7c3b958ed7 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 21:26:11 -0800 Subject: [PATCH 3/6] quest: recover a tagged but unpublished release, and name every version reader Co-Authored-By: Claude Opus 5.5 --- quest/m0/pin-bumps/release.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md index 3f21380b..0a2a16b5 100644 --- a/quest/m0/pin-bumps/release.md +++ b/quest/m0/pin-bumps/release.md @@ -20,17 +20,20 @@ CONTRIBUTING.md's rule reserving release tags for the maintainer: no release has shipped since v0.1.0, because v0.2.0's run failed and v0.2.1 was never tagged. -- Each run compares `main` with the last tag, not the push alone, and cuts - when that range holds a user-facing change (anything beyond `quest/`, CI, - tests, or chores). So a run that failed or was cancelled ships its change - with the next run, and runs on `main` queue rather than cancel each other, - unlike the PR runs Release's concurrency cancels today. The next version - follows semver's 0.x convention from the range's conventional-commit - subjects: minor when one carries `!`, patch otherwise. +- Each run compares `main` with the last published release, not the push + alone, and cuts when that range holds a user-facing change (anything beyond + `quest/`, CI, tests, or chores). A tag whose run failed before publishing + is built and published again before any newer one is cut, so a failed run's + change ships with the next run instead of hiding behind its tag. Release's + concurrency cancels superseded pull request runs today; runs on `main` must + queue instead. The next version follows semver's 0.x convention from the + range's conventional-commit subjects: minor when one carries `!`, patch + otherwise. - The version comes from the tag, not a commit: `package.json` stops carrying one, so no release needs a PR. Every reader of it takes the version from the - build instead: the CLI's `--version` (`src/cli/main.ts`, `src/cli/hooks.ts`), - `nix/package.nix`, and Release's version check. A release binary embeds its + build instead: `--version` in `src/cli/main.ts`, the stale-stub notice in + `src/cli/hooks.ts`, `nix/package.nix`, and Release's version check and its + Windows install check's fallback tag. A release binary embeds its tag; a flake build, which cannot see tags, embeds its commit. A tag pushed with `GITHUB_TOKEN` starts no other workflow, so the run that tags also builds and publishes. From 451f1847d0e7d5a9b1ffd7902d3f828d5577e096 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 22:08:01 -0800 Subject: [PATCH 4/6] quest: Release reconciles main with published releases and tags last Co-Authored-By: Claude Opus 5.5 --- quest/m0/pin-bumps/README.md | 8 ++--- quest/m0/pin-bumps/discover.md | 7 ++-- quest/m0/pin-bumps/follow-release.md | 4 +-- quest/m0/pin-bumps/release.md | 51 ++++++++++++++++------------ 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/quest/m0/pin-bumps/README.md b/quest/m0/pin-bumps/README.md index c693dcf7..9b864ff2 100644 --- a/quest/m0/pin-bumps/README.md +++ b/quest/m0/pin-bumps/README.md @@ -2,19 +2,19 @@ ## Goal -A user-facing merge to quest's `main` ships as a release, and within the hour +A merge to quest's `main` that changes what ships becomes a release, and within the hour every repository whose flake locks quest and has moq-bot installed gets a bump PR to that release, which lands on its own. No person or agent hand-bumps a flake pin. Pins through mise or the installer stay out of scope: no moq-dev, kixel-ai, or kixelated repository uses them. -The epic finishes with an end-to-end run: a user-facing merge cuts a release, +The epic finishes with an end-to-end run: a merge to `src/` cuts a release, and the watcher opens and lands a bump to it in an enrolled repository whose stubs that release changes, with `quest init`'s refreshed stubs in the PR. ## Plan -Decided 2026-11-13 (maintainer, /quest-plan), after moq-dev/moq sat on +Decided 2026-11-12 (maintainer, /quest-plan), after moq-dev/moq sat on `93c9c2ef` past [moq-dev/quest#646](https://github.com/moq-dev/quest/pull/646), so quest spawns there kept writing XS quests on Haiku 5.5 xhigh and hit its rate limits until a hand bump @@ -23,7 +23,7 @@ from [moq-dev/quest#550](https://github.com/moq-dev/quest/pull/550) never ran: the host's watcher has no `--bump` flags, since its wiring sits in the stale draft [kixelated/nixos#36](https://github.com/kixelated/nixos/pull/36). -- What do consumers pin? ✅ A release, cut on every user-facing merge +- What do consumers pin? ✅ A release, cut on every merge that changes what ships (rejected: quest's `main`, which the bump pass follows today). - What bumps them? ✅ The watcher's `--bump`, following the latest release (rejected: Renovate with a stale-stub check, since hosted Renovate cannot run diff --git a/quest/m0/pin-bumps/discover.md b/quest/m0/pin-bumps/discover.md index 37dc8ee6..820c6d90 100644 --- a/quest/m0/pin-bumps/discover.md +++ b/quest/m0/pin-bumps/discover.md @@ -3,13 +3,13 @@ ## Goal `quest land watch` bumps every repository its App is installed on whose -`flake.lock` locks the watched repository as a root input, with no list of +`flake.lock` locks the watched repository through exactly one root input, with no list of repositories on the host: installing moq-bot on a repository enrolls it, and uninstalling it stops its bumps. ## Plan -Decided 2026-11-13 (maintainer, /quest-plan; see the +Decided 2026-11-12 (maintainer, /quest-plan; see the [epic](/quest/m0/pin-bumps/README.md)): ✅ discover the targets (rejected: `--bump ` flags in the host's configuration, which went stale). Replace the repeatable `--bump ` with one switch; nothing is in @@ -17,7 +17,8 @@ production, so drop the list form. A pass lists the App's installations and their repositories, reads each default branch's `flake.lock`, and treats the repositories that lock the -watched repository as targets. Skip the watched repository itself. Rereading +watched repository through exactly one root input as targets, which is what +the bump needs (`questInput` in `src/cli/land-bump.ts`). Skip the watched repository itself. Rereading every repository each pass costs API quota: keep it to what a pass needs, such as only when the base moved. diff --git a/quest/m0/pin-bumps/follow-release.md b/quest/m0/pin-bumps/follow-release.md index 882cf9fd..5e984004 100644 --- a/quest/m0/pin-bumps/follow-release.md +++ b/quest/m0/pin-bumps/follow-release.md @@ -8,7 +8,7 @@ the PR names the release it moves to. ## Plan -Decided 2026-11-13 (maintainer, /quest-plan; see the +Decided 2026-11-12 (maintainer, /quest-plan; see the [epic](/quest/m0/pin-bumps/README.md)): ✅ consumers pin releases (rejected: quest's `main`). @@ -26,4 +26,4 @@ until `main` moves again. ## Required -- [Release every user-facing merge](/quest/m0/pin-bumps/release.md) - the releases a bump follows +- [Release every merge that changes what ships](/quest/m0/pin-bumps/release.md) - the releases a bump follows diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md index 0a2a16b5..1cf36b3e 100644 --- a/quest/m0/pin-bumps/release.md +++ b/quest/m0/pin-bumps/release.md @@ -1,8 +1,8 @@ -# [M] Release every user-facing merge +# [M] Release every merge that changes what ships ## Goal -Each push to `main` with a user-facing change publishes a new quest release +Each push to `main` that changes what ships publishes a new quest release with no person involved: the Release workflow picks the next version, tags it, and publishes the binaries, so the biweekly cut and the maintainer's tag push are gone. `quest --version` names the release in its binaries, and a @@ -10,7 +10,7 @@ flake build names its release or commit. ## Plan -Decided 2026-11-13 (maintainer, /quest-plan; see the +Decided 2026-11-12 (maintainer, /quest-plan; see the [epic](/quest/m0/pin-bumps/README.md)): ✅ the workflow cuts every release (rejected: each user-facing PR bumps `package.json` and a merge-time workflow tags it, since PRs racing through the merge queue would conflict on that @@ -20,25 +20,34 @@ CONTRIBUTING.md's rule reserving release tags for the maintainer: no release has shipped since v0.1.0, because v0.2.0's run failed and v0.2.1 was never tagged. -- Each run compares `main` with the last published release, not the push - alone, and cuts when that range holds a user-facing change (anything beyond - `quest/`, CI, tests, or chores). A tag whose run failed before publishing - is built and published again before any newer one is cut, so a failed run's - change ships with the next run instead of hiding behind its tag. Release's - concurrency cancels superseded pull request runs today; runs on `main` must - queue instead. The next version follows semver's 0.x convention from the - range's conventional-commit subjects: minor when one carries `!`, patch - otherwise. -- The version comes from the tag, not a commit: `package.json` stops carrying - one, so no release needs a PR. Every reader of it takes the version from the - build instead: `--version` in `src/cli/main.ts`, the stale-stub notice in - `src/cli/hooks.ts`, `nix/package.nix`, and Release's version check and its - Windows install check's fallback tag. A release binary embeds its - tag; a flake build, which cannot see tags, embeds its commit. A tag pushed - with `GITHUB_TOKEN` starts no other workflow, so the run that tags also - builds and publishes. +Decided 2026-11-12 (maintainer, at the review's design checkpoint): ✅ Release +reconciles `main` with the published releases, and tagging is its last step +(rejected: recovery steps for each way a run can fail, which review kept +finding new cases of; semantic-release, an added dependency that publishes +before the checks this needs to pass first). So: + +- Every run, on each push to `main` and on a daily schedule, compares `main` + with the latest published release and cuts one when that range touches + what ships: `src/`, `assets/`, the package manifests, or the flake. A + cancelled or failed run then needs no recovery: the next run sees the same + unreleased range. Runs on `main` queue rather than cancel each other. +- A run builds every binary and passes the lifecycle and archive install + checks on those local artifacts before it tags. Tagging and publishing come + last, so no tag exists without its release. The next version is greater + than every existing tag, so the dead v0.2.0 and v0.2.1 tags are skipped: + minor when the range holds a `!` subject, patch otherwise. +- The checks that need a published release, mise and the installer's URL, + run after it and fail loudly; their fix ships as the next release. They + block no consumer, since the bump pass moves flake pins, which build from + source at the release's commit. +- The version comes from the tag: `package.json` stops carrying one, so no + release needs a PR. Everything that reads it moves to the build's input: + `--version` in `src/cli/main.ts`, the stale-stub notice in + `src/cli/hooks.ts`, `nix/package.nix`, and Release's version checks. A + release binary embeds its tag; a flake build, which cannot see tags, embeds + its commit. - Update CONTRIBUTING.md's Releases section and SETUP.md's pinning advice. -- The first automated release only exists after the implementing PR merges. +- The first automated release exists only after the implementing PR merges. That PR rewrites this quest to its checks, with a `## Waiting` item on the release, rather than deleting it: the Windows ZIPs SETUP.md expects, every Install check job, and `quest worktree hook --help` from a release binary, From a4d4a034fe82d2bf35738bd4b30184c18d2f4245 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 22:25:26 -0800 Subject: [PATCH 5/6] quest: one predicate for what ships, atomic publish, and the pins discovery skips Co-Authored-By: Claude Opus 5.5 --- quest/m0/pin-bumps/README.md | 10 ++++++---- quest/m0/pin-bumps/discover.md | 3 +++ quest/m0/pin-bumps/follow-release.md | 9 +++++---- quest/m0/pin-bumps/release.md | 25 +++++++++++++++---------- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/quest/m0/pin-bumps/README.md b/quest/m0/pin-bumps/README.md index 9b864ff2..950ff1a8 100644 --- a/quest/m0/pin-bumps/README.md +++ b/quest/m0/pin-bumps/README.md @@ -3,8 +3,8 @@ ## Goal A merge to quest's `main` that changes what ships becomes a release, and within the hour -every repository whose flake locks quest and has moq-bot installed gets a bump -PR to that release, which lands on its own. No person or agent hand-bumps a +every other repository with moq-bot installed whose flake locks quest through +exactly one root input gets a bump PR to that release, which lands on its own. No person or agent hand-bumps a flake pin. Pins through mise or the installer stay out of scope: no moq-dev, kixel-ai, or kixelated repository uses them. @@ -34,8 +34,10 @@ draft [kixelated/nixos#36](https://github.com/kixelated/nixos/pull/36). - Do bumps wait for a merge click? ✅ No, they land once checks pass, as the bump pass does today (rejected: a click, which Kixel Code requires of dependency bumps; a quest bump is the exception). -- Which repositories? ✅ Every repository moq-bot is installed on whose - `flake.lock` locks quest (rejected: a `--bump` list in the host's +- Which repositories? ✅ Every other repository moq-bot is installed on whose + `flake.lock` locks quest through exactly one root input. quest's own + `quest-pinned` input is the merge driver's, which `quest check` keeps + current; bumping it would cut a release that triggers another bump (rejected: a `--bump` list in the host's configuration, since #36's list went stale within five days). Turning it on is the host's work, planned in kixelated/nixos's tree. diff --git a/quest/m0/pin-bumps/discover.md b/quest/m0/pin-bumps/discover.md index 820c6d90..5e7deb04 100644 --- a/quest/m0/pin-bumps/discover.md +++ b/quest/m0/pin-bumps/discover.md @@ -22,6 +22,9 @@ the bump needs (`questInput` in `src/cli/land-bump.ts`). Skip the watched reposi every repository each pass costs API quota: keep it to what a pass needs, such as only when the base moved. +Update README.md's "Pin bumps" section and its examples to the switch, +matching `quest land watch --help`. + Tests against a fake GitHub: a repository that locks quest is bumped, one that doesn't is never cloned, and one dropped from the installation is not bumped again. diff --git a/quest/m0/pin-bumps/follow-release.md b/quest/m0/pin-bumps/follow-release.md index 5e984004..7580f1ba 100644 --- a/quest/m0/pin-bumps/follow-release.md +++ b/quest/m0/pin-bumps/follow-release.md @@ -14,15 +14,16 @@ quest's `main`). Keep the pin lock-only: `flake.nix` names no revision or tag, and `flake.lock` locks the release's commit, so a bump stays a lock-only diff. -Today the pass strips only a revision from the input's URL (`questInput` and -`unpin` in `src/cli/land-bump.ts`); strip a tag too, or a later -`nix flake update` resolves the tag again and moves the pin back. A repository +Today the pass detects and strips only a revision: `questInput` in +`src/cli/land-bump.ts` reads `original.rev`, while a tag pin is recorded as +`original.ref`. Detect and strip a tag too, or a later `nix flake update` +resolves the tag again and moves the pin back. A repository that wants a slower cadence leaves moq-bot off and pins by hand. Key a target's recorded bump on the release it moved to, not the base head the watcher passes in: otherwise a pass that runs before the Release workflow tags the base records the target done, and the release that follows is skipped -until `main` moves again. +until `main` moves again. Update README.md's "Pin bumps" section to match. ## Required diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md index 1cf36b3e..ca5800d8 100644 --- a/quest/m0/pin-bumps/release.md +++ b/quest/m0/pin-bumps/release.md @@ -28,31 +28,36 @@ before the checks this needs to pass first). So: - Every run, on each push to `main` and on a daily schedule, compares `main` with the latest published release and cuts one when that range touches - what ships: `src/`, `assets/`, the package manifests, or the flake. A + what ships: the paths Release's pull request filter already lists as + building the binaries, plus every file it publishes, such as + `scripts/installer.sh`. One predicate serves both, so they cannot drift. A cancelled or failed run then needs no recovery: the next run sees the same unreleased range. Runs on `main` queue rather than cancel each other. - A run builds every binary and passes the lifecycle and archive install - checks on those local artifacts before it tags. Tagging and publishing come - last, so no tag exists without its release. The next version is greater - than every existing tag, so the dead v0.2.0 and v0.2.1 tags are skipped: - minor when the range holds a `!` subject, patch otherwise. + checks on those local artifacts before it tags. It then uploads every + asset to a draft release and publishes the draft, which creates the tag, so + no tag exists without its complete release; a run that fails before that + leaves only a draft, which the next run replaces. The next version is + greater than every existing tag, so the dead v0.2.0 tag is skipped (v0.2.1 + was never tagged): minor when the range holds a `!` subject, patch + otherwise. - The checks that need a published release, mise and the installer's URL, run after it and fail loudly; their fix ships as the next release. They block no consumer, since the bump pass moves flake pins, which build from source at the release's commit. - The version comes from the tag: `package.json` stops carrying one, so no release needs a PR. Everything that reads it moves to the build's input: - `--version` in `src/cli/main.ts`, the stale-stub notice in - `src/cli/hooks.ts`, `nix/package.nix`, and Release's version checks. A + find them by searching for it, since they span the CLI (`src/cli/main.ts`, + `src/cli/hooks.ts`), `nix/package.nix`, `scripts/lifecycle-check.mjs`, the + tests, and the workflows' version checks and Windows fallback tag. A release binary embeds its tag; a flake build, which cannot see tags, embeds its commit. - Update CONTRIBUTING.md's Releases section and SETUP.md's pinning advice. - The first automated release exists only after the implementing PR merges. That PR rewrites this quest to its checks, with a `## Waiting` item on the release, rather than deleting it: the Windows ZIPs SETUP.md expects, every - Install check job, and `quest worktree hook --help` from a release binary, - which [the v0.2.1 cut](https://github.com/moq-dev/quest/blob/2a80c902/quest/m1/release.md) - waited to check. Then drop SETUP.md's v0.1.0 caveat and the "next release" + Install check job, and that a release binary has `quest worktree hook`, the + Goal of [the v0.2.1 cut](https://github.com/moq-dev/quest/blob/2a80c902/quest/m1/release.md). Then drop SETUP.md's v0.1.0 caveat and the "next release" notes in README.md and docs/windows.md, and delete this quest. - Milestone: ✅ m0 (rejected: m1), so those first-release checks, which the deleted m1 quests held, move to m0 with it. From 78a410773aca8793f61e5ff2d693967b665f3edc Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 12 Nov 2026 22:30:24 -0800 Subject: [PATCH 6/6] quest: what ships includes assets and nix, and the flake passes its commit Co-Authored-By: Claude Opus 5.5 --- quest/m0/pin-bumps/README.md | 5 +++-- quest/m0/pin-bumps/release.md | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/quest/m0/pin-bumps/README.md b/quest/m0/pin-bumps/README.md index 950ff1a8..59d80049 100644 --- a/quest/m0/pin-bumps/README.md +++ b/quest/m0/pin-bumps/README.md @@ -36,8 +36,9 @@ draft [kixelated/nixos#36](https://github.com/kixelated/nixos/pull/36). dependency bumps; a quest bump is the exception). - Which repositories? ✅ Every other repository moq-bot is installed on whose `flake.lock` locks quest through exactly one root input. quest's own - `quest-pinned` input is the merge driver's, which `quest check` keeps - current; bumping it would cut a release that triggers another bump (rejected: a `--bump` list in the host's + `quest-pinned` input, the merge driver's, stays on Dependabot's weekly nix + update (`flake.nix`); bumping it here would cut a release, since it changes + the flake, which would trigger another bump (rejected: a `--bump` list in the host's configuration, since #36's list went stale within five days). Turning it on is the host's work, planned in kixelated/nixos's tree. diff --git a/quest/m0/pin-bumps/release.md b/quest/m0/pin-bumps/release.md index ca5800d8..b36b2a10 100644 --- a/quest/m0/pin-bumps/release.md +++ b/quest/m0/pin-bumps/release.md @@ -28,9 +28,11 @@ before the checks this needs to pass first). So: - Every run, on each push to `main` and on a daily schedule, compares `main` with the latest published release and cuts one when that range touches - what ships: the paths Release's pull request filter already lists as - building the binaries, plus every file it publishes, such as - `scripts/installer.sh`. One predicate serves both, so they cannot drift. A + what ships: everything the binaries or the flake package build from, + `assets/` (bundled into the binary) and `nix/` included, plus every file + Release publishes, such as `scripts/installer.sh`. Release's pull request + path filter, which misses `assets/` and `nix/` today, uses the same + predicate, so the two cannot drift. A cancelled or failed run then needs no recovery: the next run sees the same unreleased range. Runs on `main` queue rather than cancel each other. - A run builds every binary and passes the lifecycle and archive install @@ -51,7 +53,9 @@ before the checks this needs to pass first). So: `src/cli/hooks.ts`), `nix/package.nix`, `scripts/lifecycle-check.mjs`, the tests, and the workflows' version checks and Windows fallback tag. A release binary embeds its tag; a flake build, which cannot see tags, embeds - its commit. + its commit, which `flake.nix` passes to `nix/package.nix`. It also builds + `quest-pinned` from that input's own, older `nix/package.nix`, so the new + argument must not break that call. - Update CONTRIBUTING.md's Releases section and SETUP.md's pinning advice. - The first automated release exists only after the implementing PR merges. That PR rewrites this quest to its checks, with a `## Waiting` item on the