Template
feat(hooks): add ticket-gate Crush hook — enforce ticket-first work
Mechanically enforces ticket-governed work: blocks modifying operations
until an active ticket is set (echo '#NNN' > .crush/active-ticket).
Exempts read-only tools and management commands.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
!crush.json
|
||||||
|
!.gitignore
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "^(bash|edit|write|multiedit|lsp_replace_symbol|lsp_rename)$",
|
||||||
|
"command": "./hooks/ticket-gate.sh",
|
||||||
|
"timeout": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,6 +60,7 @@ Replace this bracketed text.>
|
|||||||
|
|
||||||
- **Redmine** is the single system of record for ALL project work — tickets, schedules, Gantt, dependencies. Use the `redmine-cli`. **Do not use Gitea issues.** Reference tickets as `[#NNN]`.
|
- **Redmine** is the single system of record for ALL project work — tickets, schedules, Gantt, dependencies. Use the `redmine-cli`. **Do not use Gitea issues.** Reference tickets as `[#NNN]`.
|
||||||
- **NEVER close a Redmine ticket without explicit user permission.** You may SUGGEST a close when the result is clearly scoped and delivered. If it's ambiguous whether the work is truly complete, don't suggest a close — leave that decision to the user. This applies to ALL tickets, no exceptions.
|
- **NEVER close a Redmine ticket without explicit user permission.** You may SUGGEST a close when the result is clearly scoped and delivered. If it's ambiguous whether the work is truly complete, don't suggest a close — leave that decision to the user. This applies to ALL tickets, no exceptions.
|
||||||
|
- **Ticket-first enforcement (mechanically enforced).** Before starting any work, set the active ticket: `echo '#NNN' > .crush/active-ticket`. The Crush hook (`hooks/ticket-gate.sh`) blocks modifying operations until this file exists. If no ticket exists, CREATE ONE FIRST via redmine-cli, then set it. Clear when done: `> .crush/active-ticket`.
|
||||||
- **Discourse** is the single system of record for documentation. Use the `discourse-cli`. **Do not author long-form docs in gitea.**
|
- **Discourse** is the single system of record for documentation. Use the `discourse-cli`. **Do not author long-form docs in gitea.**
|
||||||
- **Git-tracked `.md` files are stubs** that point to the relevant Discourse topic URL. Operational files that must live next to code (`AGENTS.md`, `STATUS.md`, `questions-v*.md`) are the documented exceptions.
|
- **Git-tracked `.md` files are stubs** that point to the relevant Discourse topic URL. Operational files that must live next to code (`AGENTS.md`, `STATUS.md`, `questions-v*.md`) are the documented exceptions.
|
||||||
- `STATUS.md` is a scratchpad for token efficiency, **not** a system of record.
|
- `STATUS.md` is a scratchpad for token efficiency, **not** a system of record.
|
||||||
|
|||||||
Executable
+78
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# hooks/ticket-gate.sh — enforce ticket-first work policy
|
||||||
|
#
|
||||||
|
# Blocks modifying operations until an active ticket is established.
|
||||||
|
# The agent sets the active ticket via: echo '#NNN' > .crush/active-ticket
|
||||||
|
# And clears it when done: > .crush/active-ticket
|
||||||
|
#
|
||||||
|
# Exempts read-only and management commands (so you can create tickets,
|
||||||
|
# run audits, check status, etc.).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TICKET_FILE="${CRUSH_PROJECT_DIR}/.crush/active-ticket"
|
||||||
|
TOOL="${CRUSH_TOOL_NAME:-}"
|
||||||
|
CMD="${CRUSH_TOOL_INPUT_COMMAND:-}"
|
||||||
|
|
||||||
|
# Read-only tools — always allowed
|
||||||
|
case "$TOOL" in
|
||||||
|
view|ls|grep|glob|agent|sourcegraph|fetch|agentic_fetch|download|lsp_diagnostics|lsp_symbols|lsp_definition|lsp_references|lsp_call_hierarchy|crush_info|crush_logs|question|todos)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# For bash tool: exempt read-only and management commands
|
||||||
|
if [ "$TOOL" = "bash" ]; then
|
||||||
|
# Ticket/doc/dns management — always allowed
|
||||||
|
case "$CMD" in
|
||||||
|
*"redmine-cli"*|*"discourse-cli"*|*"dns-cli"*|*"technitium"*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
# Read-only git
|
||||||
|
case "$CMD" in
|
||||||
|
*"git status"*|*"git log"*|*"git diff"*|*"git show"*|*"git branch"*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
# Repo hygiene scripts
|
||||||
|
case "$CMD" in
|
||||||
|
*"check-rules"*|*"setup-hooks"*|*"shellcheck"*|*"run-tests"*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
# Monitoring/probe commands
|
||||||
|
case "$CMD" in
|
||||||
|
*"tailscale status"*|*"access-matrix"*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
# Setting/clearing the active ticket
|
||||||
|
case "$CMD" in
|
||||||
|
*active-ticket*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For edit/write: exempt policy/hook files (these ARE the policy)
|
||||||
|
FILE_PATH="${CRUSH_TOOL_INPUT_FILE_PATH:-}"
|
||||||
|
case "$FILE_PATH" in
|
||||||
|
*/AGENTS.md|*/check-rules.sh|*/crush.json|*/hooks/*)
|
||||||
|
if [ "$TOOL" = "write" ] || [ "$TOOL" = "edit" ] || [ "$TOOL" = "multiedit" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Check for active ticket
|
||||||
|
if [ -f "$TICKET_FILE" ] && [ -s "$TICKET_FILE" ]; then
|
||||||
|
TICKET=$(cat "$TICKET_FILE")
|
||||||
|
printf '{"context":"Active ticket: %s"}\n' "$TICKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No active ticket — block
|
||||||
|
cat >&2 <<'MSG'
|
||||||
|
TICKET GATE: No active ticket set.
|
||||||
|
|
||||||
|
This project requires ticket-governed work (AGENTS.md Agent Authority).
|
||||||
|
Before modifying systems or code, set the active ticket:
|
||||||
|
|
||||||
|
echo '#NNN' > .crush/active-ticket
|
||||||
|
|
||||||
|
If no ticket exists yet, create one first (redmine-cli create), then set it.
|
||||||
|
Clear the ticket when work is complete:
|
||||||
|
|
||||||
|
> .crush/active-ticket
|
||||||
|
MSG
|
||||||
|
exit 2
|
||||||
Reference in New Issue
Block a user