General-purpose coding policy for Baruch's AI agents
74
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Medium
Suggest reviewing before use
When the agent's interactive Step-2 (PR create) and Step-7 (merge + cleanup) instructions get encoded into a reusable script that other devs run unattended (release.sh, merge-and-cleanup.sh, etc.), the script has to carry the SAME gates the interactive agent does — otherwise the script bypasses the conventions this skill exists to hold. Pulled out of SKILL.md to keep the main workflow scannable.
The script MUST enforce all of:
<type>(<scope>): <imperative summary>. Taking the title as a raw argument and passing it straight to gh pr create defeats the convention; either build the title from <type>, <scope>, <summary> inputs, or regex-validate the supplied title before push.The script MUST enforce all of:
skills/release/watch-pr-reviews.sh <owner> <repo> <pr-number> (script-owned interval/budget; emits .watch.result — ready on exit 0, changes_requested / ci_failure / dirty on exit 0, review_unrequested / pending_at_budget on exit 1 — review_unrequested means no Copilot review, no pending request and no run in flight at this head, so run skills/release/request-copilot-review.sh <owner> <repo> <pr-number> and re-run the watch). Never hand-roll the poll loop or wrap the watch in an invented timeout. Then re-check the same conditions the interactive agent checks at merge time: CI status is success (or none), the policy review carries no unresolved blocking finding under Step 7 (including a covered RULED dismissal); Copilot is advisory, every review body and inline comment has been surfaced for full reading and any blocking finding has been addressed. Ordinary advisory replies and thread resolution are outside the merge predicate. Fail loudly if any gate is red instead of proceeding.review_bodies[].body to an agent or human reader, from every author and page, including historical and dismissed reviews. Resume after full reading and resolution of blocking findings through the existing correction route. The bot-specific reviews fields route verdicts and never bound reading. The release agent may be the reader; no separate human approval is required. A wrapper cannot judge substance from state or displayed text (rules/script-delegation.md, rules/reviewer-feedback-reading.md).inline_comment_bodies[].body to an agent or human reader, including replies and advisory comments. Resume after every body has been read in full and every blocking finding addressed through the existing correction route. The agent running the release may be the reader; separate human approval is not required. Herdr rounds use the source-change handoff in skills/herdr-foreman/templates/brief-release.md, never source repair by the release worker. Advisories never block delivery after reading. Comment counts, review states, and merely displaying the text cannot satisfy this gate.git branch -d <branch> (refuses to drop unmerged work), never git branch -D. The whole point of the cleanup is "ship and tidy"; clobbering an unmerged branch with -D defeats the safety the merge gate just established.rules/agent-worktree-isolation.md), it must detect that case and run the worktree variant of cleanup: cd to the base checkout, fast-forward main, git worktree remove <worktree-root>, then git branch -d <branch>. Three derivations the wrapper needs:
[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]; true means you're in an additional worktreegit rev-parse --show-toplevel (required by git worktree remove, which refuses subdirectory paths with not a working tree)git worktree list --porcelain | awk '/^worktree / {print $2; exit}' (the first worktree entry is always the main checkout)
Skipping this branch makes git branch -d fail with a confusing "checked out at <path>" error and leaves the worktree directory plus .git/worktrees/ metadata stranded; the script must surface a coherent teardown, not that error.tessl plugin publish / tesslio/patch-version-publish publish path; a tag-triggered workflow creating a GitHub release) and runs each channel's steps below. A package on both channels runs both sets, neither standing in for the other. See rules/ci-safety.md Always Watch CI.gh pr merge, capture the registry's Latest Version as a baseline (tessl plugin info <workspace>/<plugin>). The baseline feeds the conjunction check below. A tag/asset-only publication skips it.main actually advanced to the merge commit before any further verification step runs.gh pr view <N> --json mergeCommit --jq '.mergeCommit.oid' (NOT git log -1 on main), then skills/release/resolve-publish-run.sh <owner> <repo> <merge-sha> <workflow-name>. For a tag-triggered publish, push the tag and pass it as the fifth argument alongside the commit it points at: skills/release/resolve-publish-run.sh <owner> <repo> <tag-sha> <workflow-name> <tag>. The resolver's contract is referenced once, at its invocation site in skills/release/PUBLICATION.md. A wrapper covering both channels holds one run id per channel and never overwrites one with the other. Never select by "latest on main" or any branch-only/limit-1 heuristic.jq -r '.database_id' and gh run watch <id> on that exact run. Never reduce this to "check it triggered".skills/release/verify-publish-landed.sh <workspace> <plugin> <pre-baseline> <run-id> exits 0 iff the resolved run's conclusion is success AND the registry's Latest Version is strictly greater than the pre-merge baseline. The script must NOT bypass the conjunction script's exit code (e.g., by parsing current > pre alone).pass via skills/release/verify-moderation-cleared.sh <workspace> <plugin> <version> (exponential backoff, fail loud at budget — the script owns the backoff constants and the cleared/blocked decision). A still-pending or blocked state at budget exhaustion exits non-zero; the wrapper must NOT report the release confirmed. Query the real moderation state from the registry — never fabricate one as a hedge for a failed publish.skills/release/verify-github-release.sh <owner> <repo> <tag> <run-id> exits 0 iff the resolved run's conclusion is success AND the release exists at that exact tag, is published, and every asset is retrievable. The wrapper must NOT bypass its exit code, and runs none of the three Tessl steps for this channel.skills/release/stamp-changelog.py runs in the publish workflow (.github/workflows/publish.yml), not from any SKILL.md step — the agent never invokes it. It stamps the publish-on-merge CHANGELOG so un-headed ### entries get a ## <version> — <date> heading matching the version being published.
Contract:
--changelog (default CHANGELOG.md), --manifest (default .tessl-plugin/plugin.json, then tile.json), --latest (skip the registry query; CI omits it and reads the registry through skills/release/registry-version.sh, which needs tessl and jq on PATH — exit and output contract in that script's header), --date (default today, UTC)commit-stamp.sh) with the CI-skip marker, decoupled from the publish step's exit (#284)## headingregistry-version.sh exitcompute_version / stamp_changelog — do not restate it hereThe workflow step must run BEFORE the publish step (smart-publish) so the stamped heading and the assigned version stay in lockstep.
Before stamping, the stamp-changelog action runs skills/release/check-changelog-placement.py --since-last-publish and fails the step on a non-zero exit, so a push that parks a new entry under a published heading stops before it publishes, and stays stopped until a follow-up moves the entry. The action forwards its manifest: input verbatim as --manifest. Rewording a published entry in place needs a Changelog-Edit: <version> commit trailer. The script writes one JSON verdict to stdout on every outcome and its diagnostics to stderr; the callers gate on the exit code. Its modes, decision contract and exit codes live in that script's module docstring; the PR-time form (--base) runs in .github/workflows/tests.yml.
The publish workflow above is a thin caller of the canonical reusable pipeline (.github/workflows/publish-plugin.yml). Wiring a new plugin repo onto that pipeline — or migrating one off a bespoke tesslio/patch-version-publish workflow — is a documented maintainer step, not part of the release flow. The caller template, the input guide, the Dependabot pin renewal, and the migration procedure live in the reference doc (repo-internal; docs/ is .tesslignored, so read it from the repo, not an install):
docs/fleet-publish-setup.mdA scripted run is unattended. Anything the interactive agent enforces by reading SKILL.md only protects the sessions where the SKILL.md is in the agent's context. A script that doesn't carry the same gates internally hands every consumer a sharper version of the bypass-by-automation problem.
.tessl-plugin
hooks
rules
skills
adopt-fork-pr
herdr-foreman
classify
foreman
references
specialists
templates
tests
herdr-standup
migrate-to-plugin
onboard-repo
release
references
tests