| name | bifrost-issues |
| description | Track work on Bifrost via GitHub Issues + isolated worktrees, AND own the PR/merge lifecycle (opening, queuing auto-merge, watching CI + reviews). Use when the user expresses work intent ("let's build/fix/add X", "work on Y"), pastes a list of todos/notes to triage, asks about existing issues, OR you (or the user) are about to open a PR, queue auto-merge, or merge a PR on this repo. `main` uses GitHub's native merge queue — `gh pr merge <N>` (no strategy flag) enqueues the PR; the queue rebases, runs checks once on a combined ref, and merges when green. Invoke this skill BEFORE running `gh pr create` or `gh pr merge`, not after. Light-touch on issue/work-intent triggers — nudges and helps, never blocks. Trigger phrases - "let's build", "let's fix", "work on", "add a feature", "triage", "todo", "what should I work on", "open a PR", "create an issue", "merge this PR", "queue auto-merge", "ship it", "help wanted". |
Bifrost Issues + Worktrees
All trackable work on gobifrost/bifrost lives in GitHub Issues, and all non-trivial work happens in isolated git worktrees under .worktrees/. This skill owns both halves: issue creation/triage and worktree setup/teardown. One skill because the triggers are identical — the moment the user expresses work intent on a non-trivial change, both pipelines fire.
Core Principles
- Light-touch, never block. Nudge once, respect the answer. If the user skips, proceed without guilt.
- Show drafts before filing. Never call
gh issue create without showing the drafted body first.
- GitHub conventions only. Use
assignee, help wanted, good first issue — not custom status/priority/milestone labels.
- Trivial edits don't need issues or worktrees. See the trivial-edit definition below.
- Worktrees are the default for non-trivial work. Working directly on
main is a smell — the user keeps main clean to pull updates without conflicting with in-progress work.
- Debug stacks are per-worktree. Each worktree gets an isolated
./debug.sh stack (Compose project derived from the worktree path). See the bifrost-debug skill for boot/teardown.
When to Activate
Activate on any of these:
- User expresses work intent: "let's build/add/fix X", "I want to X", "work on X", "implement X"
- User pastes a list of todos, notes, or ideas for triage
- User is about to open a PR (proactively check for issue linkage and worktree isolation)
- User asks about existing issues or what to work on next
- User wants to label an issue as
help wanted or good first issue
- User asks "what now" or "is it merged" on a PR they just opened, or asks to wait/watch CI on their own PR — see step 7 below; this skill owns the open-to-merge handoff
Do not activate for:
- Trivial edits (see below)
- Pure questions about code/architecture with no intent to change anything
- Debugging sessions where the scope isn't yet clear (wait until it is)
The Trivial-Edit Exception
A change is trivial and does not need an issue or worktree if it meets any of these:
- Typo fix
- Comment-only change
- Formatting or whitespace-only change
- ≤3-line edit to a single file with no behavior change
- Rename that doesn't cross a public API boundary
Anything else — multi-file, multi-line, new behavior, test changes, migrations, dependency bumps — is non-trivial and triggers the full flow.
Flow: From Work Intent to PR
1. Issue first
Before any code, before any worktree, get an issue number.
Search for existing issues:
gh issue list --search "<2-3 key terms>" --state all --limit 5
If a plausible match exists, surface it: "This looks related to #N — is that the same thing, or a new one?"
If none, ask once: "Is there an issue for this? I can create one, or skip if it's trivial."
When drafting an issue body:
- Pick the template (
bug, feature, chore) from the work description.
- Fill every field from conversation context; ask the user only for gaps.
- Show the draft (title + body + labels + assignee) before filing.
- On approval, file via
gh issue create --title "..." --body "..." --label "..." --assignee "@me" (self-assign only if the user is doing the work).
2. Worktree setup
Once the issue exists:
CRITICAL: Use absolute paths in every Bash call. The Bash tool's CWD leaks between calls — once you cd somewhere (e.g. for npm ci), subsequent calls silently inherit that CWD. Always write the full worktree path explicitly. Don't trust cd <worktree> to stick.
git fetch origin main
git log --oneline main..origin/main
git worktree add -b <issue-num>-<short-slug> \
/home/jack/GitHub/bifrost/.worktrees/<issue-num>-<short-slug> origin/main
cd /home/jack/GitHub/bifrost/.worktrees/<slug>/client && npm ci
Conventions:
- Branch name and worktree dir use
<issue-num>-<short-slug>. Slug is hyphenated, ≤40 chars.
.worktrees/ is already gitignored in this repo — verify once, warn if not.
- Base from
origin/main, not local main (local can be stale; origin is the shared truth).
- Do not copy
.env / .env.test files — ./test.sh and ./debug.sh find them on their own. Copying creates drift between worktrees and is a footgun.
3. Migration-drift check
Migrations are run by the bifrost-init container against a single Postgres shared across worktrees. Before starting work in the new worktree:
git diff --stat origin/main -- api/alembic/versions/
Do NOT compare migration folders with ls | tail — a __pycache__ directory in one but not the other silently skews the result. Use git diff --stat or ls api/alembic/versions/*.py.
If main has migrations the branch lacks, warn: "main has migrations ahead of this branch — merge or rebase before running the stack, otherwise the DB schema won't match your code." Don't auto-rebase.
4. Dev stack coordination
Debug stacks are per-worktree — ./debug.sh derives its Compose project name from the worktree path, so two worktrees running their own stacks don't conflict. See the bifrost-debug skill for the lifecycle (boot, status, teardown, two-mode behavior).
Most worktrees don't need a debug stack. The test stack (./test.sh stack up) is already per-worktree and is enough for unit/E2E coverage. Type generation can extract OpenAPI from the test-stack's API container. Only invoke the bifrost-debug skill when you actually need to click around the UI in this worktree.
5. Doing the work
- Make the change in the worktree, not in main.
- Use
./test.sh stack up once per worktree, then ./test.sh many times.
- Run
./test.sh, pyright, ruff, npm run tsc, npm run lint, ./test.sh client unit before claiming done (CLAUDE.md's verification checklist).
pyright/ruff require a repo-root .venv: python -m venv .venv && ./.venv/bin/pip install -r requirements.txt pyright ruff (matches .github/workflows/ci.yml).
5.5. CI bottleneck reducers before PR
Run cheap, targeted tripwires for the surfaces you touched before opening the PR. These catch the common "CI found the stale generated thing" loop locally.
| If you touched... | Run before PR |
|---|
| CLI help, entity commands, DTO flags, or SDK-facing command surface | ./test.sh tests/unit/test_cli_surface_smoke.py tests/unit/test_skill_appendix_fresh.py |
| DTO models or CLI/MCP mutation flags | ./test.sh tests/unit/test_dto_flags.py tests/unit/test_contract_version.py |
Bifrost skills under .claude/skills/ | bash scripts/sync-codex-skills.sh, then ./test.sh tests/unit/test_skill_appendix_fresh.py tests/unit/test_codex_mirror_sync.py |
Public plugin skills under plugins/bifrost/skills/ | Confirm canonical .claude/skills/ and plugin mirror match (diff -qr ...) and that public skill names do not repeat the plugin namespace |
If test_skill_appendix_fresh.py fails, run the generator before pushing:
python api/scripts/skill-truth/generate.py
If host Python is missing API dependencies, run the generator in the API test image with writable .claude/skills and read-only source mounts. Do not hand-edit generated appendices.
6. PR linkage
When opening the PR:
- Scan for issue numbers in: branch name, commit messages, conversation. The branch-name convention
<issue-num>-<slug> should yield one automatically.
- Ensure the PR body includes
Fixes #N (use Closes #N for non-bug issues; GitHub auto-closes the issue on merge either way).
- If multiple issues are addressed, one
Fixes #N line per issue.
7. Hand off to merge (auto when safe, else watch CI)
The PR isn't done at "opened." Carry it through to merged. The path depends on whether main has branch protection with required status checks — --auto is only safe when GitHub will refuse to merge until checks pass.
Capability check (do this once, immediately after opening the PR):
gh api repos/gobifrost/bifrost/branches/main/protection \
--jq '.required_status_checks.contexts // [] | length' 2>/dev/null
If the count is ≥1, --auto is safe. Otherwise it isn't (a --auto merge would fire the moment GitHub considers the PR mergeable, which without required checks is immediately).
Watch reviews and CI together — gh pr checks alone will miss code review comments.
Once a PR is open, three independent signals can come in: CI check transitions, code-review comments (CodeQL bot, human reviewers, copilot/Anthropic), and merge-state changes. The watcher must cover all three. Don't just tail -f gh pr checks — that silently misses CodeQL findings until they're already old, and an auto-merge queued PR can sit forever blocked on an "actionable" review you never noticed.
Combined watcher pattern (use this, not a checks-only loop):
prev=""
prev_c=""
while true; do
s=$(gh pr view <N> --repo gobifrost/bifrost \
--json reviews,statusCheckRollup,reviewDecision,mergeStateStatus,state 2>/dev/null) || { sleep 60; continue; }
c=$(gh api repos/gobifrost/bifrost/pulls/<N>/comments --jq '.[] | "\(.user.login):\(.id):\(.path):\(.line):\(.body|gsub("\n";" ")|.[0:100])"' 2>/dev/null | sort)
cur=$(printf '%s\n%s' "$s" "$c" | sha256sum | cut -d' ' -f1)
if [ "$cur" != "$prev" ]; then
echo "=== $(date -u +%H:%M:%S) PR <N> update ==="
jq -r '" reviewDecision: \(.reviewDecision // "(none)")\n mergeStateStatus: \(.mergeStateStatus)\n state: \(.state)\nchecks:", (.statusCheckRollup[] | " \(.name // .context // "?"): \(.conclusion // .state // "queued")"), "reviews:", (.reviews[] | " \(.author.login) \(.state)")' <<<"$s"
if [ -n "$prev_c" ] && [ "$c" != "$prev_c" ]; then
echo "NEW review comments:"
diff <(echo "$prev_c") <(echo "$c") | grep '^>' | head -10
fi
prev_c=$c
prev=$cur
fi
st=$(jq -r '.state' <<<"$s")
case "$st" in MERGED|CLOSED) echo "PR is $st"; break ;; esac
sleep 60
done
When a review comment lands (CodeQL, copilot, human): pull the body and address it before merging. CodeQL findings on this repo's pre-push hook are usually real — see feedback_codeql_friendly_idioms.md in memory. Never queue --auto and walk away from a fresh review.
When a CI job fails: fetch the failing job log immediately instead of waiting for the whole workflow run to finish. gh run view --log-failed may refuse while sibling jobs are still running; the job logs endpoint often works sooner:
gh pr checks <N> --repo gobifrost/bifrost
gh pr view <N> --repo gobifrost/bifrost \
--json statusCheckRollup \
--jq '.statusCheckRollup[] | select((.conclusion // "") == "FAILURE") | {name,workflowName,detailsUrl}'
gh api repos/gobifrost/bifrost/actions/jobs/<job_id>/logs
Fix CI failures in the same worktree and branch. Prefer a normal follow-up commit once reviewers or other agents may have seen the PR; amending with --force-with-lease is acceptable for a fresh, unreviewed PR where you are the only actor. After any force-push, re-check whether auto-merge/queue state survived.
Path A — protection exists (preferred for ship-when-green):
- Confirm with the user once ("Queue it through the merge queue so it ships when CI is green?").
- On approval:
gh pr merge <N> --repo gobifrost/bifrost (NO strategy flag — the queue dictates squash; passing --squash is rejected with "The merge strategy for main is set by the merge queue"). Enqueuing IS the auto-merge-when-green: the queue owns the rebase, runs checks once on a combined ref, and merges when green. (--auto is accepted if you want gh to wait for the PR to be mergeable before enqueuing, but NOTE the queue is the actual gating mechanism — you don't need it.)
- Arm the combined watcher above. Enqueuing does not exempt you from picking up review comments — CodeQL still fires after queueing, and the queue will not merge past a
BLOCKED/DIRTY mergeStateStatus.
- If a review is required (
required_pull_request_reviews is set) and the user isn't admin, surface that the merge is gated on approval and offer to request reviewers via gh pr edit <N> --add-reviewer <login>. Don't pick reviewers unprompted.
Path B — no protection (fallback):
- Arm the combined watcher above.
- If CI fails or a review comment lands: stay in the existing worktree on the existing branch — fixes are additional commits to
<issue-num>-<slug>, not a fresh worktree. The PR auto-updates on push. New worktree only if the branch is being abandoned entirely.
- If CI passes and reviews are clean: check
gh pr view <N> --json viewerCanAdminister,reviewDecision,mergeStateStatus to decide.
viewerCanAdminister: true → self-merge: confirm once ("CI's green and reviews are clean — merge?"), then gh pr merge <N> --squash --delete-branch=false.
- Otherwise → contributor needs review: surface reviewer-request as in Path A step 4.
- Never
--auto on Path B. Without required checks, it would fire before CI completes.
Both paths:
- Don't merge unprompted. "Merge?" or "queue auto-merge?" is the user's decision — confirm, never assume.
- If CI is already failing on the PR when you reach this step, surface the failure and don't offer merge until it's fixed.
- Review comments are not optional. A
gh pr checks watcher that doesn't also poll reviews/comments will leave the user thinking nothing is happening while CodeQL has been waiting on you for 20 minutes.
- After a force-push,
autoMergeRequest may be null even if the issue event log says added_to_merge_queue. Check both gh pr view ... --json autoMergeRequest,mergeStateStatus,statusCheckRollup and recent issue events before repeatedly re-running gh pr merge.
- When required checks are all green and
mergeStateStatus is CLEAN, a queued PR can still take several minutes to flip to MERGED. Confirm added_to_merge_queue via gh api repos/gobifrost/bifrost/issues/<N>/events; then keep the watcher attached rather than thrashing the queue.
8. Cleanup (after merge)
After the PR is merged, offer (do not run unprompted):
git worktree remove .worktrees/<issue-num>-<slug>
git branch -d <issue-num>-<slug>
git push origin --delete <issue-num>-<slug>
Then cd back to the main checkout (/home/jack/GitHub/bifrost) so subsequent commands target main and not a stale worktree path.
Behavior: Batch Triage
When the user pastes a list of todos, notes, or ideas:
- Parse and sort each item into
bug, feature (→ enhancement label), chore, or idea.
- Draft each as an issue with a good title and a template-shaped body. For
idea items too vague to fit feature.yml, use a blank issue with the idea label.
- Present the batch grouped by type:
## Bugs (3)
1. [title] — [one-line summary]
2. ...
## Features (2)
...
- Ask before filing: "File all of these? Any to drop or edit? Assign yourself to any?"
- On approval, file each via
gh issue create. Print the resulting issue numbers.
Do not auto-create worktrees for batch-triaged issues — the user is cataloging, not starting work.
Behavior: Label & Assignment Management
On user request:
- Mark as takeable:
gh issue edit N --add-label "help wanted"
- Mark as beginner-friendly:
gh issue edit N --add-label "good first issue"
- Self-assign:
gh issue edit N --add-assignee "@me"
- Assign to another contributor:
gh issue edit N --add-assignee <login>
Do not add priority labels, milestones, or status labels.
Escape Hatches
- User says "skip issue", "just do it", "no issue needed" → step aside for that request.
- User says "work in main", "don't worktree this" → respect, but warn once about the
main conflict risk.
gh not authenticated → surface the error and offer to proceed without an issue. Don't block the real work.
- User indicates the change is actually trivial after you prompted → drop the nudge immediately.
The gh issue create Pattern
gh issue create \
--title "[bug]: <summary>" \
--body "$(cat <<'EOF'
## Summary
...
## Steps to reproduce
...
## Expected behavior
...
## Actual behavior
...
## Environment
...
## Notes
- File paths and line numbers where relevant
- Proposed approach if known
EOF
)" \
--label "bug" \
--assignee "@me"
Mirror the relevant template in .github/ISSUE_TEMPLATE/. Use [bug]:, [feature]:, or [chore]: title prefixes.
What This Skill Does NOT Do
- Block any action. All nudges are cancellable.
- File issues without showing the draft first.
- Auto-rebase / auto-merge migration drift — only warns.
- Auto-stop a
./debug.sh in another worktree — user controls that manually.
- Manage milestones, projects, priority, or status labels.
- Touch closed issues (reopening is a human decision).
- Work across repos — scoped to
gobifrost/bifrost.