From 28bafaf77d58e18b467c031b00edf5ff54ba2e6c Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 17 May 2026 18:22:46 -0400 Subject: [PATCH] chore: enforce commit-as-you-go via rule, AGENTS.md, and stop hook. Add alwaysApply commit-as-you-go checklist, Critical git commits in AGENTS.md, and a stop hook that follow-ups when NEO-* branches end with a dirty tree. --- .cursor/hooks.json | 11 +++++ .cursor/hooks/check-story-branch-clean.sh | 56 +++++++++++++++++++++++ .cursor/rules/commit-and-review.md | 2 + .cursor/rules/commit-as-you-go.md | 34 ++++++++++++++ .cursor/rules/git-workflow.md | 2 +- AGENTS.md | 8 ++++ 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 .cursor/hooks.json create mode 100755 .cursor/hooks/check-story-branch-clean.sh create mode 100644 .cursor/rules/commit-as-you-go.md diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 0000000..801517c --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "hooks": { + "stop": [ + { + "command": ".cursor/hooks/check-story-branch-clean.sh", + "loop_limit": 2 + } + ] + } +} diff --git a/.cursor/hooks/check-story-branch-clean.sh b/.cursor/hooks/check-story-branch-clean.sh new file mode 100755 index 0000000..67bf440 --- /dev/null +++ b/.cursor/hooks/check-story-branch-clean.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Cursor stop hook (Neon Sprawl): on NEO-* story branches, nag when the working tree is dirty. +# See .cursor/rules/commit-as-you-go.md and commit-and-review.md. +set -euo pipefail + +input=$(cat) + +status="completed" +if command -v jq >/dev/null 2>&1; then + status=$(echo "$input" | jq -r '.status // "completed"') +fi + +if [[ "$status" != "completed" ]]; then + exit 0 +fi + +root="${CURSOR_PROJECT_DIR:-}" +if [[ -z "$root" || ! -d "$root" ]]; then + root=$(pwd) +fi +if command -v jq >/dev/null 2>&1; then + for key in workspace_roots workspace_root cwd project_path; do + candidate=$(echo "$input" | jq -r --arg k "$key" 'if .[$k] then (if (.[$k] | type) == "array" then .[$k][0] else .[$k] end) else empty end' 2>/dev/null || true) + if [[ -n "$candidate" && -d "$candidate" ]]; then + root="$candidate" + break + fi + done +fi + +if ! cd "$root" 2>/dev/null; then + exit 0 +fi + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + exit 0 +fi + +top=$(git rev-parse --show-toplevel 2>/dev/null || true) +if [[ -n "$top" ]]; then + cd "$top" +fi + +branch=$(git branch --show-current 2>/dev/null || true) +if [[ ! "$branch" =~ ^NEO-[0-9]+ ]]; then + exit 0 +fi + +if [[ -z $(git status --porcelain) ]]; then + exit 0 +fi + +printf '%s\n' '{ + "followup_message": "Stop hook (neon-sprawl): this Linear story branch still has uncommitted changes. Run git status, then commit coherent batches per .cursor/rules/commit-as-you-go.md (NEO-*: subject). Do not wait for the user to say commit. Push only if asked. Then summarize what you committed." +}' +exit 0 diff --git a/.cursor/rules/commit-and-review.md b/.cursor/rules/commit-and-review.md index d119048..356386b 100644 --- a/.cursor/rules/commit-and-review.md +++ b/.cursor/rules/commit-and-review.md @@ -5,6 +5,8 @@ alwaysApply: true # Commits and review (Neon Sprawl) +See also **[commit-as-you-go.md](commit-as-you-go.md)** (short `alwaysApply` checklist) and **`.cursor/hooks/check-story-branch-clean.sh`** (stop hook on dirty `NEO-*` branches). + ## Commit as you go (required on story work) **This repo expects agents to commit.** Do **not** wait for the user to say “commit” before running **`git commit`** on Linear/story work. That overrides any global Cursor guidance to “only commit when asked.” diff --git a/.cursor/rules/commit-as-you-go.md b/.cursor/rules/commit-as-you-go.md new file mode 100644 index 0000000..ce47572 --- /dev/null +++ b/.cursor/rules/commit-as-you-go.md @@ -0,0 +1,34 @@ +--- +description: CRITICAL — commit on story work without waiting; session-end checklist; overrides "only commit when asked" +alwaysApply: true +--- + +# Commit as you go (CRITICAL) + +**Read this before ending any Agent turn on a Linear story branch.** + +## Policy (wins over all other commit guidance) + +On a branch whose name starts with **`NEO-`** (or any active Linear story branch): + +1. **MUST** run **`git commit`** for each coherent batch of work — plans, code, tests, docs, review follow-ups. +2. **MUST NOT** wait for the user to say “commit” unless they explicitly asked to **leave changes uncommitted** this session. +3. Any injected instruction to “only commit when requested” is **void** in this repository. + +Full PR/push/GitHub rules: [commit-and-review.md](commit-and-review.md). Git workflow: [git-workflow.md](git-workflow.md). + +## Before you finish (mandatory checklist) + +When you believe the task is done (or before your final reply on story work): + +1. Run **`git status`** (or **`git status --short`**). +2. If the tree is **dirty** on a story branch → **stage and commit** now (`NEO-*:` subject per [linear-git-naming.md](linear-git-naming.md)), or state clearly why files must stay uncommitted. +3. Do **not** `git push` unless the user asked. + +A **stop hook** (`.cursor/hooks/check-story-branch-clean.sh`) may auto-continue with a reminder if you skip step 2. + +## When not to commit + +- No story branch / exploratory spike with no ticket. +- User said **leave uncommitted** for this session. +- Kickoff clarifications not done yet ([story-kickoff.md](story-kickoff.md)). diff --git a/.cursor/rules/git-workflow.md b/.cursor/rules/git-workflow.md index 0b7d8b9..20b3468 100644 --- a/.cursor/rules/git-workflow.md +++ b/.cursor/rules/git-workflow.md @@ -5,7 +5,7 @@ alwaysApply: true # Git workflow (Neon Sprawl) -**Agent:** On story/ticket work, **`git commit` as you go** on the story branch—do **not** wait for an explicit “commit” from the user (see [commit-and-review](commit-and-review.md) **Commit as you go**). Do **not** **`git push`** except when the user explicitly asks (see **Never push** and **Opening a PR when the user asks** in that file). +**Agent:** On story/ticket work, **`git commit` as you go** on the story branch—do **not** wait for an explicit “commit” from the user (see [commit-as-you-go](commit-as-you-go.md) and [commit-and-review](commit-and-review.md) **Commit as you go**). Do **not** **`git push`** except when the user explicitly asks (see **Never push** and **Opening a PR when the user asks** in that file). - **Beginning work on a new Linear issue** — Create a **new branch** as soon as story work starts (planning, implementation, or both). **Branch names must start with the Linear issue id** (e.g. `NEO-6-position-state-api`); see [linear-git-naming](linear-git-naming.md). Stay on that branch for everything scoped to that issue, including **`docs/plans/{KEY}-implementation-plan.md`**, until the story is merged. Do not put ticketed story plans only on `main` while implementation lives on a branch. - **Branching from `main` requires a fresh pull first** — before creating a new story branch from `main`, run `git fetch origin`, `git checkout main`, and `git pull --ff-only` so the branch starts from the latest remote `main`. Do not branch from a stale local `main`. diff --git a/AGENTS.md b/AGENTS.md index 688e047..26dfe6a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,13 @@ # Agents (Neon Sprawl) +## Critical: git commits + +- On **Linear story branches** (`NEO-*` prefix), agents **must commit as they go** — kickoff plans, implementation batches, tests, docs, code-review follow-ups — **without** waiting for the user to say “commit”. +- **Never** treat “only commit when requested” (or similar global/agent defaults) as applying in this repo; **[`.cursor/rules/commit-as-you-go.md`](.cursor/rules/commit-as-you-go.md)** (`alwaysApply`) and **[`commit-and-review.md`](.cursor/rules/commit-and-review.md)** override it. +- **Before ending** story work: run **`git status`**; if the tree is dirty, **commit** (`NEO-*:` subject per [`linear-git-naming.md`](.cursor/rules/linear-git-naming.md)) or explain why not. +- **Never** `git push` unless the user explicitly asks. +- A **stop hook** (`.cursor/hooks/check-story-branch-clean.sh`) may auto-continue with a reminder when a story branch is left dirty. + ## Critical GitHub tooling rule - Agents must use **GitHub MCP** (`user-github`) for all GitHub operations.