CtrlK
BlogDocsLog inGet started
Tessl Logo

worktrail-repo-init

Bootstrap a new repo, or migrate an existing single-branch repo, onto the workspace's repo-standards doctrine (~/rules/CLAUDE.repo.md): the AGENTS.md-is-truth/CLAUDE.md-imports-it split, a dev/prd, dev/stg/prd, or main-only (trunk) branch model with matching GitHub rulesets, an OpenSpec scaffold, an auto-merge workflow, and a seeded .worktrail/policy.yaml. Trigger phrases: "onboard this repo", "apply repo standards", "initialize repo standards", "set up this repo like the rest of the fleet", "bring this repo into line with the repo standards doctrine".

69

Quality

86%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Repo Init Skill

Overview

Applies the workspace's repo-standards doctrine to one repo at a time: the CLAUDE.md → @AGENTS.md split, a branch model with matching .github/rulesets/*.json, an OpenSpec scaffold, .github/workflows/worktrail-auto-merge.yml, .github/workflows/rulesets_drift_guard.yml (plus its vendored scripts/ci/rulesets/rulesets_sync.py + requirements.txt), .github/workflows/dependabot_manifest_check.yml (plus its vendored scripts/ci/dependabot/test_dependabot_config.py + requirements.txt), docs/engineering/pull-requests.md (agent-facing go:risk-* label instructions, linked from AGENTS.md), and a seeded .worktrail/policy.yaml. The CLI (worktrail-repo-init) owns file generation and the GitHub API calls; this skill owns the git workflow around it (worktree, commit, PR) and the judgment calls the CLI deliberately leaves to a human — see references/branch-model-decision.md.

When to Use

Any repo under ~/projects/ that doesn't yet follow the doctrine: no AGENTS.md, no .github/rulesets/, single-branch (main/master only, no dev). Works for both a brand-new repo and an existing repo being migrated — propose only writes files that don't already exist, so re-running it on a partially-onboarded repo is safe.

Instructions

Step 1 — Decide the branch model

Read references/branch-model-decision.md before choosing --branch-model. The short version: 2 (dev/prd) is the default — use 3 (dev/stg/prd) only when the repo has a real, distinct pre-production deployed environment to gate against (matches datalena's pattern, enforced by its own promotion-target-guard CI check). A repo with no deployment, or one whose CI/CD already provides an equivalent gate (e.g. Vercel preview deployments per-PR), stays at 2. If it's not obvious from the repo's README/AGENTS.md which applies, ask the user with AskUserQuestion rather than guessing — this changes the shape of every .github/rulesets/*.json file generated.

--branch-model main is the third choice, for trunk-only repos that should stay on a single main branch (a library, a tooling repo, anything with no deployed environment to promote between). It generates one ruleset, .github/rulesets/protect-main.json (squash-only, linear history, the required checks you add), and nothing else changes shape. Its defining property is on the apply side: apply never creates, renames, or default-flips a branch for a main-only repo — it only sets delete-branch-on-merge, applies the ruleset, and seeds labels. If the repo's current default branch is not already main (e.g. master), apply exits non-zero without touching anything; rename it to main first. A rulesets directory that mixes protect-main.json with protect-dev.json/ protect-prd.json is rejected by apply — a repo is either main-only or dev/prd, never both.

Step 2 — Create a worktree and propose

worktrail-repo-init never touches git state itself (no worktree, no commit, no push) — same division of responsibility as every other worktrail CLI. Branch off the target repo's current default branch first:

cd ~/projects/<repo>
git worktree add ../<repo>-worktrees/repo-standards -b repo-standards <current-default-branch>
cd ../<repo>-worktrees/repo-standards
worktrail-repo-init propose --repo . --branch-model 2 --json

Review the JSON result's written/skipped/warnings. If ci_jobs_discovered is non-empty, decide with the user which of those job display names (not workflow filenames — see doctrine section 3) should actually gate merges, and hand-edit the generated .github/rulesets/*.json files to add them to required_status_checks before committing — propose deliberately never does this automatically (a wrongly-required informational/flaky job would deadlock every future PR).

propose also scaffolds .github/workflows/rulesets_drift_guard.yml, a scheduled/PR-triggered workflow that runs the vendored scripts/ci/rulesets/rulesets_sync.py to check (and, on main, apply) drift between the committed .github/rulesets/*.json files and the rulesets actually configured on GitHub. It mints its own GitHub App token for the rulesets API calls rather than using secrets.GITHUB_TOKEN; nothing to configure at propose time, but see Step 4 for the App credentials it needs at runtime.

propose also scaffolds .github/workflows/dependabot_manifest_check.yml, a PR-triggered workflow that runs the vendored scripts/ci/dependabot/test_dependabot_config.py to catch a silent Dependabot-Updates failure mode: an updates entry in .github/dependabot.yml whose directory has no manifest file the declared package-ecosystem recognizes. Unlike the rulesets drift guard, it needs no GitHub credentials at all — no App token, no secrets.GITHUB_TOKEN — since it only reads files already checked out in the runner.

propose also scaffolds docs/engineering/pull-requests.md and adds a short "Pull requests" section to AGENTS.md (before any tool-managed block) pointing at it. The auto-merge workflow fails closed, so a green PR with no go:risk-low or go:risk-medium label never merges and shows no error; the doc tells agents in the repo that every PR needs the label and how to choose, set, change, and verify it. It is rendered for the chosen branch model (merge method per branch, and a hold-with- go:no-automerge rule for promotion branches), is repo-slug-free, and is write-if- absent like every other scaffolded file, so a tailored copy is never overwritten or reported as drift.

Ask the user whether to also pass --with-aspens (declares add_ons.aspens in the seeded policy file and runs aspens doc init immediately, instead of waiting for the repo's first orchestrated task) — don't default it on. Ask the user the same question about --with-gitnexus (runs gitnexus analyze --embeddings --index-only immediately, so the repo has a GitNexus index from the first commit instead of waiting for something else to trigger one) — also don't default it on. Ongoing index maintenance (cron re-indexing) still lives entirely in devops's own tooling (.claude/skills/gitnexus-index-maintenance/), with no bridge into worktrail today; --with-gitnexus only covers the one-time bootstrap index, not ongoing maintenance — capture that as a worktrail-handoff brief if it comes up.

Step 3 — Commit, push, open the PR

Standard worktree workflow: git add, commit, push, gh pr create targeting the repo's current default branch. State in the PR body that a follow-up worktrail-repo-init apply run will create dev/stg, rename the current default to prd, and flip the GitHub default branch to dev — reviewers should know the branch structure is about to change underneath this PR. For --branch-model main say instead that apply will only add branch protection on main; no branch is created or renamed.

Step 4 — Apply, after the PR merges

This step mutates live GitHub state (branch creation/rename, default branch, delete-branch-on-merge, branch protection) — get explicit user confirmation before running it, especially on a public repo. apply infers the branch model from the committed .github/rulesets/*.json files: protect-main.json alone means main-only, and the branch migration steps are skipped entirely (see Step 1). Run from the canonical checkout (not the worktree, which may be torn down already):

cd ~/projects/<repo>
git checkout <old-default-branch> && git pull
worktrail-repo-init apply --repo . --json

Report the result's branches/default_branch/delete_branch_on_merge/ rulesets status lines and the manual-follow-up checklist (retarget other open PRs onto dev, git fetch && git switch dev in every local clone). Any FAILED entry means apply exited non-zero — investigate and re-run; it is safe to re-run (it skips branches/renames already done, and PUT-then-reverifies rulesets whether or not they already exist).

If the repo's RELEASE_NOTES_APP_ID variable and RELEASE_NOTES_APP_PRIVATE_KEY secret aren't both already set, apply prints a warning that the scaffolded rulesets_drift_guard.yml will skip until the release-notes GitHub App is installed on the repo and those two credentials are configured — pass this reminder on to the user rather than treating it as a failure.

Examples

New repo, no CI, no deployment (e.g. a Windows desktop app distributed via GitHub Releases): --branch-model 2. ci_jobs_discovered comes back empty — the generated rulesets carry zero required status checks, which is correct until real CI exists.

Library or tooling repo with no deployed environment, already on main: --branch-model main. propose writes only protect-main.json; apply adds protection to main and leaves the branch layout untouched.

Existing repo with a Vercel-deployed frontend and per-PR preview deployments: --branch-model 2 — Vercel's preview URL already gives a pre-merge look at the deployed result; a persistent stg branch would duplicate that without adding a real gate.

Best Practices and Constraints

  • propose is idempotent by file existence — it never overwrites a file that's already there, so re-running it after partial completion (or after a human hand-edited a generated ruleset) is safe.
  • Never run apply speculatively "to see what happens" — it renames the live default branch and rewrites branch protection. Confirm with the user first, always, even under an otherwise-autonomous session.
  • The OpenSpec scaffold is --tools none deliberately: worktrail's own plugin already bundles the /opsx:* Claude Code integration (this repo's own AGENTS.md, "Claude Code plugin surface" section) — running openspec init --tools claude per onboarded repo would generate a second, un-vetted copy that conflicts with it.
  • .worktrail/policy.yaml is seeded with header comments plus exactly one key set: automerge: {enabled: true, max_risk: medium}, the fleet-wide default (~/rules/CLAUDE.repo.md section 3). That one is a standard, not a per-repo judgment — and leaving it commented out would make the worktrail-auto-merge.yml this same run scaffolds inert. medium is a ceiling: router/policy.py never treats high/critical as eligible whatever the file says, and clamps an invalid max_risk to low. Every other key still defaults to a safe, do-nothing value (see router/policy.py's DEFAULTS); don't guess a repo's pre_pr_cmd on its behalf, that's a follow-up decision for whoever owns the repo.
  • gitleaks.yml is the one scaffolded workflow with no paths filter, and its gitleaks-pr-diff job is wired into required_status_checks alongside openspec-validate. Both are deliberate: a leaked secret must block merge, and a required check that a docs-only diff skips never reports a status and deadlocks the merge. Its companion full-history job is workflow_dispatch only — run it once per repo to triage existing history, and allowlist a confirmed-synthetic fixture by VALUE in .gitleaks.toml, never by path.
  • worktrail-auto-merge.yml is inert until something applies a go:risk-low/go:risk-medium label — a repo that never uses worktrail-go's classifier for its PRs never has it fire. But if ci_jobs_discovered came back empty (no CI, no required_status_checks), flag this explicitly to the user: a risk-labeled PR on that repo will merge with nothing gating it at all until real CI checks are added to .github/rulesets/*.json.
  • --with-aspens reuses the existing AspensAddOn (worktrail.addons.aspens) directly for a one-time install()+configure() at bootstrap — it never calls AddOn.run() (aspens doc sync), since that's the per-task stage-and-commit path worktrail.addons.runner owns, not a bootstrap concern. A failed/unreachable aspens CLI surfaces as a warning, not a propose failure — .aspens.json existing is the only reliable success signal available (the add-on swallows subprocess errors as best-effort priming).
  • --with-gitnexus is idempotent by .gitnexus/ existing — propose checks for the directory first and skips the gitnexus analyze call entirely if a repo is already indexed, so re-running propose on a partially-onboarded repo never re-indexes. --index-only is deliberate: it skips GitNexus's own AGENTS.md/skills file injection, since bootstrap only wants the index, not a second, un-vetted copy of files this skill and the OpenSpec scaffold already manage. Like --with-aspens, it's best-effort — a failed/unreachable gitnexus CLI or a timeout surfaces as a warning, not a propose failure, because .gitnexus/ existing afterward, not the subprocess return code, is the only reliable success signal available.
Repository
behindthedash/worktrail
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.