Formalize C0/C1/C2 change classification #259
3 changed files with 42 additions and 4 deletions
Fix: switch invariant check from pre-commit to commit-msg hook
The pre-commit stage runs before the commit is created, so it can't validate the commit being made. The commit-msg stage receives the message file as argv[1], allowing the hook to include the pending commit in its invariant check. Also works standalone (no args) for validating existing branch history. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
commit
f1c2605331
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
# See https://pre-commit.com for more information
|
||||
# Run: uvx pre-commit run --all-files
|
||||
# Install: uvx pre-commit install
|
||||
# Install: uvx pre-commit install && uvx pre-commit install --hook-type commit-msg
|
||||
|
||||
repos:
|
||||
# General file hygiene
|
||||
|
|
@ -118,6 +118,7 @@ repos:
|
|||
language: system
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
stages: [commit-msg]
|
||||
|
||||
# Documentation validation
|
||||
- repo: local
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ C2(deploy-authentik): close configure-postgres
|
|||
C2(deploy-authentik): finalize rewrite cards as historical documentation
|
||||
```
|
||||
|
||||
The `mikado-branch-invariant-check` pre-commit hook validates this convention and the invariant ordering.
|
||||
The `mikado-branch-invariant-check` commit-msg hook validates this convention and the invariant ordering.
|
||||
|
||||
### Process
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ tags:
|
|||
| `mise run docs-mikado --resume` | Resume a chain: detect branch, show state and next steps |
|
||||
| `mise run docs-mikado --resume <chain>` | Resume a specific chain with branch consistency check |
|
||||
|
||||
The `mikado-branch-invariant-check` pre-commit hook runs automatically on `mikado/*` branches, validating commit message conventions and invariant ordering.
|
||||
The `mikado-branch-invariant-check` commit-msg hook runs automatically on `mikado/*` branches, validating commit message conventions and invariant ordering. Requires `uvx pre-commit install --hook-type commit-msg`.
|
||||
|
||||
## Related
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,18 @@
|
|||
#MISE description="Validate Mikado Branch Invariant on mikado/* branches"
|
||||
"""Validate the Mikado Branch Invariant for C2 change branches.
|
||||
|
||||
Runs as a pre-commit hook on mikado/* branches. Checks:
|
||||
Runs as a commit-msg hook on mikado/* branches. Receives the commit message
|
||||
file as its first argument, classifies the incoming commit, appends it to the
|
||||
existing branch history, and validates the full sequence.
|
||||
|
||||
Checks:
|
||||
1. All commits follow the C2(<chain>): <verb> <description> convention
|
||||
2. The invariant ordering is maintained: plan commits come before impl/close
|
||||
3. No plan commits appear after any impl or close commits
|
||||
4. Close commits don't appear before impl commits in the same cycle
|
||||
5. The chain stem in commit messages matches the branch name
|
||||
|
||||
Can also be run standalone (no arguments) to validate existing branch history.
|
||||
|
||||
Exit code 0 if valid (or not on a mikado/* branch), 1 if violations found.
|
||||
"""
|
||||
|
|
@ -71,6 +78,29 @@ def get_branch_commits(branch: str) -> list[dict]:
|
|||
return commits
|
||||
|
||||
|
||||
def parse_commit_message(msg_path: str) -> str:
|
||||
"""Read and return the first line of a commit message file."""
|
||||
with open(msg_path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#"):
|
||||
return line
|
||||
return ""
|
||||
|
||||
|
||||
def make_pending_commit(subject: str) -> dict:
|
||||
"""Create a pseudo-commit dict for the incoming (not yet created) commit."""
|
||||
match = C2_COMMIT_RE.match(subject)
|
||||
return {
|
||||
"sha": "(pending)",
|
||||
"subject": subject,
|
||||
"chain": match.group(1) if match else None,
|
||||
"verb": match.group(2) if match else None,
|
||||
"description": match.group(3) if match else None,
|
||||
"conventional": match is not None,
|
||||
}
|
||||
|
||||
|
||||
def check_invariant(commits: list[dict], chain_stem: str) -> list[str]:
|
||||
"""Check the Mikado Branch Invariant. Returns list of violation messages."""
|
||||
errors: list[str] = []
|
||||
|
|
@ -136,6 +166,13 @@ def main() -> None:
|
|||
chain_stem = branch.removeprefix("mikado/")
|
||||
commits = get_branch_commits(branch)
|
||||
|
||||
# If called with a commit message file (commit-msg hook), include the
|
||||
# pending commit in the validation
|
||||
if len(sys.argv) > 1:
|
||||
subject = parse_commit_message(sys.argv[1])
|
||||
if subject:
|
||||
commits.append(make_pending_commit(subject))
|
||||
|
||||
if not commits:
|
||||
# No commits on branch yet — valid (length-zero case)
|
||||
sys.exit(0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue