Full development workflow: pick Linear task, branch, implement, validate, PR with Copilot review, merge, close. Use when starting a new task, creating PRs, or managing the dev cycle.
Installation
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Full development workflow: pick Linear task, branch, implement, validate, PR with Copilot review, merge, close. Use when starting a new task, creating PRs, or managing the dev cycle.
Why .env.workflow and not .env? Next.js auto-loads any file matching .env, .env.local, .env.<mode> into process.env at build and runtime. Our workflow secrets (LINEAR_API_KEY etc.) must NOT leak into the Next.js build, so we keep them in .env.workflow — a name Next.js does not recognise. The Next.js application's own runtime env lives in .env.local. Both are gitignored.
Project override — JourneyAI (Batcave team)
This is the project-scoped copy of the skill. Two deviations from the upstream skill apply for JourneyAI work:
1. Linear ops go through MCP, not curl. Wherever the skill below says linear_query "..." or shows a curl snippet, substitute the matching Linear MCP tool. The LINEAR_API_KEY in .env.workflow is intentionally blank — daily ops do not need it.
Upstream linear_query operation
Use this MCP tool instead
Find issue by number
mcp__claude_ai_Linear__get_issue (pass BAT-NNN)
List issues by filter
mcp__claude_ai_Linear__list_issues
Move issue state
mcp__claude_ai_Linear__save_issue with id + state
Create issue
mcp__claude_ai_Linear__save_issue (no id, requires team + title)
Close issue
mcp__claude_ai_Linear__save_issue with state: "Done"
List statuses
mcp__claude_ai_Linear__list_issue_statuses
Add blocker relation
mcp__claude_ai_Linear__save_issue with blocks / blockedBy
Comment
mcp__claude_ai_Linear__save_comment
2. State pipeline — 4-state default path, Rework only on exception. The Batcave team has 7 workflow states; JourneyAI uses 5 of them and only 4 on the happy path. The default path is In Progress → In Review → Merging → Done (4 states). Rework is a fifth state used only when Copilot or the user requests blocking changes after In Review. Human Review and Todo are skipped entirely. (User preference: parking a Copilot-clean ticket at Human Review just to wait for "merge" is two interrupts for one decision.)
Lifecycle step
Set Linear state to
Trigger
Step 1 — pick task
In Progress (26847ba1-cb6b-4927-a984-849c8d68fa0e)
Task is picked up from Backlog
Step 8 — PR opened
In Review (02974b38-0a8a-43de-97a1-4ce7cf4dcded)
gh pr create succeeds
Step 9c — reviewer requests changes
Rework (8c051101-3922-41ad-999b-43a34585904e)
Net-new findings from Copilot or user that block merge
Step 10 — PR approved, merging
Merging (48a48b66-7df5-42db-9f3e-004bfbab4d20)
User said "merge" AND Copilot's most recent review is clean
Step 10 — merge complete
Done (cf5da06e-ee51-4b60-96c0-4b1bba7d316a)
Branch deleted, worktree cleaned up
"Copilot clean" is defined by Copilot's own most recent review — not by me marking threads resolved as false positives. After every batch (fix OR resolve-as-false-positive), re-request Copilot review and wait. Only stop when Copilot's latest review has zero blocking comments on the current commit. If Copilot re-raises the same finding twice that I judged false-positive, escalate to the user — don't silently stop iterating.
Re-request command (substitute the actual PR number for PR_NUMBER, e.g. gh pr view --json number --jq .number when inside a feature worktree):
PR_NUMBER=$(gh pr view --json number --jq .number)
gh api "repos/$GITHUB_REPO/pulls/$PR_NUMBER/requested_reviewers" \
-X POST -f 'reviewers[]=copilot-pull-request-reviewer[bot]'
The UUIDs above match what's in .env.workflow (STATE_*) — prefer the UUID over the name when calling save_issue so renames don't break the workflow. The unused states (Todo, Human Review) are still in .env.workflow for completeness but are not part of the default flow.
3. Phase ↔ ticket mapping for this project:
Phase
Ticket
0. Repo & scaffold
BAT-647
1. Design system
BAT-648
2. Content data model
BAT-649
3. Layout & shell
BAT-650
4. Home page
BAT-651
5. About page
BAT-652
6. Services pages
BAT-653
7. Who I Work With
BAT-654
8. Contact + form
BAT-655
9. SEO, a11y, polish
BAT-656
9b. Playwright suite
BAT-657
10. GitHub + Railway
BAT-658
When /dev-workflow is invoked without an argument, prefer the lowest-numbered ticket still in Backlog or Todo. Phases are sequential — don't pick up Phase N until Phase N−1 is in Done (or Merging).
Argument Handling
/dev-workflow BAT-42 → look up BAT-42 in Linear, set In Progress, start at step 2
/dev-workflow 42 → look up issue #42 in team $LINEAR_TEAM_PREFIX, same as above
/dev-workflow (no arg) → ask user what to work on, or list backlog tasks
Read the ticket description AND any comments on it (often where reviewers / prior agents leave critical design notes — treat them as part of the spec).
Test plan — which tests will validate which invariants
Skip for trivial changes (single typo, one-line fix, doc-only).
Why this matters: the largest source of review ping-pong is implicit contracts. Each review round reveals a new gap (concurrency, mutation-safety, threading, scope ownership, ...). A 60-second contract block up-front means you address all of these in the FIRST round, not iteratively.
4. Implement
Work the contract. Don't drift. If you find the contract was wrong, update the contract THEN the code — don't quietly diverge.
5. Validate (HARD GATE — before commit)
Rule: do NOT commit unless the narrowest meaningful checks for the files you changed have run and passed. "Tested" claims in the PR body must be backed by a command you actually ran.
Files changed
Required check
*.js (Node side)
git diff --name-only --diff-filter=AM | grep '\.js$' | xargs -I{} node --check "{}" PLUS run any relevant Node test files
*.kt (Android)
./gradlew compileDappStoreDebugKotlin PLUS ./gradlew :app:testDappStoreDebugUnitTest --tests "<targeted class>"
Test files (*Test.kt, *.test.js)
Run those exact tests
New shared abstraction / foundation utility
Run its focused test suite AND at least one integration-style consumer test if one exists
Android UI (*Screen.kt, Compose)
Compile + when practical, install on device/emulator and exercise the path
Docs only (*.md)
Read-through pass; markdown renders cleanly
Always when available
bash scripts/pre-push-check.sh
If a check cannot run (env missing, hardware unavailable, etc.), state the exact reason in the PR body. Do NOT silently skip it and claim "tested."
6. Self-Review Diff (HARD GATE — before commit)
Run git diff --staged (or git diff HEAD). Verify each item:
Docs/comments match the code as it is NOW (not a previous version, not a future plan)
Tests validate BEHAVIOR, not just source text — drift guards (regex over source) are useful but should not be the only thing pinning a contract
No stale references to deleted/renamed symbols (in code, comments, KDoc, tests, docs)
git add <specific-files> # avoid `git add -A`; explicit list only
git commit -m "$(cat <<'EOF'
feat: short description (TASK-ID)
Body — what changed and why. Include the contract bullets from step 3
if relevant. Cite specific commands run and their outcomes for the
test plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
8. Push & Create PR
PR readiness gate — all of these must be true before pushing:
PR body lists ACCURATE test results (commands + outcomes; "could not run" with reason for skipped checks)
Known limitations called out — not hidden
No local TODOs/FIXMEs/blockers undisclosed
git push -u origin "feature/${LINEAR_TEAM_PREFIX}-XXX-short-slug"
gh pr create --title "feat: short description (TASK-ID)" --body "$(cat <<'EOF'
## Summary
- What changed and why
## Implementation contract
(paste the bullets from step 3 if non-trivial)
## Test plan
- [x] <command that ran> — passed
- [ ] <command that did not run> — reason
## Known limitations
- ...
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
9. Copilot Review Cycle
Copilot auto-reviews on PR creation. Never merge without review.
9a. Pre-emptive self-audit (catches ~60% of what Copilot flags)
Run before the FIRST review request on any non-trivial PR:
No "zero", "always", "never", "no I/O", "always atomic" or similar absolutes that have a counter-example. Soften any claim where you can construct ONE counter-example.
No deleted function/field with surviving references in surrounding doc/comments
Field comments accurate to the field's CURRENT usage
Locking/concurrency comments accurate to the current critical-section scope
No bulk-script artifacts ((\), ( ,, double-spaces in changed comment blocks)
AtomicBoolean / Mutex / synchronized usage matches its contract (set-then-check vs check-then-set, finally vs early clear)
Drift: surrounding comment block describes what each changed line of code does NOW
9b. Fetch review threads (use GraphQL, not flat comments)
Flat /comments and /reviews endpoints mix stale + current and don't tell you which threads are still blocking. Use GraphQL reviewThreads so you can filter on isResolved and isOutdated:
Only treat threads where isResolved == false AND isOutdated == false as blockers. Outdated threads (Copilot commented on a line that has since been refactored) and resolved threads (already addressed in a prior commit) MUST NOT keep the PR blocked.
For poll loops watching for new reviews, also filter by submitted_at > push_time on the formal-reviews endpoint — the formal review with comment summary lands ~20-30s AFTER the Agent check-run flips to success, so don't gate on the check-run alone.
9c. Triage protocol (run BEFORE touching code)
The wrong way: address comments one-at-a-time, push per-fix, ping-pong 10+ rounds. Each fix exposes new surface for the next round.
The right way:
Step 1 — Classify every UNRESOLVED + non-OUTDATED comment in the round.
Class
Definition
Action
Correctness/safety
data loss, race, crash, infinite loop, leak
MUST fix in this round
Contract drift
doc claims X but code does Y
MUST fix one of them in this round
Quality
clearer/safer with no current bug
fix if cheap; otherwise note in PR and defer
Nit
typo, doc polish, stylistic
fix only if trivial; otherwise file as a follow-up
Only correctness + contract block merge. Don't treat every comment as must-fix — that's how 12-round PRs happen.
Step 2 — Fix in batches, push once. Address ALL the correctness/contract comments from this round in ONE commit (or one cohesive series). Don't push per-comment.
Step 3 — Drift-check pass after every code change. When you change code, immediately re-read every comment block in the surrounding ~20 lines. If implementation now diverges from doc, fix the doc IN THE SAME COMMIT.
Step 4 — Plan refactors fully BEFORE coding. For non-trivial design changes (locking semantics, single-flight gates, retry policies, mutation safety boundaries), write the contract as a comment FIRST, get the contract right, then implement against it.
Step 5 — Resolve threads as you fix. Mark addressed threads "Resolved" with Fixed in <sha> so reviewers focus on net-new findings.
Step 6 — Same-bug-class sweep BEFORE pushing. Each review finding usually points at one instance of a bug class — other instances exist elsewhere in the diff (or pre-existing in the codebase touched by the diff). Letting Copilot find them one-at-a-time across rounds is the iterative trap. After fixing each finding, sweep the codebase for the same class and bundle every instance into the SAME batch.
For each finding, ask:
Same exact bug elsewhere? Is the bug present in another call site?
New helper / semantic distinction? Did the fix introduce a new helper (e.g. "raw read vs hot-path read") that other call sites must follow?
Changed return semantics? Does a return value now matter that didn't before? Search every call site and prove each handles it or intentionally ignores it.
Changed failure / fallback chain? Does the fix change what happens on missing / malformed / parseable-but-invalid inputs? Test all three input cases.
Changed threading / context assumptions? Does the fix rely on running on Main (or off Main, or under a Mutex)? Verify every other call site obeys the same assumption.
Changed user-facing recovery guidance? If fixing a Toast or Telegram reply changes the recommended action ("retry" vs "restart"), do other strings imply the now-wrong recovery?
Doc / log drift? Did comments, KDoc, log messages, or PR description now imply behaviour that no longer matches the code?
Tests cover the class, not the line? Did you add a test for the specific reviewed line, or for the whole bug class? A test pinned to one call site lets a sibling regression sneak through.
Required sweeps when applicable:
Helper swallowed errors → search every use of that helper. A read/parse helper that returns defaults on failure is correct for a hot-read path but wrong for a "decode cleanly vs missing" decision. Find every caller and check which semantic each needs.
Return value now matters → grep all call sites.Boolean/Result return types added to a previously-Unit API. Each call site is either a logged-and-handled failure or a documented-ignored case; nothing in between.
Fallback chain changed → test all three input shapes. Missing input, malformed input (decode failure), and parseable-but-invalid input (decoded but violates the schema/matrix). Pre-fix builds often only handled one or two; the new build should handle all three.
Background-thread state notification → verify Main-thread mutation. If a bug-fix dispatches Compose state mutation onto Main, verify every other path that mutates the same state is also Main-safe.
User-facing recovery text says "restart" or "retry" → verify the action actually fixes the failure. A failure that's caused by stale-file precedence won't be fixed by a service restart; a failure caused by a transient FS error won't be fixed by retry alone if storage is full. Match the recovery text to the actual failure mode.
Validator exists in two runtimes → verify both runtimes enforce the same matrix. If a Kotlin matrix and a Node matrix are supposed to match, drift between the two is a class of bug that produces "writes succeed but reads silently fall back" surprises.
The sweep cost is a few minutes; the alternative is paying for it in extra Copilot rounds. Note the sweep result in the PR comment with the batch fix ("Same-class sweep: 2 additional instances found and fixed in same commit", or "Same-class sweep: no additional instances").
9d. Re-request review GATE
Before re-requesting Copilot, ALL of:
All current (unresolved + non-outdated) correctness/contract comments addressed in ONE batch
Step 6 same-bug-class sweep complete — every finding has been generalized and additional instances bundled into the same commit (or explicitly noted "no additional instances" in PR comment)
Step 5 validation re-ran and passed AFTER the batch fix
New/modified test files pass
Step 6 diff self-review re-done
Stale threads resolved where possible with Fixed in <sha>
If any gate fails, do NOT re-request review until it does.
gh api repos/$GITHUB_REPO/pulls/PR_NUMBER/requested_reviewers \
-X POST -f 'reviewers[]=copilot-pull-request-reviewer[bot]'
Stop iterating when correctness + contract comments hit zero. Pure quality/nit comments remaining → fine to merge if user explicitly approves. Don't chase zero-comments — chase zero-blockers.
Active polling vs ScheduleWakeup: prefer a tight bash poll loop (~60s interval) for review state instead of ScheduleWakeup 10-min naps. The 60s loop catches Copilot's typical 5-15min turnaround promptly.
10. Merge & Close
HARD GATE — never merge unless ALL of these:
Required code review signed off (Copilot clean OR explicit user approval to merge with remaining quality/nit comments)
Device test passes when device behavior is affected — install the new build on the actual device, exercise the changed feature + adjacent surfaces, confirm it works. CI green is necessary but NOT sufficient. Compile passing is necessary but NOT sufficient.
User explicit approval — the human says "merge" or equivalent. Don't infer approval from "looks good" or "yes" in a different context. Ask if unsure.
CI green — or failures explicitly unrelated and user-approved.
If any condition isn't met, do NOT merge. Wait. The cost of waiting is small; the cost of merging a broken build is shipped-to-real-users.