CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/coding-policy

General-purpose coding policy for Baruch's AI agents

95

1.31x
Quality

91%

Does it follow best practices?

Impact

96%

1.31x

Average score across 10 eval scenarios

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

SKILL.mdskills/release/

name:
release
description:
Structured workflow for shipping code via GitHub pull requests: PR creation, dual-lens automated review (gh-aw for `rules/*.md` compliance + Copilot for doc accuracy and cross-step consistency), merge, and branch cleanup. Covers readiness checks, version reasoning, review polling, feedback handling, and post-merge verification. Use when the user wants to open a pull request, ship code, merge a branch, or handle post-merge cleanup on GitHub.

Release Skill

Structured workflow for shipping code: PR creation, automated policy review, merge, and cleanup. Process each step in order — do not skip ahead, and do not stop between steps; the skill runs end-to-end from git push through merge + cleanup verification in a single agent session.

Step 1 — Verify Readiness

  • Confirm you're on a feature branch (not main/master)
  • Run the test suite — all tests must pass
  • Run the linter — no warnings or errors
  • If anything fails, fix it before proceeding

Step 2 — Create PR

  • Push the branch: git push -u origin <branch>
  • Create the PR with gh pr create:
    • Title: <type>(<scope>): <imperative summary>
    • Body:
      **Author-Model:** <model-id(s) space-separated, or `human`>
      
      ## Summary
      <what changed and why — 1-3 bullet points>
      
      ## Test plan
      - [ ] <verification steps>
  • Author-Model is mandatory per rules/author-model-declaration.md. Use the exact model ID you're running under (e.g., claude-opus-4-7, gpt-5.4), human for a hand-authored PR, or every contributing model space-separated for mixed authorship.

When this step is wrapped in a reusable script (e.g., release.sh that other devs run unattended), see the script-wrapping gates at:

skills/release/SCRIPTING.md

Proceed immediately to Step 3.

Step 3 — Reason About Versioning

Decide the bump per semver. Patch is the default and is handled automatically by tesslio/patch-version-publish — only update the manifest version for minor or major.

Step 4 — Policy Review Fires Automatically

Opening the PR, or pushing further commits to an existing PR, automatically triggers the paired gh-aw policy reviewers (OpenAI + Anthropic, cross-family by author-model). The workflows are bound to the pull_request event (opened / synchronize / reopened / edited); a plain git push to a non-PR branch does NOT fire them. See the trigger / self-gating / authorship mechanics at:

skills/release/GH_AW_DETAILS.md

Also request Copilot. Copilot is a deliberate second reviewer with a different lens — gh-aw enforces rules/*.md compliance, Copilot reads for doc accuracy, cross-step consistency, and ambiguity. Both reviewers gate the merge:

skills/release/request-copilot-review.sh <owner> <repo> <pr-number>

Proceed immediately to Step 5.

Step 5 — Poll PR State

Capture a single JSON snapshot of CI status, bot review states, and inline comment counts:

skills/release/poll-pr-reviews.sh <owner> <repo> <pr-number>

The script returns:

  • ci.statuspending | success | failure | none (the gh-aw workflow appears here as a check once it has run)
  • reviews.gh_aw.state and reviews.copilot.state — latest review per bot (APPROVED | CHANGES_REQUESTED | COMMENTED | none)
  • inline_comments.gh_aw and inline_comments.copilot — top-level inline comment counts

Loop until ci.status is success (or none if no checks are configured) and no bot has CHANGES_REQUESTED. COMMENTED does NOT block the polling loop — exit it and proceed to Step 6. (COMMENTED with inline comments still requires reply-per-thread before the Step 7 merge gate, but that's a separate condition the operator confirms at merge time, not a poll-loop exit criterion.) If the gh-aw review check ran but no review was posted, inspect logs with gh run view --log-failed. Do not retry via GraphQL — gh-aw is event-triggered, not request-triggered.

Step 6 — Address Feedback; No Re-request Needed

  • CI failures: Fix every one, no exceptions
  • Review suggestions: Apply what's right and reasonable. Push back on anything that misreads scope or over-engineers — but cite concrete evidence (file:line, log line, spec quote) when declining; never hand-wave
  • Reply on EVERY thread — nothing left dangling. Use these exact opening literals so threads scan consistently:
    • Accepted: Fixed in <sha> (literal phrase; semantically-equivalent variants like Done or Accepted and fixed do not satisfy)
    • Declined: Declining — <reason with cited evidence> (em dash , not a hyphen or period)
  • Push fixes to the same branch
  • gh-aw re-runs automatically on every push (pull_request: synchronize); Copilot needs a manual re-request each push via skills/release/request-copilot-review.sh (same args as Step 4).
  • Repeat Step 5 until every active bot review is APPROVED or COMMENTED with no blocking items, and every thread has a reply.

Step 7 — Merge + Cleanup

Only proceed when:

  • Step 5's poll returns ci.status as success (or none if no checks are configured) AND no bot has CHANGES_REQUESTED, AND
  • Both reviews.gh_aw.state and reviews.copilot.state are NOT none — i.e., each gating reviewer has actually posted a review. A reviewer that hasn't run yet leaves state: none and inline_comments: 0, which would otherwise satisfy the no-CHANGES_REQUESTED check vacuously, AND
  • Every inline comment from Step 5's inline_comments count has a Fixed in <sha> or Declining — <reason> reply per Step 6 (verify by listing the PR's review comments — the poll script tracks counts, not reply state, so the operator confirms thread closure).

A COMMENTED review with zero inline comments is fully non-blocking; a COMMENTED review with inline comments is non-blocking once every thread has a reply.

Pick the right cleanup path based on where you ran the skill from.

(A) From the base checkout (no additional worktree):

# Merge
gh pr merge <N> --merge --delete-branch

# Update local
git checkout main && git pull --ff-only

# Clean up local branch
git branch -d <branch>

# Prune stale remote refs
git remote prune origin

(B) From an additional worktree (per rules/agent-worktree-isolation.md):

# Merge (running from inside the worktree is fine)
gh pr merge <N> --merge --delete-branch

# Return to the base checkout, fast-forward main
cd <path-to-base-checkout>
git checkout main && git pull --ff-only

# Tear down the worktree — directory + `.git/worktrees/` metadata
git worktree remove <path-to-worktree>

# Now safe: branch is no longer checked out anywhere and is fully merged
git branch -d <branch>

# Prune stale remote refs
git remote prune origin

Order in (B) is mandatory: git branch -d refuses to delete a branch that is checked out in any worktree, so the git worktree remove step must come before git branch -d. Reversing the order produces a "checked out at <path>" error and leaves a stranded branch.

After merge — per rules/ci-safety.md's Always Watch CI duty extended through release:

  • Verify the merge landed on main (git pull --ff-only succeeds; git log -1 --oneline shows the merge commit)
  • Watch the publish workflow to a terminal state — not just "check it triggered". Bind the run to the merge commit SHA, never to "latest on main": after the fast-forward pull, capture merge_sha=$(git log -1 --format=%H), then resolve the run for that exact commit with gh run list --branch main --workflow "<publish-workflow-name>" --json databaseId,headSha --jq '.[] | select(.headSha == "'"$merge_sha"'") | .databaseId' | head -n 1, then gh run watch <run-id>. --limit 1 alone is race-prone: if another merge lands on main between your merge and the watch, it picks the wrong run. The watch is a timing precondition for the next bullet's registry check (so we don't query mid-flight), not an authoritative gate — workflow exit code is approximate (workflows can succeed without running the publish step under some conditions, or fail in post-publish cleanup); the actual gate is registry-advanced
  • Confirm the registry advanced — capture PRE=$(tessl tile info <workspace>/<tile> | grep "Latest Version" | awk '{print $NF}') BEFORE the merge, then after the watch returns, run the same query again and confirm the new value is greater than PRE. Registry-advanced is the authoritative signal the publish landed (registry-not-advanced = it didn't, regardless of the workflow's exit code). Do not compare against a specific expected version (interleaved merges in busy repos may advance past yours anyway). See rules/ci-safety.md's "Always Watch CI" extension for the full framing — Tessl registry never rejects, moderationPassed: false means won't-surface-in-search, security finding means install-flag-required, neither is rejection. Naively re-running a failed publish can create an extra release when the workflow includes a version-bump step (as this tile's publish.yml does via tesslio/patch-version-publish); the safer recovery is a follow-up commit that fires a fresh publish on merge.
  • Report the outcome: merged PR URL, version published, registry confirmation

When this step is wrapped in a reusable script (e.g., merge-and-cleanup.sh that other devs run unattended), see skills/release/SCRIPTING.md for the gates the script must enforce.

Finish here — the skill is complete.

README.md

tile.json