Skip to content

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

  1. Never edit on main. Two enforcement layers work in tandem: scripts/hooks/block-edit-on-main.sh (PreToolUse) blocks edits at write time, and scripts/hooks/check-worktree-isolation.sh (pre-commit) catches any that reach commit stage.

  2. 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 Code

    Branch names must be <type>/<slug> where type ∈ feat|fix|chore|docs|refactor|test. scripts/worktree.sh enforces this.

  3. Commit format: conventional commits with a required ticket key in this repo.

    type(scope): HIMMEL-N message
    Optional longer body...
    Platforms tested: gitbash
    Co-Authored-By: ...

    scripts/hooks/check-commit-msg.sh validates the format on commit-msg; CI rechecks every PR commit. A ticket reference is required by default: set JIRA_PROJECT_KEY in .env for PROJECT-123, or TICKET_ID_PATTERN for another tracker; with neither, the gate accepts himmel’s own #N enumeration (#123). TICKET_ID_REQUIRED=0 opts out.

    The Co-Authored-By: Claude trailer is emitted by Claude Code itself, not by any himmel script. To turn it off, set the attribution key 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 deprecated includeCoAuthoredBy boolean and takes precedence over it. Either disables the trailer; prefer attribution. This does not affect the Platforms tested: / Security reviewed: attestation trailers, which the pre-push gates require independently.

  4. 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 blocks gh pr create until automated review is clean.

  5. Open the PR: gh pr create --body-file <file> (writing the body to a file avoids heredoc / shell-substitution surprises).

  6. 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 mapfile require bash 4+. Re-derive the set with grep -rl mapfile scripts --include='*.sh' instead of trusting a count here.
  • Some scripts use realpath -m with a python3 fallback so macOS works without coreutils.

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:

Terminal window
touch .himmel-dev

Without 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:

Terminal window
bash scripts/hooks/test-check-cr-before-push.sh
bash 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

ConcernDoc
Repo-wide rules + conventionsCLAUDE.md
First-time setupdocs/setup/new-machine.md
Handover systemdocs/handover/
AFK (unattended) modedocs/handover/afk-mode.md
Luna-area docs conventiondocs/luna/
Tooling catalogdocs/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).

Terminal window
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-NNN

A 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.

AreaRunnerCap per batch
Cleanup tickets and PRscleanup-batch.sh plan [<paths-file>] queues the work; cleanup-batch.sh check refuses a branch over the cap20 files, one directory family
Worktree reclaimworktree-batch.sh plan, then worktree-batch.sh apply <file> (each path goes through worktree.sh prune --only)10 worktrees
Jira hygienejira-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.

Terminal window
bash scripts/insights/self-review.sh # the last 30 days
bash scripts/insights/self-review.sh --since 2026-09-21 --until 2026-10-21

Baseline, 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.