Repository navigation
docs(quest): plan agent workflow helpers #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # Agent workflow helpers | ||
|
|
||
| ## Goal | ||
|
|
||
| Agents stop hand-writing `gh`, `jq`, and `sed` loops for routine quest | ||
| workflow steps, because `quest` commands cover them and the skills point at | ||
| those commands: CI results while waiting, a one-shot PR status, claims, moving | ||
| and deleting quests, serialized check-then-merge, and unattended triage. | ||
|
|
||
| Only generic helpers belong here. Non-goals: the kix.quest UI, a daemon, and | ||
| deciding whose review or which result counts; the agent still decides. | ||
|
|
||
| ## Plan | ||
|
|
||
| Decided 2026-10-19 from an audit of Claude Code transcripts in moq-dev/moq and | ||
| moq-dev/moq.pro: 396 sessions from Oct 17-19, 2026, plus about 339k Bash | ||
| commands from Sep-Oct. Session counts in each child come from that window. | ||
|
|
||
| - Decided (user): plan only the really generic helpers. Replying to and | ||
| resolving review threads (103 and 68 sessions) is dropped: its attribution | ||
| footer and wording are user and repository policy, and `gh api` already | ||
| does the call. Parsing open decisions from a PR body is dropped from | ||
| `quest status`, since that body format is a skill convention. | ||
| - GitHub calls live in `src/cli/` beside `quest wait`, outside the | ||
| forge-agnostic core. Pure tree edits, such as link rewriting, go in the | ||
| core. The first child to add a second GitHub caller extracts the client in | ||
| `src/cli/wait.ts` (auth, rate-limit retry, same-origin pagination) instead | ||
| of copying it. | ||
| - Decided (user): no separate documentation page. Each child updates | ||
| `--help`, README examples, and the skill prose it replaces; the user asked | ||
| for those skill edits through this plan. | ||
| - Decided (user): this epic ranks after the worktree and gate quests and | ||
| before the Kix decision UI. Children rank by sessions affected. | ||
|
|
||
| ## Required | ||
|
|
||
| - [PR status](/quest/m0/helpers/status.md) - one call for head, checks, merge state, reviews by head, and unresolved threads | ||
| - [Move and delete quests](/quest/m0/helpers/move.md) - `quest mv` and `quest rm` keep every root-absolute link valid | ||
| - [CI checks in wait](/quest/m0/helpers/wait-checks.md) - `quest wait` wakes on required check results for the head | ||
| - [Claim quests](/quest/m0/helpers/claim.md) - `quest claim`, `--release`, and `quest unblocked --unclaimed` | ||
| - [Named gate lock](/quest/m0/helpers/gate-lock.md) - `quest gate --lock <name>` serializes check-then-merge | ||
| - [Unattended triage](/quest/m0/helpers/afk.md) - an `afk` argument that only asks about merges and deletes | ||
|
|
||
| ## Related | ||
|
|
||
| - [Expensive command gate](/quest/m0/local-gate.md) - the gate the lock builds on | ||
| - [Agent lifecycle integration](/quest/m0/worktree-agents.md) - removes the agent-level load-average rules |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # [XS] Triage unattended with an afk argument | ||
|
|
||
| ## Goal | ||
|
|
||
| `/quest-complete afk` and `/quest-spawn afk` act on their recommendations | ||
| without asking, except merges and deletes. This replaces the prompt the | ||
| user keeps typing before stepping away: "skip/iterate any that you | ||
| recommend, and prompt for any that you recommend I merge... relatively | ||
| simple decisions only". | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): one shared `afk` argument for both skills, not | ||
| a new default and not limited to `/quest-complete`. | ||
| - In afk mode, dispatch recommended skip, `/quest-iterate`, and | ||
| `/quest-ready` picks without asking. Still ask about every merge and | ||
| delete, as one simple question each with the recommendation first. | ||
| - A decision that is not simple becomes a skip with a note in the summary | ||
| rather than a question. Suggested `/quest-plan` follow-ups wait for the | ||
| summary, since planning is an interview. | ||
| - The rest of the argument still filters the PRs or quests in scope, as | ||
| today. | ||
| - Edit `assets/skills/complete.md` and `assets/skills/spawn.md`, following | ||
| `PROMPTING.md`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # [S] Claim and release quests with quest claim | ||
|
|
||
| ## Goal | ||
|
|
||
| `quest claim <quest>` claims a ready quest the way `quest guide` describes, | ||
| and `quest unblocked --unclaimed` lists ready quests nobody holds. Agents stop | ||
| running `git ls-remote origin 'refs/heads/quest/...'` and | ||
| `gh pr list --head/--search` by hand before starting or spawning (105 of 396 | ||
| sessions), since `quest ready` checks no claims. | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): one `claim` command plus an `--unclaimed` | ||
| filter, rather than a separate claims listing. | ||
| - `claim` refuses a blocked quest, checks for an open PR from the quest's | ||
| branch, then pushes an empty commit with the explicit refspec | ||
| `refs/heads/<branch>:refs/heads/<branch>`, never forcing. A rejected push | ||
| means someone else holds the claim, which makes the push the atomic check. | ||
| Give each claim commit a random nonce, so two claimers on the same base, | ||
| identity, and second never build the same commit and both see success. | ||
| Distinct exit codes separate "taken" from a failure. | ||
| - Decided (user): `quest claim --release <quest>` deletes the remote claim | ||
| branch only while its tip is still the empty claim commit and it has no | ||
| open PR, using a lease on that SHA; otherwise it refuses and says why. | ||
| - `quest unblocked --unclaimed` uses one `ls-remote` for `refs/heads/quest/*` | ||
| and one query for open PRs, and prints skipped claims on stderr with their | ||
| age and PR so stale claims can be offered for takeover. | ||
| - The branch check is plain git. Only the open-PR check is GitHub-specific; | ||
| with a non-GitHub origin it is skipped with a warning. kix.quest publishes | ||
| quest branches and PRs, so its jobs count as claims here too. | ||
| - Default the remote to origin, with `--remote` to override. | ||
| - Update `start` and `spawn` skill prose to use these commands. | ||
| - Test claiming free and taken quests, a race between two claimers, an open | ||
| PR without a branch claim, releasing a claim, refusing to release a branch | ||
| with work on it, and the filtered ranking. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # [S] Serialize commands with quest gate --lock | ||
|
|
||
| ## Goal | ||
|
|
||
| `quest gate --lock <name> <cmd>` runs a command while holding a named | ||
| exclusive lock for one user on one host, so a local check-then-merge sequence | ||
| runs one at a time. It replaces the `~/.cache/moq-pro-merge.lock.d` mkdir | ||
| mutex that moq.pro agents invented (79 of 396 sessions). | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): a named exclusive lock rather than a general | ||
| named semaphore with a limit. | ||
| - Hold it until the command and its descendants exit, and release it when | ||
| the holder crashes, using an OS-held lock (or the Linux scope ownership | ||
| from the gate) rather than PID guesses. | ||
| - The lock takes no CPU slot, so gated recipes inside the command still get | ||
| normal admission instead of running as nested calls. | ||
| - While waiting, print the name and the holder's command and start time. | ||
| Names are short tokens; reject anything that could escape the lock | ||
| directory. | ||
| - Build on `src/cli/gate.ts` from [kixelated/quest#128](https://github.com/kixelated/quest/pull/128); | ||
| start after that PR merges. Switching moq.pro to the lock is a follow-up | ||
| in that repository. | ||
| - Data for the gate quests: the audit also counted 1,731 load-average | ||
| polling commands in moq and moq.pro sessions since Oct 14, which the gate | ||
| and the agent lifecycle quest remove. | ||
| - Test contention across processes, a crashed holder, cancellation while | ||
| waiting, exit status propagation, and a gated recipe inside the lock. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Expensive command gate](/quest/m0/local-gate.md) - CPU admission the lock sits beside |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # [M] Move and delete quests with quest mv and quest rm | ||
|
|
||
| ## Goal | ||
|
|
||
| `quest mv <from> <to>` and `quest rm <path>` move or delete a quest or epic | ||
| and keep every root-absolute link in the tree valid. Agents stop following | ||
| `git rm` and `git mv` with `grep -r` and `sed -i` over Required links (203 | ||
| sessions since Oct 14). `quest check` already finds the broken links; these | ||
| commands avoid making them. | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): run `git mv` and `git rm`, so the change is | ||
| staged. Fail outside a git work tree rather than falling back to plain | ||
| file operations. | ||
| - `mv` rewrites every link to the moved path under `quest/`; moving an epic | ||
| directory rewrites links under it as a prefix. Moving a quest into or out | ||
| of an epic changes its structural membership; `quest check` reports what | ||
| the epic's Required still needs rather than `mv` guessing. | ||
| - `rm` drops list items that link to the deleted path (or under a deleted | ||
| epic) and deletes a section left empty, as the guide requires for an empty | ||
| Required. Removing an epic with children requires `-r`, as with `git rm`. | ||
| - Both report prose mentions of the path and mentions outside `quest/` | ||
| without editing them, and run `quest check` on the result. | ||
|
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a deleted quest is referenced by an inline Markdown link in a Goal or Plan, AGENTS.md reference: assets/AGENTS.md:L137-L140 Useful? React with 👍 / 👎. |
||
| - Neither touches branches or PRs, since renaming an open PR's head branch | ||
| closes the PR. `mv` refuses while the quest, or any quest under a moved | ||
| epic, has a claim branch or open PR, because the old branch name would no | ||
| longer be recognized as its claim. Use the same lookup as `quest claim`, | ||
| so whichever lands second reuses it. | ||
| - Put the link rewriting in the core as a pure edit of documents; the CLI | ||
| owns git. Update the `delete` and `audit` skills to use `quest rm` and | ||
| `quest mv`. | ||
| - Test moving a quest, moving an epic, deleting a quest listed under several | ||
| Required and Related sections, refusing a non-empty epic without `-r`, and | ||
| a destination that already exists, and refusing to move a claimed quest. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # [M] Report a PR's state with quest status | ||
|
|
||
| ## Goal | ||
|
|
||
| `quest status <pr>` prints a pull request's current state in one call, plain | ||
| or `--json`, so agents stop chaining `gh api .../reviews|comments|reactions` | ||
| and `reviewThreads` GraphQL queries, often right after `quest wait` returns | ||
| (219 of 396 sessions). It reports facts; it never decides whether the PR is | ||
| ready to merge or whose review counts. | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): report the head SHA, draft and open state, | ||
| failing and pending checks with whether each is required, mergeability, | ||
| auto-merge, and merge-queue state. | ||
| - Decided (user): report each reviewer's latest review marked current or | ||
| stale against the head, and reactions on the PR and its reviews, as | ||
| `quest wait` reports them. | ||
| - Decided (user): report unresolved review threads with their node IDs, | ||
| path, line, last author, and URL, so an agent can reply to or resolve one | ||
| without another query. | ||
| - Accept the same PR argument as `quest wait`: a GitHub PR URL, or a number | ||
| in origin. Use one GraphQL query where it fits (`statusCheckRollup` with | ||
| `isRequired(pullRequestNumber:)`, `reviews`, `reviewThreads`, | ||
| `mergeQueueEntry`, `autoMergeRequest`), paginating long lists. | ||
| - Plain output is stable lines meant for agents; `--json` is a documented, | ||
| deterministic object. Exit 0 for any PR state; non-zero only when the | ||
| command fails, matching `quest ready`. | ||
| - Point `merge`, `iterate`, and `complete` skill prose at it for reading a | ||
| PR's state, especially after `quest wait` wakes. | ||
| - Test with recorded responses: stale and current reviews, a required and an | ||
| optional failing check, a queued PR, resolved and unresolved threads, and | ||
| pagination. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # [M] Wake quest wait on required CI checks | ||
|
|
||
| ## Goal | ||
|
|
||
| `quest wait` also reports the head's required CI results, so one blocking | ||
| wait covers reviews, merges, and CI. The `merge` skill drops its "own | ||
| blocking check watcher", which led 119 of 396 sessions to poll | ||
| `gh pr checks` or write their own `wait-ci.sh`. | ||
|
|
||
| ## Plan | ||
|
|
||
| - Decided 2026-10-19 (user): add events to every wait rather than a separate | ||
| `--checks` mode. `checks:failed` fires at the head's first failing required | ||
| check; `checks:passed` fires once every required check on the head passes. | ||
| Each event names the check and links its details. | ||
| - Decided (user): only required checks wake the wait. Read requiredness from | ||
| GraphQL `isRequired(pullRequestNumber:)` on check runs and status contexts, | ||
| which needs only read access and covers branch protection and rulesets. A | ||
| repository with no required checks treats every check as required. | ||
| Neutral and skipped results pass. Non-required failures never wake; | ||
| `quest status` lists them. | ||
| - Report a check result reached after `--since`, so a wait started after CI | ||
| finished still reports it. A new push starts over for the new head. The | ||
| resume cursor records what was reported, so resuming never replays it. | ||
| - Read checks in the existing poll; keep its interval, rate-limit handling, | ||
| and exit codes. Drop "Check runs ... are not activity" from `--help`. | ||
| - Update the `merge` and `complete` skill prose: one `quest wait` covers | ||
| CI, so delete the separate check watcher. | ||
| - Test a required failure, an optional failure that does not wake, all | ||
| required passing, no required checks, a push mid-wait, and resuming from | ||
| a cursor after a reported result. |
Uh oh!
There was an error while loading. Please reload this page.