Skip to content

feat(cli): claim a quest with a draft PR through quest start - #167

Merged
kixelated merged 10 commits into
mainfrom
quest/m0/helpers/start
Oct 21, 2026
Merged

kixelated merged 10 commits into
mainfrom
quest/m0/helpers/start

Conversation

@kixelated

@kixelated kixelated commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Completes quest/m0/helpers/start.md. Before starting or spawning, agents ran git ls-remote origin 'refs/heads/quest/...' and gh pr list --head/--search by hand (105 of 396 sessions), and two sessions sometimes worked on the same quest. This PR was itself claimed the way quest start now does it: an empty commit, then a draft PR opened right away.

Changes

  • quest start <quest> claims a ready quest. It checks for the quest's branch upstream and for open PRs from that branch in any repository, then builds an empty claim commit on the upstream default branch with a random Quest-Claim: nonce. It pushes the commit to the branch with an explicit refspec, never forcing, and opens a draft PR right away. It prints the PR URL and switches the checkout onto a new local branch at the claim. If the quest branch is already checked out, it fast-forwards it instead. It fails first on a tree quest check rejects, since a malformed Required can hide a blocker.
    • A rejected push means someone else pushed the branch first. If two PRs race (from forks, say), the earliest open PR keeps the claim. The later one is closed and its branch deleted.
    • A GitHub fork (fork/parent from the API) pushes to itself and opens the PR on its parent. Off GitHub, only the branch is pushed, with a warning.
    • --remote overrides origin.
    • It refuses a blocked quest, and a quest missing from the upstream default branch (a stale checkout).
  • quest start --release <quest> deletes the branch with --force-with-lease on its tip, then closes the PR. It does this only while the tip is still an empty commit, and otherwise refuses and says why.
  • Exit codes: 0 done, 3 refused (blocked, held, or work on the branch), 2 usage, 1 failure.
  • quest unblocked [path] replaces quest ready, which is removed to free the name for the PR-state command. Given a path, it prints the blocker list, or the themed quest log on a terminal. --unclaimed drops held quests from the ranking: one ls-remote plus one paginated open-PR listing. For each skipped quest it prints a stderr line with the PR, its state (draft, draft, decisions needed, or ready for review), and how long ago it was last active.
  • Hand-back marker: the guide now says to hand back a draft by adding a ## Decisions needed section, and that tools read only whether that heading is present. A draft without the heading still belongs to its starter.
  • CI on ready PRs only: check.yml adds ready_for_review to the pull_request types and gives each job github.event_name != 'pull_request' || github.event.pull_request.draft == false. The guide, README, and SETUP.md recommend the same pattern.
  • Docs: the guide's Execution section builds on kixelated/quest#157's draft-PR text and now uses the commands. The start and spawn skills, README (new "Claim a quest" section, matching --help), docs/getting-started.md, SETUP.md, and the kix Worker's text and comments all use quest unblocked now.
  • The quest is deleted. Its Required entry in pr-states is dropped, and audit references in other quests now point at the deleted file at d265755.

Shared GitHub code

After kixelated/quest#162 and kixelated/quest#164 merged, this PR extends main's single src/cli/github.ts and src/cli/claims.ts, which quest mv, quest status, quest wait, and quest start all use:

  • GitHub.request(path, { etag, body, method }) can PATCH, accepts any 2xx, and includes GitHub's error message (such as a 422 reason) in failures. wait and graphql use the options form.
  • claims.ts resolves the upstream once with upstream(): a fork's parent, reached with the remote's URL scheme. Its repository name comes from the configured remote URL before insteadOf expansion. This replaces forkParent and githubBranches. A fork's own branches (read from its push URL) and PRs still count, as quest mv expected. remoteBranches returns tips. openPullRequests returns number, head repository, draft, timestamps, and the hand-back flag. Claim adds pull to branch and url.

Decisions made (routine; recorded for review)

  1. The terminal's themed list of ready quests from quest ready is dropped. On a terminal, unblocked keeps its heading and count\tpath lines. The themed single-quest view moves to unblocked <path>.
  2. Exit code 3 means "refused, pick other work". It covers blocked quests, held quests, and release refusals.
  3. Forks are detected through the GitHub API rather than a separate --upstream flag. The parent is reached with the remote's URL scheme (clone_url or ssh_url).
  4. A claim held only by a branch (no PR) is listed without an age. Getting one would cost an API call per branch.
  5. --release accepts any empty single-parent commit at the tip whose parent is on the upstream default branch, so claims made by hand can be released too, but an empty commit after pushed work cannot. It does not touch local branches.
  6. The draft PR's title is the quest title without the size, and its body links the quest. The tool adds no model footer, since it posts as whoever runs it.
  7. Branches on a fork's parent are read with git ls-remote on the parent's URL rather than through the GitHub API, since quest start fetches its base from there anyway.

Suggested follow-ups

  • Theme the quest unblocked ranking on a terminal (size colours and titles), bringing back what quest ready's list showed.
  • Check readiness against the fetched upstream tree rather than the checkout, so a blocker added upstream after your last pull is caught (Codex finding).
  • Name the head repository from a remote's push URL, for triangular remotes that fetch upstream and push to a fork (Codex finding).
  • Switch moq-dev/moq and moq-dev/moq.pro CI to ready PRs only, as the quest planned.
  • The kix board should treat a draft without ## Decisions needed as Accepted. You're planning this separately.

Validation

  • just check: passed.
  • just test: passed after merging main. Vitest ran 284 CLI tests (21 in test/start.test.ts) and 265 Worker tests. The shell suites and lifecycle checks for both builds passed too.
  • New tests run against local bare remotes, with GitHub replayed in memory and insteadOf mapping github.com URLs to the bare repos. They cover: claiming a free quest; a branch already upstream; a fork PR holding the claim, with and without ## Decisions needed, and once ready; two claimers racing; a fork PR that opened first winning after ours opens; claiming from a fork; a quest missing upstream; release; release refusing a branch with work, or with an empty commit after work; claims from a fork; the non-GitHub path through the CLI; --root below the git top level; switching or fast-forwarding the checkout; a malformed tree; a fork reached through a URL alias; and the filtered --unclaimed ranking. Codex review findings (checkout, tree validation, alias scheme) are fixed in 878e919, and the release finding in 96c9b81.
  • Tried live: quest unblocked --unclaimed on this repository lists every open quest PR as skipped.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 4 commits October 21, 2026 11:44
claim-nonce: 30d93de94810d0ee

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace quest ready with quest unblocked [path] across the guide, skills, README, SETUP, and the kix Worker, and delete the finished quest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review October 21, 2026 19:03
@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:21:15.072633Z afa357c New commits
🔒 Security Review ✅ Completed 2026-10-21T20:28:58.574560Z afa357c New commits
ℹ️ 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: 669540c28b

ℹ️ 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/start.ts Outdated
Comment on lines +72 to +74
// A local branch to build on, unless one already exists.
if (!succeeds(root, ["update-ref", `refs/heads/${branch}`, sha, ""])) {
deps.warn(`local branch ${branch} already exists; the claim commit is ${sha}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Check out the claim commit before returning

This creates the quest ref but never attaches the current worktree to it; git update-ref only stores the new object in the named ref. In a fresh isolated worktree, later edits therefore remain on the base branch, while a worktree created with git worktree add -b <quest-branch> hits the warning path and leaves that branch behind the remotely pushed claim commit, causing the prescribed explicit push to omit the work or be rejected as non-fast-forward. Switch the current worktree to the claim commit, or fail with actionable checkout instructions, before reporting success.

AGENTS.md reference: AGENTS.md:L17-L17

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 878e919. quest start now switches the checkout onto a new local branch at the claim commit. If the quest branch is already checked out (for example after git worktree add -b), it fast-forwards it. If a local branch exists but can't be moved safely, it warns with the merge to run. Tests cover all three cases.

(Written by Claude Opus 5.5)

Comment thread src/cli/main.ts
Comment on lines +228 to +233
if (blockers(docs, doc).length > 0) {
explain(docs, doc, path);
code = 3;
return;
}
if (theme) print("Ready quests by milestone and downstream impact (other prerequisites may remain):");
for (const quest of ranked) print(`${quest.dependents}\t${quest.path}`);
settle(startQuest(root(), doc.path, doc.title?.text ?? doc.path, options.remote, deps), (started) =>

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 Validate the quest tree before claiming

When the local tree is structurally invalid, blockers() alone can misclassify work as ready; for example, a blocked quest whose ## Required heading is mistyped as ## Requires has no blockers according to this predicate even though quest check rejects it. Calling quest start directly in that state pushes a claim branch and opens a PR for blocked work, so run the same structural check used by the ranking before performing any remote mutation.

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 878e919. quest start now runs the same structural check as the ranking before it touches the remote, and exits 1 with the findings. There's a regression test for a misspelled ## Requires.

(Written by Claude Opus 5.5)

Comment thread src/cli/claims.ts Outdated
Comment on lines +87 to +88
return {
git: string(/^https:/.test(configured || url) ? parent.clone_url : parent.ssh_url),

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 Preserve the resolved URL scheme for fork parents

For a fork remote configured through an alias such as corp:me/project with url.https://github.com/.insteadOf=corp:, remoteUrl() resolves to HTTPS and identifies GitHub, but this test inspects the nonempty unexpanded alias and chooses the parent's SSH URL. Git documents that git remote get-url expands insteadOf configuration (official documentation), so HTTPS-only users can push to their configured fork yet quest start fails while reading the parent over SSH; choose the scheme from the resolved URL when the configured value did not itself identify GitHub.

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 878e919. The configured URL is used only when it names GitHub itself. Otherwise the resolved URL decides both the repository and the parent's scheme. There's a regression test with a corp: alias that resolves to HTTPS.

(Written by Claude Opus 5.5)

…ork's URL scheme

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

Copy link
Copy Markdown
Owner Author

Outcome: the quest is implemented and ready for review.

  • quest start claims a quest with an empty commit and a draft PR, and quest start --release gives the claim back.
  • quest unblocked [path] replaces quest ready. --unclaimed filters the ranking to quests nobody holds.
  • The guide now defines ## Decisions needed as the hand-back marker.
  • CI runs only on PRs that are ready for review.

All three Codex findings are fixed in 878e919. just check and just test pass locally.

The routine decisions I made are listed in the PR body. src/cli/github.ts and claims.ts still need unifying with kixelated/quest#164 and kixelated/quest#162. Whichever of those PRs lands after this one should do it.

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 21, 2026 12:15
…test

Windows ARM runners spend seconds spawning git; the first test hit vitest's 5 s default.

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

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: 2d4d74985c

ℹ️ 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/start.ts Outdated
Comment on lines +176 to +178
const empty =
parents.length === 1 &&
gitOutput(root, ["rev-parse", `${tip}^{tree}`]) === gitOutput(root, ["rev-parse", `${parents[0]}^{tree}`]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require the tip to be the generated claim commit

When work has been pushed and is followed by any empty commit (for example, a CI retrigger), this check passes because it compares only the tip's tree with its immediate parent. --release then deletes the branch and closes its PR, discarding all earlier work despite promising to refuse once work exists; verify the tip's Quest-Claim metadata rather than accepting every empty commit.

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 96c9b81. --release now also requires the empty commit's parent to be on the upstream default branch, so an empty commit after pushed work (a CI retrigger, say) is refused. That keeps hand-made claims releasable without requiring the Quest-Claim trailer. The release test now covers a retrigger after work.

(Written by Claude Opus 5.5)

Comment thread src/cli/start.ts
Comment on lines +46 to +49
const base = remoteHead(root, up.git);
const prefix = gitOutput(root, ["rev-parse", "--show-prefix"]);
if (!succeeds(root, ["cat-file", "-e", `${base}:${prefix}${path}`])) {
throw new Refusal(`${path} is not on the upstream default branch; update your checkout`);

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 Recheck readiness from the fetched upstream tip

When the local checkout is stale or locally edited, the CLI validates readiness from the local files but builds the claim from the freshly fetched upstream tip; this check verifies only that the quest still exists there. If upstream has since added a blocker or become structurally invalid, quest start therefore pushes a claim for blocked work, so readiness and structure must be validated against base (or the local quest tree must be proven identical to it) before pushing.

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.

Not fixing in this PR; recording it as a follow-up. The skill already tells agents to update their checkout before quest start, and the gap is a blocker added upstream between that update and the claim. Validating against the fetched base means loading the quest tree from a commit rather than the checkout, which quest check --base work may provide. A wrong claim here is a draft PR that quest start --release undoes.

(Written by Claude Opus 5.5)

Comment thread src/cli/claims.ts Outdated
Comment on lines +77 to +79
const configured = gitOutput(root, ["config", "--get", `remote.${remote}.url`]);
const url = remoteUrl(root, remote);
const source = githubRepository(configured) ? configured : url;

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 the repository from the push URL

When a triangular remote fetches from upstream but has remote.<name>.pushurl pointing at the user's fork, these calls identify the fetch repository as both the PR base and head even though git push <remote> writes to the fork. The subsequent PR creation looks for the branch upstream, fails, and deletes the fork branch; Git documents that git remote get-url --push queries push URLs, so repository discovery must account for that URL separately.

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.

Not fixing in this PR; recording it as a follow-up. Triangular remotes (fetch from upstream, pushurl to a fork) are uncommon. The merge did make the push side consistent: a fork's own branches and --release now read the remote's push URL. Identifying the head repository from the push URL is the remaining piece.

(Written by Claude Opus 5.5)

Re-apply this PR's extensions on main's src/cli/github.ts and
src/cli/claims.ts instead of keeping a second copy:

- GitHub.request takes { etag, body, method } so it can PATCH, accepts
  any 2xx, and reports GitHub's reason for a refusal. wait and graphql
  use the options form.
- claims.ts resolves the upstream once (a fork's parent, by the
  remote's URL scheme), returns branch tips from remoteBranches, and
  reads the extra PR fields quest start needs. forkParent and
  githubBranches fold into upstream(); a fork's own branches and PRs
  still count, as quest mv expected.
- quest start --release refuses an empty tip that follows pushed work:
  the empty commit must sit straight on the upstream default branch.

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: 96c9b817b5

ℹ️ 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/start.ts Outdated
head: fork ? `${github.head.owner}:${branch}` : branch,
base: github.defaultBranch,
draft: true,
body: `Claims [${path}](https://github.com/${owner}/${repo}/blob/${github.defaultBranch}/${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 Include the repository prefix in the claim link

When --root points below the Git worktree root (a supported case covered by the new subdirectory test), the quest exists upstream at ${prefix}${path}, but this PR body links to /${path}. The claim succeeds while its only quest link points to the wrong file or a 404; pass the repository-relative prefixed path to openDraft and use it in the URL.

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 afa357c. The draft PR links the quest by its repository path, with the --root prefix and URL-encoded. A new test claims a quest whose tree sits under sub/ and checks the link.

(Written by Claude Opus 5.5)

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

Copy link
Copy Markdown
Owner Author

Merging via /quest-merge, as approved.

  • Merged main after kixelated/quest#162, kixelated/quest#164, and kixelated/quest#166. There is now one GitHub client (src/cli/github.ts) and one claims module (src/cli/claims.ts), used by quest mv, quest status, quest wait, and quest start. The request extensions (PATCH, any 2xx, GitHub's error reason) and the claim extensions (upstream(), branch tips, extra PR fields) sit on main's versions. upstream() replaces forkParent and githubBranches, and a fork's own branches and PRs still count. Main's skill and guide changes are kept, and no quest ready command uses remain.
  • Codex findings: fixed the release check, so an empty commit after work is not released (96c9b81), and the claim link below the top level (afa357c). Two are deferred as follow-ups: checking readiness against the fetched upstream tree, and naming the head repository from a push URL for triangular remotes.
  • Decisions are recorded in the PR body. just check, just test, and CI pass on afa357c. The only change since Codex's review of 96c9b81 is the one-line link fix.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 57d44e3 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: afa357c791

ℹ️ 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/start.ts
head: fork ? `${github.head.owner}:${branch}` : branch,
base: github.defaultBranch,
draft: true,
body: `Claims [${path}](https://github.com/${owner}/${repo}/blob/${github.defaultBranch}/${file}).`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the required model attribution to claim PRs

When an agent follows the updated start or spawn skill, this generated body is the initial GitHub post, but it does not end with the required (written by <model>) attribution. Have the caller supply the running model or otherwise ensure the generated PR body includes that footer before creation.

AGENTS.md reference: AGENTS.md:L31-L32

Useful? React with 👍 / 👎.

Comment thread src/cli/start.ts
remote: string,
deps: ClaimDependencies,
): Promise<Started> {
const branch = questBranch(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 Encode quest paths before deriving branch names

When a valid quest filename contains a character Git forbids in refs, such as a space, questBranch() returns that character verbatim and the push always fails, even though the tree loader accepts any non-instruction .md filename and the move functionality supports names containing spaces. Encode branch components or make quest check reject paths that cannot form the documented quest branch.

Useful? React with 👍 / 👎.

Comment thread src/cli/start.ts
const { owner, repo } = github.repository;
const fork = github.head.owner !== owner || github.head.repo !== repo;
// The quest's path in the repository, which differs from `path` when the tree sits below the top level.
const file = encodeURI(gitOutput(root, ["rev-parse", "--show-prefix"]) + 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 Percent-encode reserved characters in the quest link

For a valid quest such as quest/m0/foo#bar.md, encodeURI leaves the # unescaped, so the generated GitHub URL requests quest/m0/foo and treats bar.md as a fragment. Such a path can also form a valid Git branch, so the claim succeeds but its only quest link is broken; encode each path component with encodeURIComponent instead.

Useful? React with 👍 / 👎.

kixelated added a commit that referenced this pull request Oct 21, 2026
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 added a commit that referenced this pull request Oct 21, 2026
Re-create the afk quest to replace the `afk` argument that landed in #166
with /quest-afk. Move ready-at-hand-back into guide-rules, since quest start
landed in #167 without it. Drop pr-states' Required on the landed quest
start, and the helpers README entries for landed quests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated deleted the quest/m0/helpers/start branch October 21, 2026 21:05
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