# How SwarmForge Enforces Role Bylines in Commits: Inside commit_msg_hook.bb

> Learn how SwarmForge's commit_msg_hook.bb automatically adds role bylines to commits. Discover how it ensures consistent attribution using environment variables or roles.tsv for your repository.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-31

---

**The `commit_msg_hook.bb` script automatically appends a "By [Role]." byline to every commit message by checking the `SWARMFORGE_ROLE` environment variable or inferring the role from a `roles.tsv` file, ensuring consistent attribution across the repository.**

SwarmForge, an open-source framework maintained by Uncle Bob, ensures code traceability by requiring every Git commit to include a role-specific byline. The enforcement mechanism lives in `swarmforge/scripts/commit_msg_hook.bb`, a Babashka script that functions as a `commit-msg` Git hook. This script intercepts commit messages and automatically appends the correct role attribution before the commit is finalized.

## How Role Detection Works

The script determines the author’s role through two distinct pathways implemented in the `role` function.

### Environment Variable Lookup

If the `SWARMFORGE_ROLE` environment variable is set, the script uses its value directly. This approach is ideal for CI/CD pipelines or developers who switch roles frequently between projects.

```bash
export SWARMFORGE_ROLE="Engineer"
git commit -m "Implement new feature"

# Result: Commit message ends with "By Engineer."

```

### File-Based Role Inference via roles.tsv

When the environment variable is absent, the script attempts to infer the role from a `roles.tsv` file. The `roles-file` function locates this file in either the repository’s `.swarmforge` directory or the common Git directory, while `infer-role` parses the entries.

According to the source code in `swarmforge/scripts/commit_msg_hook.bb` (lines 19-46), the `infer-role` function scans each line of the TSV file and compares the stored working-tree path (third column) with the current repository root. When a match is found, it returns the corresponding role name.

## Building and Appending the Byline

Once the role is identified, the script constructs the standardized byline using the `byline` function:

```clojure
(defn byline [role-name]
  (str "By " role-name "."))

```

The `append-byline` helper (lines 47-51) adds a blank line, the formatted byline, and a trailing newline to the existing message. This ensures consistent formatting regardless of whether the original message ended with newlines or not.

## The Commit Message Patching Process

The `-main` entry point (lines 53-61) receives the path to the temporary commit message file that Git creates (typically `.git/COMMIT_EDITMSG`). It reads the current message content and checks whether the correct byline is already present. If the specific role byline is missing, the script writes the modified message back to the file with the appended byline.

If the script cannot locate a role—meaning neither the environment variable is set nor a matching entry exists in `roles.tsv`—it exits cleanly without modifying the message, allowing the commit to proceed unchanged.

## Configuring Role Mappings with roles.tsv

The `roles.tsv` file enables per-project role assignment without environment variables. Each line follows a strict tab-separated format:

```

<role>	<description>	<working-tree-path>

```

For example:

```text
Engineer	Develop new features	/home/user/projects/swarm-forge
Product Manager	Guide roadmap	/home/user/projects/swarm-forge

```

When you run `git commit` from `/home/user/projects/swarm-forge`, the `infer-role` function matches the repository root against the third column and identifies the committer as "Engineer".

## Practical Usage Examples

### Testing the Hook Manually

You can test the hook logic without creating a permanent commit:

```bash
echo "Test commit message" > /tmp/COMMIT_EDITMSG
bb swarmforge/scripts/commit_msg_hook.bb /tmp/COMMIT_EDITMSG
cat /tmp/COMMIT_EDITMSG

```

### Viewing the Registration

The hook is registered by `swarmforge/scripts/swarmforge.bb` (lines 265-304), which configures Git to invoke `commit_msg_hook.bb` during the commit-msg phase.

## Summary

- **Automatic enforcement**: The Babashka script functions as a Git `commit-msg` hook, intercepting every commit to verify role attribution.
- **Dual detection method**: Roles are determined via the `SWARMFORGE_ROLE` environment variable or inferred from `roles.tsv` paths.
- **Non-blocking fallback**: If no role is found, the script exits silently without preventing the commit.
- **Standardized format**: All bylines follow the exact pattern "By [Role]." with proper newline separation.
- **Per-project configuration**: The `roles.tsv` file allows different roles for different repositories based on working directory paths.

## Frequently Asked Questions

### What happens if I don't have a role configured?

If neither the `SWARMFORGE_ROLE` environment variable nor a matching entry in `roles.tsv` exists, the script exits without modifying your commit message. The commit proceeds normally, just without the SwarmForge byline.

### Can I override the role for a single commit?

Yes. Set the `SWARMFORGE_ROLE` environment variable inline for a one-time override:

```bash
SWARMFORGE_ROLE="Product Manager" git commit -m "Update roadmap"

```

### Where should the roles.tsv file be located?

The script searches for `roles.tsv` in two locations: the `.swarmforge` directory within your repository, or the common Git directory. According to the `roles-file` function in `commit_msg_hook.bb`, it checks these paths in order to find the appropriate role mappings.

### How does the script handle existing bylines?

The `-main` function checks whether the commit message already contains the correct byline for the current role. If the exact "By [Role]." string is present, the script leaves the message unchanged. If the role has changed or the byline is missing, it appends the new correct byline to the end of the message.