Contributing to himmel
The workflow is built for one operator first and works with several. The repo is engineered for one main maintainer and occasional collaborators; the conventions below keep that working at any team size.
Branch + PR workflow
-
Never edit on
main. Two enforcement layers work in tandem:scripts/hooks/block-edit-on-main.sh(PreToolUse) blocks edits at write time, andscripts/hooks/check-worktree-isolation.sh(pre-commit) catches any that reach commit stage. -
Create a worktree per ticket / unit of work:
Terminal window bash scripts/worktree.sh feat/himmel-<N>-<slug># or/worktree feat/himmel-<N>-<slug> # in Claude CodeBranch names must be
<type>/<slug>wheretype ∈ feat|fix|chore|docs|refactor|test.scripts/worktree.shenforces this. -
Commit format: conventional commits with a required ticket key in this repo.
type(scope): HIMMEL-N messageOptional longer body...Platforms tested: gitbashCo-Authored-By: ...scripts/hooks/check-commit-msg.shvalidates the format oncommit-msg; CI rechecks every PR commit. A ticket reference is required by default: setJIRA_PROJECT_KEYin.envforPROJECT-123, orTICKET_ID_PATTERNfor another tracker; with neither, the gate accepts himmel’s own#Nenumeration (#123).TICKET_ID_REQUIRED=0opts out.The
Co-Authored-By: Claudetrailer is emitted by Claude Code itself, not by any himmel script. To turn it off, set theattributionkey in your user-scope~/.claude/settings.json(keeps it personal and out of the repo; for a project-local override use the gitignored.claude/settings.local.json):{"attribution": { "commit": "", "pr": "" }}attribution(Claude Code 2.0.62+) supersedes the deprecatedincludeCoAuthoredByboolean and takes precedence over it. Either disables the trailer; preferattribution. This does not affect thePlatforms tested:/Security reviewed:attestation trailers, which the pre-push gates require independently. -
Push opens a multi-agent review-owed marker. Run
/pr-check(or/pr-review-toolkit:review-pr) in your Claude session before opening the PR; the marker blocksgh pr createuntil automated review is clean. -
Open the PR:
gh pr create --body-file <file>(writing the body to a file avoids heredoc / shell-substitution surprises). -
Merge mode is squash + admin + delete-branch. Repo settings forbid merge-commits.
Terminal window gh pr merge <N> --squash --admin --delete-branch
Pre-commit / pre-push gates
All gates are wired in .pre-commit-config.yaml, and that file is the source
of truth. Highlights:
- Format / lint: trailing-whitespace, EOF, yaml, json, shellcheck, gitleaks.
- Branch hygiene: worktree-isolation, merged-branch check, no-push-to-main.
- Jira plugin enforcement: committed code referencing Atlassian MCP Jira
tools that have a
scripts/jira/plugin equivalent is blocked (mcp-plugin-refs). - CR-before-push: the multi-agent review-owed marker described above
(
check-cr-before-push.sh). - Platforms-tested: shell / script changes must include a
Platforms tested: <platforms>line in the commit body or PR description. - PR mergeable + no force-push: pre-push refuses pushes when the PR is
CONFLICTING or when a force-push targets
main.
Cross-platform conventions
- bash 3.2 is the floor (macOS default). Scripts that use
mapfilerequire bash 4+. Re-derive the set withgrep -rl mapfile scripts --include='*.sh'instead of trusting a count here. - Some scripts use
realpath -mwith apython3fallback so macOS works withoutcoreutils.
Enable the doc-guard gate
The catalog-sync gate (doc-guard, scripts/hooks/check-doc-guard.sh) is opt-in: it guards
himmel contributors but must stay off for adopters who use himmel only as a
harness. Enable it by dropping a gitignored marker at the repo root:
touch .himmel-devWithout this marker, scripts/himmel-update.sh --plugins-check will remind
you the gate is off. The marker is listed in .gitignore and never committed.
Smoke tests
Every new shell hook or handover script ships with a paired smoke test
at scripts/<area>/test-<thing>.sh. Run after any edit:
bash scripts/hooks/test-check-cr-before-push.shbash scripts/handover/test-auto-commit.sh# etc.After editing any file a team card reads (the role definitions and shared sources named in the ROLES list of scripts/docs/gen-team-page.sh), run bash scripts/docs/gen-team-page.sh to rebuild the Meet the team page; scripts/docs/test-team-page.sh fails until you do.
Where to find things
| Concern | Doc |
|---|---|
| Repo-wide rules + conventions | CLAUDE.md |
| First-time setup | docs/setup/new-machine.md |
| Handover system | docs/handover/ |
| AFK (unattended) mode | docs/handover/afk-mode.md |
| Luna-area docs convention | docs/luna/ |
| Tooling catalog | docs/tooling-catalog.md |
Filing tickets
Use the local Jira CLI rather than the Atlassian MCP plugin (see CLAUDE.md
## Jira tooling — prefer plugin over MCP for the rationale and command
mapping).
node scripts/jira/dist/index.js create --project HIMMEL --type Task \ --title "Short subject" --desc "Spec body with acceptance criteria" \ --sector "Ship loop" --parent HIMMEL-NNNA Task, Story or Bug needs --sector and either --parent <epic> or
--labels standalone; create refuses it otherwise (HIMMEL-6223).
Guard changes: one structural round
A guard (a hook or gate that denies a command) gets one structural round: one PR that closes the bypass class it was built for. Bypasses found after that round go into one residual ticket on that guard. They do not each get a new PR. Guard churn was the costliest pattern in the 30-day self-review: 28 merges went to the write fence alone, including the three most expensive PRs.
Cleanups run in capped batches
A cleanup ships as a queue of batches, never one open-ended PR at a time.
Each runner in scripts/cleanup/ has a fixed cap and refuses a batch over it.
| Area | Runner | Cap per batch |
|---|---|---|
| Cleanup tickets and PRs | cleanup-batch.sh plan [<paths-file>] queues the work; cleanup-batch.sh check refuses a branch over the cap | 20 files, one directory family |
| Worktree reclaim | worktree-batch.sh plan, then worktree-batch.sh apply <file> (each path goes through worktree.sh prune --only) | 10 worktrees |
| Jira hygiene | jira-batch.sh plan stale|version|status, then jira-batch.sh apply <file> (adds a stale, version-drift or status-drift label) | 20 tickets |
The 20-file cap comes from HIMMEL-5629: across its 59 merges the median
merge touched 21 files, and a tenth of them touched more than 109.
A directory family is the first two path components, such as
scripts/hooks. Review the plan output before you run apply.
Monthly self-review
scripts/insights/self-review.sh prints five numbers for a window of main:
merges, fix share, guard churn, compaction share, the ticket with the most
merges (sprawl), and merges that mention a red main.
bash scripts/insights/self-review.sh # the last 30 daysbash scripts/insights/self-review.sh --since 2026-09-21 --until 2026-10-21Baseline, 2026-09-21 to 2026-10-21: 2,353 merges; fix 1,393 against feat 560 (59 %); guard churn 385; 1,166 of 2,053 runs compacted (56 %); HIMMEL-5629 with 56 merges; 15 red-main merges. Compare each new 30-day window with it.
Reporting issues
Open a GitHub issue or file a Jira ticket directly. There’s no separate issue-template gate.
For security vulnerabilities, do not open a public issue; follow
SECURITY.md. All participation is governed by our
CODE_OF_CONDUCT.md.