From 4029866ed4c164c87770469e2dbed96b4215193a Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Thu, 6 Aug 2026 15:20:16 +0100 Subject: [PATCH 1/3] chore(claude): require ticket-scoped conventional commits on every commit The git conventions memory said to match the repository's existing log style. Several repositories (multicluster, core-services-cloud) have histories dominated by bare "WSP-1234: summary" subjects, so matching them produced commits that were not in conventional form. A related failure was scope decay within a session: the first commit was correct and later ones degraded to bare "test:" or "refactor:" subjects. Both required commit history to be rebased by hand. - Make "(): " mandatory on every commit and explicitly override repository log style. Style matching now applies to branch names only. - Describe how to establish the real ticket ID (named in the request, extracted from the branch, or taken from existing commits on the branch) and require asking rather than guessing when none is available. Replace the literal WSP-1234 examples with so the placeholder cannot be committed verbatim. - Record scope decay across a session as a named failure mode. - Cover merge commits, preferring rebase and requiring an explicit message when a merge commit is unavoidable. - Add a pre-push verification grep that must return no output. - Note that a clean git log does not prove a subject was correct when written, because rebasing replaces it; compare author and committer dates instead. Update the MEMORY.md index entry to match. --- home/claude/memory/MEMORY.md | 2 +- home/claude/memory/git_conventions.md | 28 ++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/home/claude/memory/MEMORY.md b/home/claude/memory/MEMORY.md index 0ce9713..c6b9f75 100644 --- a/home/claude/memory/MEMORY.md +++ b/home/claude/memory/MEMORY.md @@ -1,6 +1,6 @@ - [User name](user_name.md) — address the user as Lyra - [Soviet engineer persona](persona_soviet_engineer.md) — terse, dry, pragmatic; no emojis; technical accuracy over voice -- [Git conventions](git_conventions.md) — never commit to main, always a branch; Conventional Commits branches and messages; inspect repo style first; commit at logical checkpoints +- [Git conventions](git_conventions.md) — never commit to main, always a branch; EVERY commit is `type(): summary` using the live ticket, overrides repo's bare-prefix style; watch for scope decay on follow-up commits; grep to verify before pushing - [Git network ops](git_network_ops.md) — GitHub and Gitea (code.emmathe.dev) both pushable in-sandbox (sandbox off, agent key); raise Gitea PRs via tea CLI - [Git commit signing](git_commit_signing.md) — signs in-sandbox via ssh-agent (allowAllUnixSockets + inlined pubkey); sig=N without allowedSignersFile is cosmetic, still signed - [Git check state first](git_check_state.md) — always check branch/status/divergence before git work; Lyra edits repos between sessions diff --git a/home/claude/memory/git_conventions.md b/home/claude/memory/git_conventions.md index af383e3..5b11cb7 100644 --- a/home/claude/memory/git_conventions.md +++ b/home/claude/memory/git_conventions.md @@ -11,8 +11,30 @@ metadata: **Branch naming:** Follow the repo's existing convention — inspect with `git branch -a` or `git for-each-ref` before creating. Prefer Conventional Commits prefixes (`feat/`, `fix/`, `chore/`, `docs/`, `refactor/`). Format: `/-`. Only ask if no convention is discoverable. -**Commit messages:** Conventional Commits. Subject line: `(): ` — ticket ID as the scope. Use additional `-m` flags for rationale/body. Commit at logical checkpoints, not one giant final commit. +**Commit messages — every commit, without exception:** `(): `. The ticket ID goes in the scope. Use additional `-m` flags for rationale/body. Commit at logical checkpoints, not one giant final commit. -**Why:** Lyra's standard workflow for traceability and clean history. +**`` is the real ticket for the work in hand.** It is a symbol to substitute, never a literal — if a commit subject ever reaches git still containing ``, or a made-up number, that is a defect. Establish the actual ID before the first commit, in this order: -**How to apply:** Whenever creating a branch or committing in any repo. Inspect existing branches/log first so you match the repo's actual style; the format above is the default when nothing else is established. +1. The ticket Lyra named in the request. +2. The current branch name — `task/WSP-32542/remove-wspgov-terraform` gives `WSP-32542`. Extract it: `git branch --show-current | grep -oE '[A-Z]{2,}-[0-9]+'`. +3. The ticket the branch's existing commits already use. + +If none of those yield an ID, ask which ticket to file the work under. Do not guess, do not reuse the ID from an unrelated earlier task in the session, and do not invent a plausible-looking number. Every commit in a branch normally carries the same ID; if the work genuinely spans two tickets, split the commits accordingly rather than picking one at random. + +**This format is mandatory and overrides the repo's existing log style.** Many repos (`multicluster`, `core-services-cloud`) have histories full of bare `: summary` subjects written by other people. Do not copy that. Match repo style for *branch names* only; commit subjects are always full Conventional Commits with the ticket scope. CI enforces this, and a failure means Lyra rebases the history by hand. + +**Known failure mode — scope decay across a session.** The first commit gets `fix(): ...` correctly, then follow-up commits in the same sitting degrade to bare `test: add tests for class`, `refactor: hoist middleware`, `chore: tidy`. This has caused real rebase work in `core-services-cloud`. The second, third and fifth commits need the ticket scope exactly as much as the first. Re-read the subject against the format before every single `git commit`. + +**Merge commits count too.** Prefer `git rebase origin/` over `git merge` so none is created. If unavoidable, set the message explicitly: `git merge --no-ff -m ": merge master into "`. Keep the ID uppercase; the check is case-sensitive. + +**Before pushing, verify — do not skip this:** +``` +git log --format=%s origin/..HEAD | grep -vE '^[a-z]+(\([A-Z]{2,}-[0-9]+\))!?: ' +``` +Must print nothing. Writing each subject carefully is not a substitute for running it. + +**Auditing past behaviour is unreliable.** If Lyra has already rebased to fix a bad subject, the log shows her corrected version, not what was originally written. A clean `git log` is not evidence that nothing was wrong. Check author date vs committer date (`--format="%ad %cd"`) — a mismatch means history was rewritten. Never argue from a clean log that the fault did not occur. + +**Why:** Lyra's standard workflow for traceability, and a hard CI gate. A malformed subject is manual rebase work for her, not just a red build. + +**How to apply:** On every commit in every repo. Format first, repo style second. Run the verification grep before every push. Relates to [[git_check_state]]. -- 2.54.0 From 1d5a5adbcc059a540d400acd591f84f654113a1a Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Thu, 6 Aug 2026 15:20:53 +0100 Subject: [PATCH 2/3] chore(claude): exempt repos without an issue tracker from the ticket scope The previous commit required a ticket scope on every commit in every repository. This repository has no Jira project, so the rule as written would either block a commit or invite a fabricated WSP number. Record the exception: in personal repositories the scope is the area of the change (claude, deps, hosts) and conventional form still applies. The ticket requirement is scoped to the Jira-backed work repositories that enforce it in CI. --- home/claude/memory/git_conventions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/home/claude/memory/git_conventions.md b/home/claude/memory/git_conventions.md index 5b11cb7..9ce660c 100644 --- a/home/claude/memory/git_conventions.md +++ b/home/claude/memory/git_conventions.md @@ -21,6 +21,8 @@ metadata: If none of those yield an ID, ask which ticket to file the work under. Do not guess, do not reuse the ID from an unrelated earlier task in the session, and do not invent a plausible-looking number. Every commit in a branch normally carries the same ID; if the work genuinely spans two tickets, split the commits accordingly rather than picking one at random. +**Exception — repos with no issue tracker.** Personal repos such as `nixfiles` have no Jira project. There the scope is the area of the change, not a ticket: `chore(claude): ...`, `chore(deps): ...`, `feat(hosts): ...`. Conventional form is still required; only the ticket scope is dropped. Never invent a WSP number to satisfy the rule in a repo that has no tickets. The ticket requirement applies to the work repos under `~/code` that are backed by the WSP Jira project and gated by CI. + **This format is mandatory and overrides the repo's existing log style.** Many repos (`multicluster`, `core-services-cloud`) have histories full of bare `: summary` subjects written by other people. Do not copy that. Match repo style for *branch names* only; commit subjects are always full Conventional Commits with the ticket scope. CI enforces this, and a failure means Lyra rebases the history by hand. **Known failure mode — scope decay across a session.** The first commit gets `fix(): ...` correctly, then follow-up commits in the same sitting degrade to bare `test: add tests for class`, `refactor: hoist middleware`, `chore: tidy`. This has caused real rebase work in `core-services-cloud`. The second, third and fifth commits need the ticket scope exactly as much as the first. Re-read the subject against the format before every single `git commit`. @@ -37,4 +39,4 @@ Must print nothing. Writing each subject carefully is not a substitute for runni **Why:** Lyra's standard workflow for traceability, and a hard CI gate. A malformed subject is manual rebase work for her, not just a red build. -**How to apply:** On every commit in every repo. Format first, repo style second. Run the verification grep before every push. Relates to [[git_check_state]]. +**How to apply:** Conventional form on every commit in every repo; the ticket scope additionally on every commit in a Jira-backed work repo. Format first, repo style second. Run the verification grep before every push. Relates to [[git_check_state]]. -- 2.54.0 From f61a206977d4a5776f4549dda4247e146be42d0e Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Thu, 6 Aug 2026 16:53:36 +0100 Subject: [PATCH 3/3] style(claude): apply prettier formatting to the git conventions memory treefmt runs prettier over markdown in this repository and the CI formatting check failed on the two preceding commits. Prettier prefers underscores for emphasis and requires blank lines around fenced code blocks. No wording changes. --- home/claude/memory/git_conventions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/home/claude/memory/git_conventions.md b/home/claude/memory/git_conventions.md index 9ce660c..1a8c540 100644 --- a/home/claude/memory/git_conventions.md +++ b/home/claude/memory/git_conventions.md @@ -23,16 +23,18 @@ If none of those yield an ID, ask which ticket to file the work under. Do not gu **Exception — repos with no issue tracker.** Personal repos such as `nixfiles` have no Jira project. There the scope is the area of the change, not a ticket: `chore(claude): ...`, `chore(deps): ...`, `feat(hosts): ...`. Conventional form is still required; only the ticket scope is dropped. Never invent a WSP number to satisfy the rule in a repo that has no tickets. The ticket requirement applies to the work repos under `~/code` that are backed by the WSP Jira project and gated by CI. -**This format is mandatory and overrides the repo's existing log style.** Many repos (`multicluster`, `core-services-cloud`) have histories full of bare `: summary` subjects written by other people. Do not copy that. Match repo style for *branch names* only; commit subjects are always full Conventional Commits with the ticket scope. CI enforces this, and a failure means Lyra rebases the history by hand. +**This format is mandatory and overrides the repo's existing log style.** Many repos (`multicluster`, `core-services-cloud`) have histories full of bare `: summary` subjects written by other people. Do not copy that. Match repo style for _branch names_ only; commit subjects are always full Conventional Commits with the ticket scope. CI enforces this, and a failure means Lyra rebases the history by hand. **Known failure mode — scope decay across a session.** The first commit gets `fix(): ...` correctly, then follow-up commits in the same sitting degrade to bare `test: add tests for class`, `refactor: hoist middleware`, `chore: tidy`. This has caused real rebase work in `core-services-cloud`. The second, third and fifth commits need the ticket scope exactly as much as the first. Re-read the subject against the format before every single `git commit`. **Merge commits count too.** Prefer `git rebase origin/` over `git merge` so none is created. If unavoidable, set the message explicitly: `git merge --no-ff -m ": merge master into "`. Keep the ID uppercase; the check is case-sensitive. **Before pushing, verify — do not skip this:** + ``` git log --format=%s origin/..HEAD | grep -vE '^[a-z]+(\([A-Z]{2,}-[0-9]+\))!?: ' ``` + Must print nothing. Writing each subject carefully is not a substitute for running it. **Auditing past behaviour is unreliable.** If Lyra has already rebased to fix a bad subject, the log shows her corrected version, not what was originally written. A clean `git log` is not evidence that nothing was wrong. Check author date vs committer date (`--format="%ad %cd"`) — a mismatch means history was rewritten. Never argue from a clean log that the fault did not occur. -- 2.54.0