.claude/project-context.md documents the branch convention as:
Branches follow Conventional Branch, with the issue number carried for Gitea: <type>/<issue-number>-<short-slug> … Drop the number when there is no issue behind the work (chore/tidy-dead-routes).
That escape hatch no longer reflects how the project is run. Every branch and every pull request should have an issue behind it; where none exists, the issue gets filed first rather than the number omitted.
Why it changed
The clause reads as harmless — it is only meant for trivial work — but in practice it turns "there is no issue yet" into "there is no issue ever". Two branches this week went that way: feature/app-icon (merged as #70) and the header mark (now #76, filed retroactively). Both came from direct requests in conversation, both were legitimate work, and neither existed in the tracker until afterwards. The convention permitted it, so nothing flagged it.
The cost is not bookkeeping. An issue is where the reasoning, the alternatives considered, and the verification end up — the durable record that survives the conversation that produced it. A branch with no issue behind it leaves that record nowhere, and the only trace is a commit message.
Change
Rewrite the clause to require an issue in all cases, and say explicitly that work arriving through conversation gets an issue filed first. Everything else about the convention — the <type>/ prefixes, the (#N) subject, the Closes #N body line, the point that Gitea links from the commit rather than the branch name — stays as it is.
Worth deciding at the same time
Whether to enforce it mechanically. A PreToolUse hook could refuse a git checkout -b whose branch name carries no issue number. That would make the rule impossible to forget rather than merely written down, which is the same argument that produced the route-wrapping guard in #59 and the tsconfig drift check in #67 — both cases where a convention held only by memory had already been forgotten once.
The counter-argument is that a hook fires on a mechanical signal and cannot tell a genuine exception from a mistake. Worth a decision rather than a default.
Severity
Low. Nothing is broken; the documentation simply permits something the project no longer wants, and the next session would follow it in good faith.
## Problem
`.claude/project-context.md` documents the branch convention as:
> Branches follow Conventional Branch, with the issue number carried for Gitea: `<type>/<issue-number>-<short-slug>` … **Drop the number when there is no issue behind the work** (`chore/tidy-dead-routes`).
That escape hatch no longer reflects how the project is run. Every branch and every pull request should have an issue behind it; where none exists, the issue gets filed first rather than the number omitted.
## Why it changed
The clause reads as harmless — it is only meant for trivial work — but in practice it turns "there is no issue yet" into "there is no issue ever". Two branches this week went that way: `feature/app-icon` (merged as #70) and the header mark (now #76, filed retroactively). Both came from direct requests in conversation, both were legitimate work, and neither existed in the tracker until afterwards. The convention permitted it, so nothing flagged it.
The cost is not bookkeeping. An issue is where the reasoning, the alternatives considered, and the verification end up — the durable record that survives the conversation that produced it. A branch with no issue behind it leaves that record nowhere, and the only trace is a commit message.
## Change
Rewrite the clause to require an issue in all cases, and say explicitly that work arriving through conversation gets an issue filed first. Everything else about the convention — the `<type>/` prefixes, the `(#N)` subject, the `Closes #N` body line, the point that Gitea links from the commit rather than the branch name — stays as it is.
## Worth deciding at the same time
Whether to enforce it mechanically. A `PreToolUse` hook could refuse a `git checkout -b` whose branch name carries no issue number. That would make the rule impossible to forget rather than merely written down, which is the same argument that produced the route-wrapping guard in #59 and the tsconfig drift check in #67 — both cases where a convention held only by memory had already been forgotten once.
The counter-argument is that a hook fires on a mechanical signal and cannot tell a genuine exception from a mistake. Worth a decision rather than a default.
## Severity
Low. Nothing is broken; the documentation simply permits something the project no longer wants, and the next session would follow it in good faith.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
.claude/project-context.mddocuments the branch convention as:That escape hatch no longer reflects how the project is run. Every branch and every pull request should have an issue behind it; where none exists, the issue gets filed first rather than the number omitted.
Why it changed
The clause reads as harmless — it is only meant for trivial work — but in practice it turns "there is no issue yet" into "there is no issue ever". Two branches this week went that way:
feature/app-icon(merged as #70) and the header mark (now #76, filed retroactively). Both came from direct requests in conversation, both were legitimate work, and neither existed in the tracker until afterwards. The convention permitted it, so nothing flagged it.The cost is not bookkeeping. An issue is where the reasoning, the alternatives considered, and the verification end up — the durable record that survives the conversation that produced it. A branch with no issue behind it leaves that record nowhere, and the only trace is a commit message.
Change
Rewrite the clause to require an issue in all cases, and say explicitly that work arriving through conversation gets an issue filed first. Everything else about the convention — the
<type>/prefixes, the(#N)subject, theCloses #Nbody line, the point that Gitea links from the commit rather than the branch name — stays as it is.Worth deciding at the same time
Whether to enforce it mechanically. A
PreToolUsehook could refuse agit checkout -bwhose branch name carries no issue number. That would make the rule impossible to forget rather than merely written down, which is the same argument that produced the route-wrapping guard in #59 and the tsconfig drift check in #67 — both cases where a convention held only by memory had already been forgotten once.The counter-argument is that a hook fires on a mechanical signal and cannot tell a genuine exception from a mistake. Worth a decision rather than a default.
Severity
Low. Nothing is broken; the documentation simply permits something the project no longer wants, and the next session would follow it in good faith.