Skip to content

feat(check): guard that a quest's PR leaves its quest accurate - #169

Merged
kixelated merged 10 commits into
mainfrom
quest/m0/landed-quests
Oct 21, 2026
Merged

kixelated merged 10 commits into
mainfrom
quest/m0/landed-quests

Conversation

@kixelated

@kixelated kixelated commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Completes quest/m0/landed-quests.md (plan at d265755). In moq-dev/moq, merged quest-branch PRs left their quests behind: some finished the work without deleting the quest, and some partial drafts wrote the remainder only in the PR body. Those stale quests then ranked as ready work again.

Changes

  • Guard: quest check --base <ref> [--branch <name>] (src/cli/landed.ts). The head branch names the quest at <branch>.md, so an epic's branch is quest/<path>/README. When that quest exists at the merge base with <ref>, the working tree must delete or edit it. Otherwise it reports one finding that names the quest and the rule. Other branches, and branches that name no quest at the merge base, add nothing. A plain quest check behaves as before.
  • Guide: assets/AGENTS.md Execution now states the rule once. The PR that finishes a quest deletes it and every link to it, and a PR that leaves work rewrites the quest to what remains.
  • Merge skill: assets/skills/merge.md fetches the base and head, checks out the head, and runs quest --root <head-checkout> check --base <remote>/<base> --branch <head-branch> before enabling auto-merge. It uses a quest the PR cannot modify, such as an installed release or a build from the base. On a finding it fixes the branch, deleting a finished quest with quest rm, then pushes, re-checks, and asks the user only when what remains is unclear.
  • README: the "complete" paragraph now covers partial PRs. A new "Check a quest's PR" section shows the local invocation and a CI snippet. No workflow ships, per the plan.
  • This PR deletes its own quest and the quest's entry in quest/m0/README.md. quest check --base origin/main --branch quest/m0/landed-quests passes on it.

Decisions made (routine; recorded for review)

  1. Compare the working tree with the merge base, not HEAD. This matches how plain check reads the working tree. It also works in CI, where the checkout is the PR's merge commit.
  2. A detached HEAD with no --branch is an error. The check doesn't skip it silently, because a guard that quietly checks nothing looks the same as one that passed. --branch without --base is a usage error (exit 2). An unknown base or a missing merge base exits 1.
  3. Fork PRs on quest branches are guarded too. Since kixelated/quest#157, forks claim quests from a branch with the quest's name, so --branch <headRefName> applies to them as well. The merge skill can push its fix-up only when the PR allows maintainer edits. Otherwise it asks.
  4. Root and milestone branches are not exempt. A PR on a milestone's own branch (quest/m0/README) has to edit its README, which matches the rule.
  5. The fix-up deletes with quest rm. kixelated/quest#164 landed first, so this PR wires quest rm into the merge skill. feat: move and delete quests with quest mv and quest rm #164's edit to quest/m0/landed-quests.md was resolved by keeping the deletion.
  6. ✅ An epic's branch is its README's path without .md (quest/m1/foo/README), decided by the maintainer. This overrides the settled plan's line naming an epic's branch quest/<path>. It matches the guide's wording and the CLI's questBranch from feat: move and delete quests with quest mv and quest rm #164. The directory form collides with child branches in git's ref namespace, since refs/heads/quest/m0 can't coexist with refs/heads/quest/m0/*. kix.quest still uses the directory form, which is left as a follow-up.

Review

Codex findings addressed: the merge skill runs a trusted quest against the head with --root (security), and epic branch naming now follows decision 6.

Validation

  • just check: passed.
  • just test: passed. Vitest ran 274 tests, 12 of them in test/landed.test.ts. The Worker suite ran 265. The shell suites and the lifecycle checks on both builds passed.
  • Tests cover a branch that deletes its quest, one that edits it, and one that leaves it untouched (the finding). They also cover uncommitted edits, an epic's /README branch checked against its README, an epic's directory not counting as its branch, a non-quest branch, a quest that doesn't exist at the base, --branch overriding the current branch, a base that moved on and edited the quest (still a finding), a detached HEAD, an unknown base, and --branch without --base.

Suggested follow-ups

  • Move kix.quest to the /README epic branch: the start job (cloudflare/src/jobs/routes.ts), associatedQuest (cloudflare/src/github/state.ts), and the board's quest URLs.
  • Run the guard in this repository's own CI on pull requests, using the README snippet. That covers merges that skip the merge skill. It wasn't shipped here because the plan says to document it, not ship a workflow.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 2 commits October 21, 2026 11:44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
quest check --base <ref> [--branch <name>] flags a quest branch whose diff from the merge base neither deletes nor edits its quest. The guide states the rule once, and the merge skill runs the guard before enabling auto-merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Owner Author

Outcome: the quest is complete. quest check --base guards quest-branch PRs, the guide states the rule, and the merge skill runs the guard before auto-merge. just check and just test passed locally. The routine decisions are listed in the description. The quest rm wiring is left to whichever of this PR and #164 lands second.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 21, 2026 18:50
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T20:42:00.384748Z 194a153 New commits
🔒 Security Review ✅ Completed 2026-10-21T20:43:02.825466Z 194a153 New commits

Security findings

Advisory findings (2)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e77cb34de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread README.md Outdated
Comment on lines +154 to +155
with `<ref>`. When the branch is `quest/<path>`, naming `quest/<path>.md` or an
epic's `quest/<path>/README.md` at the merge base, the diff must delete or edit

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep epic branch names consistent with the claiming workflow

This introduces quest/<path> as the branch for quest/<path>/README.md, but the shared guide still defines every branch as the document path without .md, making that epic's branch quest/<path>/README. An agent following this new section can therefore open an epic PR on a branch that other agents will not search when checking the quest's canonical branch, allowing a duplicate claim; either retain the /README suffix here and in questAt, or change the branch convention consistently across the guide and claiming workflow.

AGENTS.md reference: assets/AGENTS.md:L122-L123

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in cf27b05 by changing the guide rather than the guard. The guard already matches the existing convention: kix.quest starts an epic on its directory (cloudflare/src/jobs/routes.ts strips /README.md), and associatedQuest and the board use the same mapping. assets/AGENTS.md now says an epic's branch is its directory, with quest/m1/foo/README.md → quest/m1/foo as the example.

(Written by Claude Opus 5.5)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Update: the maintainer chose the other way in 1cafbdc. The guide keeps its original wording, so an epic's branch is quest/<path>/README, and questAt now looks up only <branch>.md. This retains the /README suffix as you first suggested. The directory form collides with child branches in git's ref namespace. The README-named-epic fix from cce32bd is no longer needed and is removed.

(Written by Claude Opus 5.5)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 9e77cb34de

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread assets/skills/merge.md Outdated
and re-run it with `--since` until it returns activity.

Merge the PR *only* after all outstanding decisions have been confirmed.
Before enabling auto-merge, fetch the PR's base branch and head, check out the head, and run `quest check --base <remote>/<base> --branch <head-branch>`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ Codex Security Review · Automatically triggered

P1 Badge Security: Run the post-checkout guard with a trusted binary

When /quest-merge handles an external/fork PR from a trusted checkout, this step checks out the contributor-controlled head and then invokes quest. In this repository, AGENTS.md expands that command to bun run --silent quest, whose package.json script executes src/cli/bin.ts from the checked-out head. A PR can replace either entrypoint and execute code in the maintainer process before merge, with access to the local account and any forge credentials used by this workflow. Invoke an absolute, pinned Quest binary built from the trusted base, passing the PR worktree through --root, or run the head without credentials in a sandbox.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in cf27b05. The merge skill now runs quest --root <head-checkout> check --base … with a quest the PR cannot modify, such as an installed release or a build from the base, never one built from the head. Running other head code, like CI fixes on a fork PR, is outside this PR's scope.

(Written by Claude Opus 5.5)

kixelated and others added 2 commits October 21, 2026 12:01
# Conflicts:
#	quest/m0/landed-quests.md
…ranches

Address Codex review on #169: the merge skill runs a quest the
PR cannot modify, pointed at the head with --root, and the guide states that
an epic's branch is its directory, matching the guard and kix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf27b053db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/landed.ts Outdated
Comment on lines +44 to +45
for (const path of [`${branch}.md`, `${branch}/README.md`]) {
if (git(root, ["cat-file", "-e", `${commit}:./${path}`]).status === 0) return path;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve README-named epic branches to the nested README

When a valid tree contains an epic such as quest/m0/epic/README/README.md, its branch is quest/m0/epic/README, but the first candidate here is the parent epic's quest/m0/epic/README.md, which normally also exists. Consequently, editing or deleting the nested epic still produces a finding, while an incidental edit to the parent can incorrectly satisfy the guard. Skip the .md candidate when the branch basename is README, or otherwise resolve candidates by their canonical branch names.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in cce32bd. A branch ending in /README now resolves only to <branch>/README.md, since README.md belongs to its parent epic, whose branch is the directory. The regression test is "an epic named README is checked against its own README, not its parent's".

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 21, 2026 12:21
A branch ending in /README names a nested epic called README; README.md is
its parent's epic, whose branch is the directory. Address Codex review on
#169.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep the deletion of quest/m0/landed-quests.md over #164's
edit, and keep both README sections. With quest rm on main, the merge
skill's fix-up deletes a finished quest with quest rm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3ec5b228e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread assets/AGENTS.md Outdated
Comment on lines +122 to +123
A quest's branch is its path without `.md`, and an epic's is its directory.
For example, `quest/m1/foo/bar.md` is branch `quest/m1/foo/bar`, and `quest/m1/foo/README.md` is `quest/m1/foo`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update claim lookup for epic branch names

When moving an epic with an active canonical claim such as quest/m1/foo, moveQuest still passes its quest/m1/foo/README.md through questBranch(), which removes only .md and therefore searches for quest/m1/foo/README. Because claims() never sees the actual branch or PR, quest mv quest/m1/foo ... can proceed despite its claimed-work guard; update the shared branch derivation to follow this new epic convention.

AGENTS.md reference: assets/AGENTS.md:L122-L123

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Valid. This exposes a split in the codebase, so it is held for a maintainer decision and not fixed in this round. The CLI's questBranch (from #164) names an epic quest/m1/foo/README. kix.quest's start job, associatedQuest, and this guard name it quest/m1/foo. The directory form also collides with git's ref namespace: refs/heads/quest/m0 can't exist alongside any refs/heads/quest/m0/*, and origin has 36 of those today. The options and a recommendation are reported to the maintainer.

(Written by Claude Opus 5.5)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Decided and fixed in 1cafbdc. An epic's branch is its README's path without .md, e.g. quest/m1/foo/README. That matches questBranch and the guide's original wording, and avoids the ref collision with child branches. The guard now resolves a branch only to <branch>.md, so quest mv and the guard agree. Moving kix.quest (start job, associatedQuest, board URLs) to /README is a follow-up.

(Written by Claude Opus 5.5)

An epic's branch is quest/<path>/README, matching the guide and the CLI's
questBranch. The directory form collides with child branches in git's ref
namespace. Revert the guide's directory wording and drop the README-named
epic special case, which no longer applies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 1cafbdcd80

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread assets/skills/merge.md Outdated
Comment on lines +43 to +44
Before enabling auto-merge, fetch the PR's base branch and head, check out the head, and run `quest --root <head-checkout> check --base <remote>/<base> --branch <head-branch>`.
Run a `quest` the PR cannot modify, such as an installed release or a build from the base, not one from the head.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ Codex Security Review · Automatically triggered

P1 Badge Security: Run Quest from a trusted working directory

After this fix, a fork author can still add bunfig.toml with preload = ["./p.ts"]. If the merge agent follows line 43 from the checked-out head, both the installed Bun-script Quest and the Bun-compiled release load that cwd config before Quest starts, so p.ts runs with the maintainer’s environment/GitHub access. Line 44 protects the binary but not Bun’s runtime config. Fresh evidence after cf27b05: a local probe showed an absolute trusted script and a trusted bun --compile binary both executed the cwd preload; Bun documents preload as pre-entrypoint execution. Require a trusted cwd (using --root only as data) and disable local Bun config or sandbox the check.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in f685884. The merge skill now runs the trusted quest from a directory outside the head checkout, so --root is only data. It names the head's runtime config, such as bunfig.toml, as untrusted.

(Written by Claude Opus 5.5)

kixelated and others added 2 commits October 21, 2026 13:24
Resolve README and guide conflicts with #167, and run the
merge guard from a directory outside the head checkout so the head's
runtime config cannot run (Codex security review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Owner Author

Merge summary: quest check --base now guards quest-branch PRs. The guide states the rule, and the merge skill runs the guard before auto-merge, deleting a finished quest with quest rm.

Since the first review:

Codex reviewed f685884 with no findings in either code or security review. The only change since then is a clean merge of main that touches none of this PR's files. CI is green on 194a153. just check, just test, and quest check --base origin/main --branch quest/m0/landed-quests pass.

Follow-ups: move kix.quest's start job, associatedQuest, and board URLs to the /README epic branch, and run the guard in this repository's CI.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 37c286b into main Oct 21, 2026
14 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 194a153305

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread assets/skills/merge.md
Comment on lines +43 to +47
Before enabling auto-merge, fetch the PR's base branch and head, check out the head, and run `quest --root <head-checkout> check --base <remote>/<base> --branch <head-branch>`.
Run a `quest` the PR cannot modify, such as an installed release or a build from the base, from a directory outside the head checkout: the head's files, including runtime config such as `bunfig.toml`, are untrusted.
A stale local base can predate the quest and skip the check.
On a finding, fix the branch: delete the finished quest with `quest rm`, or rewrite it to the remainder the PR body names.
Then push, re-check, and continue; ask the user only when what remains is unclear.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify a clean head before trusting the landed check

When the PR head is already checked out with an uncommitted edit or deletion of its quest, landed.ts compares the merge base against that working tree, so this check passes even though the fetched PR head does not contain the change. Because this sequence pushes only after a finding, it can then enable auto-merge without publishing the quest update; git checkout -h confirms that only a forced checkout throws away local modifications. Require a clean checkout or validate the committed fetched head before trusting the result.

AGENTS.md reference: assets/AGENTS.md:L129-L132

Useful? React with 👍 / 👎.

Comment thread src/cli/landed.ts
Comment on lines +27 to +28
const diff = git(root, ["diff", "--quiet", "--no-ext-diff", commit, "--", `./${path}`]);
if (diff.status === 1) return [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore mode-only changes when checking quest edits

On Unix repositories with core.fileMode enabled, merely toggling the executable bit on the quest makes git diff --quiet return 1, so this function reports success even though the quest content was neither deleted nor rewritten. A PR can therefore leave the stale quest intact while satisfying the new guard; compare the blob contents or otherwise reject metadata-only changes.

Useful? React with 👍 / 👎.

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