refactor: go harness-agnostic — drop Crush hooks, enforce Discourse SoR

Apply answers to questions-v1.md (Q1–Q5):

- Q4: Remove crush.json and hooks/ entirely. All enforcement is now portable
  via git hooks (pre-commit/pre-push) + check-rules.sh + AGENTS.md prose.
  Works under Crush, OpenWebUI, Hermes, or any agent framework.
- Q5: Remove docs/JOURNAL.md. Redmine is the system of record for work;
  Discourse for docs. JOURNAL.md was a stopgap.
- Q3: Add mandatory Discourse pointer-header check to check-rules.sh. Any
  non-exempt tracked .md without a Discourse URL FAILs. All projects, no
  exceptions.
- Q2: Reference real CLI container invocation paths
  (KNEL-AIMiddleware/{redmine,discourse}-cli/) in AGENTS.md instead of the
  missing bin/ shortcuts.
- Q1: Note tea + docker login are preconfigured on TSYS workstations.

Also: make test default is now no-op pass so the template self-validates;
make validate now passes clean on the repo itself (17 PASS / 0 FAIL).

💘 Generated with Crush

Assisted-by: Crush via Crush <crush@charm.land>
This commit is contained in:
2026-08-07 12:04:04 -05:00
parent 374288a105
commit 9dc85ba41e
15 changed files with 121 additions and 268 deletions
+36 -12
View File
@@ -107,15 +107,17 @@ fi
# 3. Required-files manifest — the files every project using this template owns.
# ----------------------------------------------------------------------------
$RULE_VERBOSE && log_step "Required files"
REQUIRED_FILES="AGENTS.md STATUS.md questions-v1.md .env.example Makefile crush.json scripts/check-rules.sh scripts/setup-hooks.sh"
REQUIRED_FILES="AGENTS.md STATUS.md questions-v1.md .env.example Makefile scripts/check-rules.sh scripts/setup-hooks.sh"
REQUIRED_FILES="$REQUIRED_FILES ${PROJECT_REQUIRED_FILES:-}"
for f in $REQUIRED_FILES; do
if [ -f "$f" ]; then check "$f exists" "pass"; else check "$f MISSING" "fail"; fi
done
# ----------------------------------------------------------------------------
# 4. Doc freshness — STATUS.md touched today; JOURNAL.md has today's entry.
# Warnings (not failures): staleness is a signal, not a break.
# 4. Doc freshness — STATUS.md touched today.
# Warning (not failure): staleness is a signal, not a break.
# Redmine is the system of record for work; Discourse for docs. STATUS.md is
# a scratchpad only — see BASELINE-PROMPT.md §3, §8.
# ----------------------------------------------------------------------------
$RULE_VERBOSE && log_step "Doc freshness"
if [ -f STATUS.md ]; then
@@ -129,20 +131,42 @@ else
check "STATUS.md MISSING" "fail"
fi
if [ -f docs/JOURNAL.md ]; then
LAST_ENTRY="$(grep -oE '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' docs/JOURNAL.md | tail -1 | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' || echo unknown)"
if [ "$LAST_ENTRY" = "$TODAY" ]; then
check "JOURNAL.md has an entry for today ($LAST_ENTRY)" "pass"
else
check "JOURNAL.md has no entry for today (last: $LAST_ENTRY) — add one" "warn"
# ----------------------------------------------------------------------------
# 4b. Discourse pointer-header policy (MANDATORY).
# Discourse is the system of record for documentation. In-repo .md files are
# stubs that point to a Discourse topic URL. Operational files exempt.
# Override exemptions via PROJECT_DOC_EXEMPT (space-separated globs of
# basenames) and the Discourse host via PROJECT_DISCOURSE_HOST.
# ----------------------------------------------------------------------------
$RULE_VERBOSE && log_step "Discourse pointer-header"
DISCOURSE_HOST="${PROJECT_DISCOURSE_HOST:-community.turnsys.com}"
DOC_EXEMPT="${PROJECT_DOC_EXEMPT:-AGENTS.md STATUS.md WORKING.md README.md LICENSE .env.example questions-v*.md BASELINE-PROMPT.md PATTERNS.md}"
POINTER_MISSING=0
while IFS= read -r -d '' f; do
base="$(basename "$f")"
exempt=false
for pat in $DOC_EXEMPT; do
# shellcheck disable=SC2254
case "$base" in $pat) exempt=true; break ;; esac
done
[ "$exempt" = true ] && continue
if ! grep -qF "$DISCOURSE_HOST" "$f" 2>/dev/null; then
if [ "$POINTER_MISSING" -eq 0 ]; then
$RULE_VERBOSE && printf ' %s\n' "Missing $DISCOURSE_HOST URL in:"
fi
POINTER_MISSING=$((POINTER_MISSING + 1))
$RULE_VERBOSE && printf ' %s\n' "$f"
fi
done < <(find . -path ./.git -prune -o -path ./.tmp -prune -o -name '*.md' -print0 2>/dev/null)
if [ "$POINTER_MISSING" -eq 0 ]; then
check "All non-exempt .md cite Discourse ($DISCOURSE_HOST)" "pass"
else
check "docs/JOURNAL.md MISSING" "fail"
check "$POINTER_MISSING .md file(s) missing Discourse pointer (see BASELINE-PROMPT.md §3)" "fail"
fi
# ----------------------------------------------------------------------------
# 5. Git state — uncommitted changes are a warning (not a hard failure here;
# the pre-push hook and audit-before-git crush hook harden this where it matters).
# 5. Git state — uncommitted changes are a warning (the pre-push hook hardens
# this where it matters).
# ----------------------------------------------------------------------------
$RULE_VERBOSE && log_step "Git state"
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
+1 -1
View File
@@ -21,7 +21,7 @@ cd "$REPO_ROOT"
SIZE_LIMIT="${GARDEN_MD_LINE_LIMIT:-300}"
# Operational files that legitimately live next to code, not in Discourse.
EXEMPT_FILES="${GARDEN_EXEMPT:-AGENTS.md STATUS.md WORKING.md questions-v.*.md docs/JOURNAL.md PATTERNS.md BASELINE-PROMPT.md README.md}"
EXEMPT_FILES="${GARDEN_EXEMPT:-AGENTS.md STATUS.md WORKING.md questions-v.*.md PATTERNS.md BASELINE-PROMPT.md README.md}"
log_step "Gardening report for $REPO_ROOT"
+4 -4
View File
@@ -1,17 +1,17 @@
#!/usr/bin/env bash
# pre-commit — fast rule audit (< 1s typical).
# Hot-path bypass: commits that ONLY touch STATUS.md / JOURNAL.md / WORKING.md
# skip the audit so frequent status/journal commits stay frictionless.
# Hot-path bypass: commits that ONLY touch STATUS.md / WORKING.md skip the
# audit so frequent status/task commits stay frictionless.
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
CHANGED="$(git diff --cached --name-only)"
HOT_PATHS="$(printf '%s\n' "$CHANGED" | grep -vE '^(STATUS.md|docs/JOURNAL.md|WORKING.md)$' || true)"
HOT_PATHS="$(printf '%s\n' "$CHANGED" | grep -vE '^(STATUS.md|WORKING.md)$' || true)"
if [ -z "$HOT_PATHS" ]; then
echo "hot-path files only (STATUS/JOURNAL/WORKING) — skipping rule audit"
echo "hot-path files only (STATUS/WORKING) — skipping rule audit"
exit 0
fi
+4 -3
View File
@@ -33,9 +33,10 @@ cat <<EOF
Git hooks installed. The following now run automatically:
pre-commit fast rule audit (shellcheck, image pinning, required files,
doc freshness, WORKING.md completion, hygiene).
Hot-path bypass for STATUS.md / JOURNAL.md / WORKING.md.
pre-commit fast rule audit (shellcheck, image pinning, container naming,
required files, doc freshness, Discourse pointers, WORKING.md
completion, hygiene).
Hot-path bypass for STATUS.md / WORKING.md.
pre-push full rule audit (includes make test) + clean-working-tree gate.
Bypass either with \`git commit --no-verify\` / \`git push --no-verify\`