What `/submit-for-review` Actually Does (Code Cannon)
A saved prompt that acts like a careful BA: right branch, real checks, a PR that links back to the ticket, review gates, and follow-ups when the review was only mostly clean.
If you’re going to get comfortable with agentic coding, a lot of the work isn’t the model. It’s the guardrails. I want tickets created a certain way, with certain labels, tied to milestones when that matters. I do not want the agent to improvise that every time (or worse, to quietly guess). I want it to behave like it has read the employee handbook.
Different tools call these things different names (skills, commands, rules, saved prompts). At the bottom they’re the same idea: fixed text the agent is supposed to follow. Stitch enough of them together and you get something that behaves less like a clever autocomplete and more like a business analyst who remembers the process: did we open a ticket? Is it in the right sprint? Did a human review the code? Did QA happen?
One piece of that stack, in Code Cannon, is /submit-for-review. The skill lives in the repo as skills/submit-for-review.md. In the workflow it’s “Phase 3”: type-check, commit, open a pull request, run (or skip) review, merge to the right place, then tie up the ticket and optionally spin out follow-ups.
What I wanted from one command
At a high level, the boring checklist I refuse to do manually every time:
- Confirm I’m not sitting on
main(or whatever this repo treats as protected) like an idiot. - Run the project’s real check command (not “whatever sounds right today”).
- Push, open a PR against the branch this project actually uses for integration.
- Review the PR, respect labels and automation, update the linked issue.
- If the review found small stuff but not blockers, offer to file follow-up tickets instead of losing them in a comment thread.
The skill encodes all of that as explicit steps so Cursor, Claude Code, Gemini CLI, or Codex all get the same playbook after sync.py fills in the template tokens from .codecannon.yaml.
Branch and project shape first
Step one is blunt: if you’re on a protected branch ({{BRANCH_PROD}}, and optionally {{BRANCH_DEV}} or {{BRANCH_TEST}} when the project uses them), stop. No commit, no PR, no heroic merge. Switch to a feature branch.
That sounds obvious until you’ve watched an agent try to “help” from the wrong place. Different repos mean different things by “integration.” Code Cannon reads that from config so the agent follows this project’s pattern, not the last repo you had open.
The check gate (and why it refuses to improvise)
Next the skill moves to repo root (git rev-parse --show-toplevel), then verifies the make target exists with make -n before it runs {{CHECK_CMD}}. If the Makefile doesn’t have the target, it stops and tells you to add it or re-run /setup. It does not substitute npm test because that usually works. I’m opinionated here: on repos I control I like a Makefile with a check target, but the skill doesn’t assume that. It assumes whatever you configured, and treats a missing target as a configuration bug, not a cue to freestyle.
If the check fails, that’s the end of the run. Fix the errors, come back. The whole point is to catch the dumb stuff before it hits GitHub.
Issue linking from the branch name
Here’s where I get rigid on purpose. On repos I own, branches created from /start follow feature/<number>-<short-slug>, for example feature/42-fix-login. The skill parses that number and uses it as the linked issue for the PR body and for later steps (QA labels, resolution comments).
If the branch doesn’t match that pattern, it still proceeds, but it warns you that the PR won’t carry an issue reference. That’s the nudge that says “this branch wasn’t born from /start, or someone renamed things.” I’d rather have that explicit than have the agent hallucinate a link.
Merge base, commit, push
Before commit, it merges from the right upstream (origin/{{BRANCH_DEV}} when you have a dev integration branch, otherwise origin/{{BRANCH_PROD}}). Conflicts stop the show with a clear message.
Commit rules are boring on purpose: imperative subject line, meaningful, no secrets or build junk in the message. Then push and open the PR.
PR bodies and a GitHub sharp edge
There’s a whole mandatory dance around gh pr create because of how agent harnesses handle shell quoting. The skill insists on writing pr_body.md with the editor tool, then gh pr create ... --body-file <path>. No inline --body, no heredocs, no $(cat ...). That reads like paranoia until you’ve watched permission prompts explode on a # heading in a one-liner.
It also encodes a GitHub detail I wish I didn’t have to know: closing keywords must use the unqualified #42 form, not Owner/Repo#42. The qualified form can leave closingIssuesReferences empty in the API, which quietly breaks auto-close and anything downstream that trusts that edge. So the skill overrides generic “always use fully qualified refs” advice for the one line in the PR that exists to close a ticket.
Trunk mode vs dev-branch mode changes the magic words: Closes #N when merging to default branch closes the issue; Issue #N when you’re merging to a dev branch keeps the issue open until promotion.
Review, merge, human-shaped ticket updates
{{REVIEW_GATE}} can be off, advisory, or ai (the default). Off skips review and merges after checks. Advisory runs review but merges anyway and makes you own any CRITICAL items in prose. AI mode is the strict one: CRITICAL means no merge; non-blocking findings get listed and you’re asked whether to fix first or merge now.
Merge itself goes through {{MERGE_CMD}} with the same make -n preflight as the check step. No silent fallback to gh pr merge if your Makefile says otherwise.
After a successful merge, if the project has a QA-ready label and the branching model matches, it can add that label to the linked issue. It also posts a short “Resolution” comment aimed at PMs and BAs (outcome language, no file paths), with a pointer to the PR for technical detail. That part matters to me more than it should: the ticket is where non-devs look. The PR is where devs look. The skill forces both to stay in sync.
Follow-up tickets without spam
If review produced [WARNING] or [NOTE] lines and you actually merged, step nine offers to create GitHub issues from those findings. You pick 1,3, all, or none. Each follow-up gets a body template tied back to the merged PR, labels chosen from the same pool as /start (no inventing new labels), no milestone inheritance, no auto-assign to you. It’s a pressure valve for “we shipped but we should track the nits.”
It explicitly does not offer follow-ups when the merge didn’t happen or when review was off. Blockers get fixed, not ticketed out of guilt.
Code Cannon is open source (repo); the value for me isn’t the Markdown file in isolation, it’s that one source of truth syncs to every agent I use. /submit-for-review is the piece that keeps shipping from feeling like improv night.
If you’ve solved the same problem differently (or you think the branch naming convention is too much ceremony), I’m curious how you wired yours.