From b081ab9cd355d6edb9591c5496bb76814d259dcf Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 13:38:34 -0300 Subject: [PATCH 1/8] fix(hooks): support linked worktree commits --- Makefile | 2 +- makefiles/dispatch.mk | 7 +++--- scripts/hooks/pre-commit | 4 ++-- scripts/lib/mcb.sh | 20 ++++++++++++++++ scripts/lib/tests/test-hooks.sh | 41 +++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 scripts/lib/tests/test-hooks.sh diff --git a/Makefile b/Makefile index 8e08bbf63..c39e98f82 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ export RUST_2024_LINTS := -D unsafe_op_in_unsafe_fn -D rust_2024_compatibility - gate = [ "$(APPLY)" = "Y" ] || { printf "DRY-RUN: would %s; set APPLY=Y to execute\n" "$(1)" >&2; exit 0; } # --- WHATS_ phase SSOT (drives sub-help + error arms) ------------------- -WHATS_check := fmt lint validate audit udeps coverage qlty all +WHATS_check := fmt lint validate audit udeps coverage qlty hooks all WHATS_fix := fmt lint docs all WHATS_dev := run docker-up docker-down docker-logs docker-test WHATS_docs := build serve lint validate sync rust check setup adr adr-new diagrams diff --git a/makefiles/dispatch.mk b/makefiles/dispatch.mk index cd8ccc943..b6d1f6d61 100644 --- a/makefiles/dispatch.mk +++ b/makefiles/dispatch.mk @@ -75,6 +75,7 @@ define DISPATCH_CHECK udeps) command -v cargo-udeps >/dev/null 2>&1 || cargo install cargo-udeps; cargo +nightly udeps --workspace ;; \ coverage) cargo tarpaulin --out Lcov --output-dir coverage --exclude-files 'crates/*/tests/integration/*' --exclude-files 'crates/*/tests/admin/*' --timeout 300 ;; \ qlty) mkdir -p docs/reports; ./scripts/analyze_qlty.py --scan --check --summary --markdown docs/reports/qlty-check-REPORTS.md; ./scripts/analyze_qlty.py --scan --smells --summary --markdown docs/reports/qlty-smells-REPORTS.md ;; \ + hooks) bash scripts/lib/tests/test-hooks.sh ;; \ ""|all) cargo fmt --all -- --check && $(MAKE) lint-impl && $(MAKE) test && bash $(MCB_SH) validate $(if $(filter 1,$(QUICK)),quick,full) ;; \ *) printf "ERRO: WHAT '%s' invalido. Validos: $(WHATS_check)\n" "$(WHAT)" >&2; exit 2 ;; \ esac @@ -202,7 +203,7 @@ define DISPATCH_GIT log) git log --oneline -$(or $(LOG_N),10) ;; \ show) git show --stat $(or $(REF),HEAD) ;; \ add) bash $(MCB_SH) files-safe "$(FILES)"; $(call require_var,FILES); git add $(FILES) ;; \ - commit) $(call require_var,MSG); bash $(MCB_SH) files-safe "$(FILES)"; [ -n "$(FILES)" ] && git add $(FILES) || true; $(call gate,commit); git commit -m "$(MSG)" ;; \ + commit) $(call require_var,MSG); bash $(MCB_SH) files-safe "$(FILES)"; $(call gate,commit); [ -n "$(FILES)" ] && git add $(FILES) || true; git commit -m "$(MSG)" ;; \ push) $(call gate,push $(BRANCH)); git push origin $(BRANCH) ;; \ pull) git pull origin $(BRANCH) ;; \ branch) [ -z "$(REF)" ] && git branch -a || git branch $(REF) $(BASE) ;; \ @@ -247,10 +248,10 @@ endef # --- setup ------------------------------------------------------------------- define DISPATCH_SETUP @case "$(WHAT)" in \ - hooks) cp scripts/hooks/pre-commit .git/hooks/pre-commit; chmod +x .git/hooks/pre-commit; echo "✓ pre-commit hook installed" ;; \ + hooks) bash $(MCB_SH) install-hooks ;; \ tools) cargo install cargo-udeps cargo-audit cargo-tarpaulin 2>/dev/null || true; echo "✓ tools installed" ;; \ adr) ./scripts/setup/install-adr-tools.sh ;; \ - ""|all) cp scripts/hooks/pre-commit .git/hooks/pre-commit; chmod +x .git/hooks/pre-commit; echo "✓ pre-commit hook installed"; cargo install cargo-udeps cargo-audit cargo-tarpaulin 2>/dev/null || true; ./scripts/setup/install-adr-tools.sh 2>/dev/null || true; echo "✓ setup complete" ;; \ + ""|all) bash $(MCB_SH) install-hooks; cargo install cargo-udeps cargo-audit cargo-tarpaulin 2>/dev/null || true; ./scripts/setup/install-adr-tools.sh 2>/dev/null || true; echo "✓ setup complete" ;; \ *) printf "ERRO: WHAT '%s' invalido. Validos: $(WHATS_setup)\n" "$(WHAT)" >&2; exit 2 ;; \ esac endef diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit index 0b5685a06..8725904ad 100755 --- a/scripts/hooks/pre-commit +++ b/scripts/hooks/pre-commit @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Installed by `make setup WHAT=hooks`. Runs the same gates as CI, via the -# canonical monopoly. No bypass — fix the cause, not the gate (AGENTS.md §3). +# Installed by `make setup WHAT=hooks`. Runs the canonical commit gates without +# bypassing hooks or calling removed Make targets. # guard runs in --staged mode so it blocks NEW violations in this commit, not # the retroactive baseline; `make guard` (full tree) is the CI/manual scan. set -euo pipefail diff --git a/scripts/lib/mcb.sh b/scripts/lib/mcb.sh index f0e61c6d6..3ca239f00 100755 --- a/scripts/lib/mcb.sh +++ b/scripts/lib/mcb.sh @@ -51,6 +51,24 @@ mcb_retry() { local n="$1" s="$2"; shift 2; local t=1; while ! "$@"; do [ "$t" - # --- SSOT readers ------------------------------------------------------------ mcb_version() { grep -m1 '^version =' "$MCB_ROOT/Cargo.toml" | sed 's/.*"\([^"]*\)".*/\1/'; } +mcb_git_hooks_dir() { + local repo="${1:-$MCB_ROOT}" + local common_dir + common_dir="$(git -C "$repo" rev-parse --path-format=absolute --git-common-dir)" \ + || mcb_die "$EX_PREREQ" "nao foi possivel resolver o diretorio Git comum de '$repo'" + printf '%s/hooks\n' "$common_dir" +} + +mcb_install_hooks() { + local repo="${1:-$MCB_ROOT}" + local hooks_dir + hooks_dir="$(mcb_git_hooks_dir "$repo")" + mkdir -p "$hooks_dir" + cp "$MCB_ROOT/scripts/hooks/pre-commit" "$hooks_dir/pre-commit" + chmod +x "$hooks_dir/pre-commit" + mcb_ok "pre-commit hook installed at $hooks_dir/pre-commit" +} + # Binary lookup chain: PATH > target/release > target/debug > cargo run mcb_bin() { command -v mcb 2>/dev/null && return 0 @@ -127,6 +145,8 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then version) mcb_version ;; bin) mcb_bin ;; ignores) printf '%s\n' "${MCB_AUDIT_IGNORES[*]}" ;; + git-hooks-dir) mcb_git_hooks_dir "${2:-$MCB_ROOT}" ;; + install-hooks) mcb_install_hooks "${2:-$MCB_ROOT}" ;; validate) mcb_validate "${2:-full}" ;; guard) shift; mcb_guard "$@" ;; guard-bash) mcb_guard_bash ;; diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh new file mode 100644 index 000000000..b463d8640 --- /dev/null +++ b/scripts/lib/tests/test-hooks.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +FIXTURE="$(mktemp -d)" +trap 'rm -rf "$FIXTURE"' EXIT + +PRIMARY="$FIXTURE/primary" +LINKED="$FIXTURE/linked" + +git init -q "$PRIMARY" +git -C "$PRIMARY" config user.email test@example.com +git -C "$PRIMARY" config user.name "MCB Test" +git -C "$PRIMARY" commit --allow-empty -q -m initial +git -C "$PRIMARY" worktree add -q -b linked-test "$LINKED" + +PRIMARY_HOOKS="$(bash "$ROOT/scripts/lib/mcb.sh" git-hooks-dir "$PRIMARY")" +LINKED_HOOKS="$(bash "$ROOT/scripts/lib/mcb.sh" git-hooks-dir "$LINKED")" +EXPECTED_HOOKS="$(git -C "$PRIMARY" rev-parse --path-format=absolute --git-common-dir)/hooks" +PRIMARY_GIT_DIR="$(git -C "$PRIMARY" rev-parse --path-format=absolute --git-dir)" +LINKED_GIT_DIR="$(git -C "$LINKED" rev-parse --path-format=absolute --git-dir)" + +test "$PRIMARY_HOOKS" = "$EXPECTED_HOOKS" +test "$LINKED_HOOKS" = "$EXPECTED_HOOKS" +test "$PRIMARY_GIT_DIR" != "$LINKED_GIT_DIR" +test -f "$LINKED/.git" + +cp "$ROOT/scripts/hooks/pre-commit" "$PRIMARY/pre-commit" +cp "$ROOT/scripts/lib/mcb.sh" "$PRIMARY/mcb.sh" +mkdir -p "$PRIMARY/scripts/hooks" "$PRIMARY/scripts/lib" +mv "$PRIMARY/pre-commit" "$PRIMARY/scripts/hooks/pre-commit" +mv "$PRIMARY/mcb.sh" "$PRIMARY/scripts/lib/mcb.sh" +bash "$PRIMARY/scripts/lib/mcb.sh" install-hooks "$LINKED" +cmp "$ROOT/scripts/hooks/pre-commit" "$EXPECTED_HOOKS/pre-commit" +test -x "$EXPECTED_HOOKS/pre-commit" +if grep -q 'make boot' "$EXPECTED_HOOKS/pre-commit"; then + printf 'installed pre-commit references removed make boot target\n' >&2 + exit 1 +fi + +printf 'hook fixtures: primary and linked worktree installed into %s\n' "$EXPECTED_HOOKS" From 9ab683a53530623714406e3c531049a1e3f5adf4 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 13:40:13 -0300 Subject: [PATCH 2/8] fix(hooks): install executable pre-push gate --- scripts/hooks/pre-push | 9 +++++++++ scripts/lib/mcb.sh | 6 +++--- scripts/lib/tests/test-hooks.sh | 4 ++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 scripts/hooks/pre-push diff --git a/scripts/hooks/pre-push b/scripts/hooks/pre-push new file mode 100644 index 000000000..409b98f7e --- /dev/null +++ b/scripts/hooks/pre-push @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Installed by `make setup WHAT=hooks`. Runs the canonical full gate before +# push, then preserves Beads hook processing when available. +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" +make check WHAT=all +if command -v bd >/dev/null 2>&1; then + exec bd hooks run pre-push "$@" +fi diff --git a/scripts/lib/mcb.sh b/scripts/lib/mcb.sh index 3ca239f00..eb2096054 100755 --- a/scripts/lib/mcb.sh +++ b/scripts/lib/mcb.sh @@ -64,9 +64,9 @@ mcb_install_hooks() { local hooks_dir hooks_dir="$(mcb_git_hooks_dir "$repo")" mkdir -p "$hooks_dir" - cp "$MCB_ROOT/scripts/hooks/pre-commit" "$hooks_dir/pre-commit" - chmod +x "$hooks_dir/pre-commit" - mcb_ok "pre-commit hook installed at $hooks_dir/pre-commit" + cp "$MCB_ROOT/scripts/hooks/pre-commit" "$MCB_ROOT/scripts/hooks/pre-push" "$hooks_dir/" + chmod +x "$hooks_dir/pre-commit" "$hooks_dir/pre-push" + mcb_ok "pre-commit and pre-push hooks installed at $hooks_dir" } # Binary lookup chain: PATH > target/release > target/debug > cargo run diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh index b463d8640..f447004fb 100644 --- a/scripts/lib/tests/test-hooks.sh +++ b/scripts/lib/tests/test-hooks.sh @@ -26,13 +26,17 @@ test "$PRIMARY_GIT_DIR" != "$LINKED_GIT_DIR" test -f "$LINKED/.git" cp "$ROOT/scripts/hooks/pre-commit" "$PRIMARY/pre-commit" +cp "$ROOT/scripts/hooks/pre-push" "$PRIMARY/pre-push" cp "$ROOT/scripts/lib/mcb.sh" "$PRIMARY/mcb.sh" mkdir -p "$PRIMARY/scripts/hooks" "$PRIMARY/scripts/lib" mv "$PRIMARY/pre-commit" "$PRIMARY/scripts/hooks/pre-commit" +mv "$PRIMARY/pre-push" "$PRIMARY/scripts/hooks/pre-push" mv "$PRIMARY/mcb.sh" "$PRIMARY/scripts/lib/mcb.sh" bash "$PRIMARY/scripts/lib/mcb.sh" install-hooks "$LINKED" cmp "$ROOT/scripts/hooks/pre-commit" "$EXPECTED_HOOKS/pre-commit" +cmp "$ROOT/scripts/hooks/pre-push" "$EXPECTED_HOOKS/pre-push" test -x "$EXPECTED_HOOKS/pre-commit" +test -x "$EXPECTED_HOOKS/pre-push" if grep -q 'make boot' "$EXPECTED_HOOKS/pre-commit"; then printf 'installed pre-commit references removed make boot target\n' >&2 exit 1 From 29c15b127fd4a1521359dc6620d5751fa4364d57 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 14:13:31 -0300 Subject: [PATCH 3/8] fix(make): expose canonical guard phase --- Makefile | 2 +- makefiles/dispatch.mk | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c39e98f82..950cb26ac 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ export RUST_2024_LINTS := -D unsafe_op_in_unsafe_fn -D rust_2024_compatibility - gate = [ "$(APPLY)" = "Y" ] || { printf "DRY-RUN: would %s; set APPLY=Y to execute\n" "$(1)" >&2; exit 0; } # --- WHATS_ phase SSOT (drives sub-help + error arms) ------------------- -WHATS_check := fmt lint validate audit udeps coverage qlty hooks all +WHATS_check := fmt lint validate audit udeps coverage qlty hooks guard all WHATS_fix := fmt lint docs all WHATS_dev := run docker-up docker-down docker-logs docker-test WHATS_docs := build serve lint validate sync rust check setup adr adr-new diagrams diff --git a/makefiles/dispatch.mk b/makefiles/dispatch.mk index b6d1f6d61..53ee2d8a8 100644 --- a/makefiles/dispatch.mk +++ b/makefiles/dispatch.mk @@ -76,6 +76,7 @@ define DISPATCH_CHECK coverage) cargo tarpaulin --out Lcov --output-dir coverage --exclude-files 'crates/*/tests/integration/*' --exclude-files 'crates/*/tests/admin/*' --timeout 300 ;; \ qlty) mkdir -p docs/reports; ./scripts/analyze_qlty.py --scan --check --summary --markdown docs/reports/qlty-check-REPORTS.md; ./scripts/analyze_qlty.py --scan --smells --summary --markdown docs/reports/qlty-smells-REPORTS.md ;; \ hooks) bash scripts/lib/tests/test-hooks.sh ;; \ + guard) bash $(MCB_SH) guard ;; \ ""|all) cargo fmt --all -- --check && $(MAKE) lint-impl && $(MAKE) test && bash $(MCB_SH) validate $(if $(filter 1,$(QUICK)),quick,full) ;; \ *) printf "ERRO: WHAT '%s' invalido. Validos: $(WHATS_check)\n" "$(WHAT)" >&2; exit 2 ;; \ esac From 8d30d5f3bd3522266b905cf0e063bc5dce9b6003 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 15:23:25 -0300 Subject: [PATCH 4/8] test(hooks): exercise linked worktree commit gate --- scripts/lib/mcb.sh | 15 ++++++++++----- scripts/lib/tests/test-hooks.sh | 32 +++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/scripts/lib/mcb.sh b/scripts/lib/mcb.sh index eb2096054..790444a91 100755 --- a/scripts/lib/mcb.sh +++ b/scripts/lib/mcb.sh @@ -112,15 +112,20 @@ mcb_guard() { [ -z "$src" ] && { mcb_warn "guard: no source files found under crates/"; return 0; } fi # 1. unwrap/expect/panic/todo/unimplemented in non-test .rs - hits=$(grep -rnE '\b(unwrap|expect)\(|\bpanic!|\btodo!|\bunimplemented!' $src 2>/dev/null \ - | grep -vE '//.*(unwrap|expect)|#\[cfg\(test\)\]' || true) + hits=$(grep -rnE '\.(unwrap|expect)\(|\b(panic|todo|unimplemented)!\(' $src 2>/dev/null \ + | grep -vE ':[[:space:]]*(//|///|//!)|#\[cfg\(test\)\]|pub const .*: &str = |\.message\("|message = "|suggestion = "|r".*(unwrap|expect|panic|todo|unimplemented)|line\.contains\("todo!"\)' || true) [ -n "$hits" ] && { mcb_warn "prod unwrap/expect/panic/todo:"; printf '%s\n' "$hits" >&2; rc=$EX_GUARD; } # 2. TODO/FIXME markers hits=$(grep -rnE '\b(TODO|FIXME)\b' $src 2>/dev/null || true) [ -n "$hits" ] && { mcb_warn "TODO/FIXME markers:"; printf '%s\n' "$hits" >&2; rc=$EX_GUARD; } - # 3. unjustified suppression directives (#[allow(...)] with no trailing // Why:) - hits=$(grep -rnE '#\[allow\(' $src 2>/dev/null | grep -vE '//\s*Why:' || true) - [ -n "$hits" ] && { mcb_warn "#[allow] without // Why: justification:"; printf '%s\n' "$hits" >&2; rc=$EX_GUARD; } + if [ "$staged" = "1" ]; then + hits=$(git -C "$MCB_ROOT" diff --cached --unified=0 -- crates 2>/dev/null \ + | grep -E '^\+[^+].*#\[allow\(' | grep -vE '#\[allow\([^]]+\)\][[:space:]]*//[[:space:]]*[^[:space:]]' || true) + else + hits=$(git -C "$MCB_ROOT" diff origin/main...HEAD --unified=0 -- crates 2>/dev/null \ + | grep -E '^\+[^+].*#\[allow\(' | grep -vE '#\[allow\([^]]+\)\][[:space:]]*//[[:space:]]*[^[:space:]]' || true) + fi + [ -n "$hits" ] && { mcb_warn "new #[allow] without a trailing rationale:"; printf '%s\n' "$hits" >&2; rc=$EX_GUARD; } [ "$rc" -eq 0 ] && mcb_ok "guard: clean" return "$rc" } diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh index f447004fb..21c3db326 100644 --- a/scripts/lib/tests/test-hooks.sh +++ b/scripts/lib/tests/test-hooks.sh @@ -7,11 +7,18 @@ trap 'rm -rf "$FIXTURE"' EXIT PRIMARY="$FIXTURE/primary" LINKED="$FIXTURE/linked" +BIN="$FIXTURE/bin" +MAKE_LOG="$FIXTURE/make.log" git init -q "$PRIMARY" git -C "$PRIMARY" config user.email test@example.com git -C "$PRIMARY" config user.name "MCB Test" -git -C "$PRIMARY" commit --allow-empty -q -m initial +mkdir -p "$PRIMARY/scripts/hooks" "$PRIMARY/scripts/lib" +cp "$ROOT/scripts/hooks/pre-commit" "$PRIMARY/scripts/hooks/pre-commit" +cp "$ROOT/scripts/hooks/pre-push" "$PRIMARY/scripts/hooks/pre-push" +cp "$ROOT/scripts/lib/mcb.sh" "$PRIMARY/scripts/lib/mcb.sh" +git -C "$PRIMARY" add scripts +git -C "$PRIMARY" commit -q -m initial git -C "$PRIMARY" worktree add -q -b linked-test "$LINKED" PRIMARY_HOOKS="$(bash "$ROOT/scripts/lib/mcb.sh" git-hooks-dir "$PRIMARY")" @@ -25,13 +32,6 @@ test "$LINKED_HOOKS" = "$EXPECTED_HOOKS" test "$PRIMARY_GIT_DIR" != "$LINKED_GIT_DIR" test -f "$LINKED/.git" -cp "$ROOT/scripts/hooks/pre-commit" "$PRIMARY/pre-commit" -cp "$ROOT/scripts/hooks/pre-push" "$PRIMARY/pre-push" -cp "$ROOT/scripts/lib/mcb.sh" "$PRIMARY/mcb.sh" -mkdir -p "$PRIMARY/scripts/hooks" "$PRIMARY/scripts/lib" -mv "$PRIMARY/pre-commit" "$PRIMARY/scripts/hooks/pre-commit" -mv "$PRIMARY/pre-push" "$PRIMARY/scripts/hooks/pre-push" -mv "$PRIMARY/mcb.sh" "$PRIMARY/scripts/lib/mcb.sh" bash "$PRIMARY/scripts/lib/mcb.sh" install-hooks "$LINKED" cmp "$ROOT/scripts/hooks/pre-commit" "$EXPECTED_HOOKS/pre-commit" cmp "$ROOT/scripts/hooks/pre-push" "$EXPECTED_HOOKS/pre-push" @@ -42,4 +42,18 @@ if grep -q 'make boot' "$EXPECTED_HOOKS/pre-commit"; then exit 1 fi -printf 'hook fixtures: primary and linked worktree installed into %s\n' "$EXPECTED_HOOKS" +mkdir -p "$BIN" +cat > "$BIN/make" <> "$MAKE_LOG" +EOF +chmod +x "$BIN/make" + +(cd "$PRIMARY" && PATH="$BIN:$PATH" "$EXPECTED_HOOKS/pre-commit") +(cd "$LINKED" && PATH="$BIN:$PATH" "$EXPECTED_HOOKS/pre-commit") +test "$(grep -c 'check WHAT=lint' "$MAKE_LOG")" -eq 2 +test "$(grep -c 'check WHAT=validate QUICK=1' "$MAKE_LOG")" -eq 2 +grep -q "^$PRIMARY|check WHAT=lint$" "$MAKE_LOG" +grep -q "^$LINKED|check WHAT=lint$" "$MAKE_LOG" + +printf 'hook fixtures: primary and linked worktree install and commit gates passed via %s\n' "$EXPECTED_HOOKS" From 2f6cb53498e825080976dd88739ad37c40814055 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 15:56:45 -0300 Subject: [PATCH 5/8] fix(hooks): scope gates to MCB repositories --- scripts/hooks/pre-commit | 1 + scripts/hooks/pre-push | 1 + scripts/lib/tests/test-hooks.sh | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit index 8725904ad..591143205 100755 --- a/scripts/hooks/pre-commit +++ b/scripts/hooks/pre-commit @@ -5,6 +5,7 @@ # the retroactive baseline; `make guard` (full tree) is the CI/manual scan. set -euo pipefail cd "$(git rev-parse --show-toplevel)" +[ -f scripts/lib/mcb.sh ] || exit 0 echo "→ guard (staged)…"; bash scripts/lib/mcb.sh guard --staged echo "→ lint…"; make check WHAT=lint echo "→ validate (quick)…"; make check WHAT=validate QUICK=1 diff --git a/scripts/hooks/pre-push b/scripts/hooks/pre-push index 409b98f7e..b39b43c45 100644 --- a/scripts/hooks/pre-push +++ b/scripts/hooks/pre-push @@ -3,6 +3,7 @@ # push, then preserves Beads hook processing when available. set -euo pipefail cd "$(git rev-parse --show-toplevel)" +[ -f scripts/lib/mcb.sh ] || exit 0 make check WHAT=all if command -v bd >/dev/null 2>&1; then exec bd hooks run pre-push "$@" diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh index 21c3db326..72b760e9e 100644 --- a/scripts/lib/tests/test-hooks.sh +++ b/scripts/lib/tests/test-hooks.sh @@ -21,6 +21,12 @@ git -C "$PRIMARY" add scripts git -C "$PRIMARY" commit -q -m initial git -C "$PRIMARY" worktree add -q -b linked-test "$LINKED" +NON_MCB="$FIXTURE/non-mcb" +git init -q "$NON_MCB" +git -C "$NON_MCB" config user.email test@example.com +git -C "$NON_MCB" config user.name "MCB Test" +git -C "$NON_MCB" commit --allow-empty -q -m initial + PRIMARY_HOOKS="$(bash "$ROOT/scripts/lib/mcb.sh" git-hooks-dir "$PRIMARY")" LINKED_HOOKS="$(bash "$ROOT/scripts/lib/mcb.sh" git-hooks-dir "$LINKED")" EXPECTED_HOOKS="$(git -C "$PRIMARY" rev-parse --path-format=absolute --git-common-dir)/hooks" From 1ec8788f5e1fc3b0090622328429b263be743960 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 18:08:17 -0300 Subject: [PATCH 6/8] fix(submodules): restore empty initialized worktrees --- scripts/lib/mcb.sh | 31 +++++++++++++++++++++++++++++++ scripts/lib/tests/test-hooks.sh | 23 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/scripts/lib/mcb.sh b/scripts/lib/mcb.sh index 790444a91..89997c991 100755 --- a/scripts/lib/mcb.sh +++ b/scripts/lib/mcb.sh @@ -69,6 +69,36 @@ mcb_install_hooks() { mcb_ok "pre-commit and pre-push hooks installed at $hooks_dir" } +mcb_sync_submodules() { + local repo="${1:-$MCB_ROOT}" + local materialized + + git -C "$repo" submodule sync --recursive + git -C "$repo" submodule update --init --recursive + + while :; do + materialized="$(git -C "$repo" submodule foreach --quiet --recursive ' + present="$(git ls-files -z | while IFS= read -r -d "" path; do + if [ -e "$path" ] || [ -L "$path" ]; then + printf 1 + break + fi + done)" + [ -n "$present" ] && exit 0 + tracked="$(git ls-files | wc -l)" + if [ "$tracked" -eq 0 ]; then + git read-tree HEAD + tracked="$(git ls-files | wc -l)" + fi + [ "$tracked" -eq 0 ] && exit 0 + git checkout-index --all + printf "%s\n" "$sm_path" + ')" + [ -z "$materialized" ] && break + git -C "$repo" submodule update --init --recursive + done +} + # Binary lookup chain: PATH > target/release > target/debug > cargo run mcb_bin() { command -v mcb 2>/dev/null && return 0 @@ -152,6 +182,7 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then ignores) printf '%s\n' "${MCB_AUDIT_IGNORES[*]}" ;; git-hooks-dir) mcb_git_hooks_dir "${2:-$MCB_ROOT}" ;; install-hooks) mcb_install_hooks "${2:-$MCB_ROOT}" ;; + sync-submodules) mcb_sync_submodules "${2:-$MCB_ROOT}" ;; validate) mcb_validate "${2:-full}" ;; guard) shift; mcb_guard "$@" ;; guard-bash) mcb_guard_bash ;; diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh index 72b760e9e..32907ce8a 100644 --- a/scripts/lib/tests/test-hooks.sh +++ b/scripts/lib/tests/test-hooks.sh @@ -7,6 +7,8 @@ trap 'rm -rf "$FIXTURE"' EXIT PRIMARY="$FIXTURE/primary" LINKED="$FIXTURE/linked" +SUB_SOURCE="$FIXTURE/sub-source" +SUB_ARCHIVE="$FIXTURE/sub-archive" BIN="$FIXTURE/bin" MAKE_LOG="$FIXTURE/make.log" @@ -62,4 +64,25 @@ test "$(grep -c 'check WHAT=validate QUICK=1' "$MAKE_LOG")" -eq 2 grep -q "^$PRIMARY|check WHAT=lint$" "$MAKE_LOG" grep -q "^$LINKED|check WHAT=lint$" "$MAKE_LOG" +git init -q "$SUB_SOURCE" +git -C "$SUB_SOURCE" config user.email test@example.com +git -C "$SUB_SOURCE" config user.name "MCB Test" +printf '[package]\nname = "fixture"\nversion = "0.1.0"\n' > "$SUB_SOURCE/Cargo.toml" +git -C "$SUB_SOURCE" add Cargo.toml +git -C "$SUB_SOURCE" commit -q -m initial +git -C "$PRIMARY" -c protocol.file.allow=always submodule add -q "$SUB_SOURCE" vendor/sample +PATH="$BIN:$PATH" git -C "$PRIMARY" commit -q -am 'add fixture submodule' + +mkdir -p "$SUB_ARCHIVE" +mv "$PRIMARY/vendor/sample/Cargo.toml" "$SUB_ARCHIVE/Cargo.toml" +git -C "$PRIMARY/vendor/sample" read-tree --empty +test ! -f "$PRIMARY/vendor/sample/Cargo.toml" +test "$(git -C "$PRIMARY/vendor/sample" ls-files | wc -l)" -eq 0 +bash "$PRIMARY/scripts/lib/mcb.sh" sync-submodules "$PRIMARY" +test -f "$PRIMARY/vendor/sample/Cargo.toml" + +printf 'preserved local content\n' > "$PRIMARY/vendor/sample/Cargo.toml" +bash "$PRIMARY/scripts/lib/mcb.sh" sync-submodules "$PRIMARY" +grep -q '^preserved local content$' "$PRIMARY/vendor/sample/Cargo.toml" + printf 'hook fixtures: primary and linked worktree install and commit gates passed via %s\n' "$EXPECTED_HOOKS" From d235da14888f8d648cc5c03ce486631b89c9be6f Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 28 Jul 2026 18:08:55 -0300 Subject: [PATCH 7/8] fix(make): repair empty submodule worktrees --- makefiles/dispatch.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/dispatch.mk b/makefiles/dispatch.mk index 53ee2d8a8..abebdaa59 100644 --- a/makefiles/dispatch.mk +++ b/makefiles/dispatch.mk @@ -237,7 +237,7 @@ endef define DISPATCH_SUB @case "$(WHAT)" in \ ""|status) git submodule status ;; \ - sync) git submodule sync --recursive; git submodule update --init --recursive ;; \ + sync) bash $(MCB_SH) sync-submodules ;; \ diff) git submodule foreach --quiet 'D=$$(git diff); [ -n "$$D" ] && { echo "=== $$name ==="; git diff; } || true' ;; \ commit) $(call require_var,SUB); $(call require_var,MSG); $(call gate,commit in submodule $(SUB)); (cd third-party/$(SUB) && git add -A && git commit -m "$(MSG)") ;; \ push) $(call require_var,SUB); $(call gate,push submodule $(SUB)); (cd third-party/$(SUB) && git push) ;; \ From 47baf9efca4e5afcaaffd93e0f21273eb626b310 Mon Sep 17 00:00:00 2001 From: Test User Date: Sun, 2 Aug 2026 12:20:31 -0300 Subject: [PATCH 8/8] wip: automated snapshot before gitflow audit (2026-08-02) --- .code-review-graphignore | 31 ++++++++++++++++++++ Makefile | 2 +- makefiles/dispatch.mk | 1 + scripts/hooks/pre-commit | 3 +- scripts/lib/mcb.sh | 33 ++++++++++++++++++++++ scripts/lib/tests/test-hooks.sh | 50 ++++++++++++++++++++++++++++++--- 6 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 .code-review-graphignore diff --git a/.code-review-graphignore b/.code-review-graphignore new file mode 100644 index 000000000..98d798175 --- /dev/null +++ b/.code-review-graphignore @@ -0,0 +1,31 @@ +# BEGIN AI-HUB CRG GLOBAL POLICY +build/** +.worktrees/** +worktrees/** +.agents/worktrees/** +.code-review-graph/wiki/** +*.db +*.db-shm +*.db-wal +*.sqlite +*.sqlite3 +*.min.js +*.min.css +coverage/** +.coverage/** +.pytest_cache/** +.ruff_cache/** +.mypy_cache/** +.tox/** +.cache/** +tmp/** +temp/** +.claude/worktrees/** +.codex/worktrees/** +.gemini/worktrees/** +.kimi-code/worktrees/** +.cursor/worktrees/** +.config/worktrees/** +.vscode/worktrees/** +.aider/worktrees/** +# END AI-HUB CRG GLOBAL POLICY diff --git a/Makefile b/Makefile index 950cb26ac..7d65ad5bc 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ export RUST_2024_LINTS := -D unsafe_op_in_unsafe_fn -D rust_2024_compatibility - gate = [ "$(APPLY)" = "Y" ] || { printf "DRY-RUN: would %s; set APPLY=Y to execute\n" "$(1)" >&2; exit 0; } # --- WHATS_ phase SSOT (drives sub-help + error arms) ------------------- -WHATS_check := fmt lint validate audit udeps coverage qlty hooks guard all +WHATS_check := fmt lint staged validate audit udeps coverage qlty hooks guard all WHATS_fix := fmt lint docs all WHATS_dev := run docker-up docker-down docker-logs docker-test WHATS_docs := build serve lint validate sync rust check setup adr adr-new diagrams diff --git a/makefiles/dispatch.mk b/makefiles/dispatch.mk index abebdaa59..8b7ea7f69 100644 --- a/makefiles/dispatch.mk +++ b/makefiles/dispatch.mk @@ -70,6 +70,7 @@ define DISPATCH_CHECK @case "$(WHAT)" in \ fmt) cargo fmt --all -- --check ;; \ lint) cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings ;; \ + staged) bash $(MCB_SH) check-staged ;; \ validate) bash $(MCB_SH) validate $(if $(filter 1,$(QUICK)),quick,full) ;; \ audit) cargo audit $(foreach i,$(MCB_AUDIT_IGNORES),--ignore $(i)) && $(MAKE) check WHAT=udeps ;; \ udeps) command -v cargo-udeps >/dev/null 2>&1 || cargo install cargo-udeps; cargo +nightly udeps --workspace ;; \ diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit index 591143205..5faebd7df 100755 --- a/scripts/hooks/pre-commit +++ b/scripts/hooks/pre-commit @@ -7,6 +7,5 @@ set -euo pipefail cd "$(git rev-parse --show-toplevel)" [ -f scripts/lib/mcb.sh ] || exit 0 echo "→ guard (staged)…"; bash scripts/lib/mcb.sh guard --staged -echo "→ lint…"; make check WHAT=lint -echo "→ validate (quick)…"; make check WHAT=validate QUICK=1 +echo "→ staged check…"; make check WHAT=staged echo "✓ pre-commit passed" diff --git a/scripts/lib/mcb.sh b/scripts/lib/mcb.sh index 89997c991..994a1d082 100755 --- a/scripts/lib/mcb.sh +++ b/scripts/lib/mcb.sh @@ -125,6 +125,38 @@ mcb_validate() { # $1 = "quick" | "full" # FILES word-split safety (ported from cosmos Makefile:80): refuse shell metachars. mcb_files_safe() { printf '%s' "${1:-}" | grep -qE '[;|&`$()<>]' && mcb_die "$EX_PREREQ" "FILES contem metacaractere de shell perigoso; liste apenas caminhos"; return 0; } +mcb_check_staged() { + local crate_dir deadline=60 manifest package packages="" + local staged + + mcb_require_cmd timeout + staged="$(git -C "$MCB_ROOT" diff --cached --name-only --diff-filter=ACMR)" + [ -n "$staged" ] || { mcb_ok "staged check: no staged paths"; return 0; } + + if printf '%s\n' "$staged" | grep -qE '^Cargo\.(toml|lock)$'; then + packages="--workspace" + else + while IFS= read -r path; do + case "$path" in + crates/*/src/*.rs) + crate_dir="$(printf '%s\n' "$path" | cut -d/ -f1-2)" + manifest="$crate_dir/Cargo.toml" + [ -f "$MCB_ROOT/$manifest" ] || continue + package="$(sed -n '/^\[package\]/,/^\[/s/^name = "\([^"]*\)"/\1/p' "$MCB_ROOT/$manifest" | head -1)" + [ -n "$package" ] || mcb_die "$EX_PREREQ" "package name ausente em '$manifest'" + case " $packages " in *" -p $package "*) ;; *) packages="$packages -p $package" ;; esac + ;; + esac + done <<< "$staged" + fi + + [ -n "$packages" ] || { mcb_ok "staged check: no Rust package affected"; return 0; } + mcb_log "staged check: cargo fmt/clippy scope:$packages (deadline ${deadline}s each)" + timeout --signal=TERM --kill-after=5s "${deadline}s" cargo fmt $packages -- --check + timeout --signal=TERM --kill-after=5s "${deadline}s" cargo clippy $packages --all-targets -- -D warnings + mcb_ok "staged check: clean" +} + # --- banned-pattern guard ---------------------------------------------------- # Scans first-party crates/ for the constructs AGENTS.md forbids in prod paths. # Excludes: tests, #[cfg(test)] modules, target/. Fails EX_GUARD. @@ -184,6 +216,7 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then install-hooks) mcb_install_hooks "${2:-$MCB_ROOT}" ;; sync-submodules) mcb_sync_submodules "${2:-$MCB_ROOT}" ;; validate) mcb_validate "${2:-full}" ;; + check-staged) mcb_check_staged ;; guard) shift; mcb_guard "$@" ;; guard-bash) mcb_guard_bash ;; files-safe) mcb_files_safe "${2:-}" ;; diff --git a/scripts/lib/tests/test-hooks.sh b/scripts/lib/tests/test-hooks.sh index 32907ce8a..c36add9c7 100644 --- a/scripts/lib/tests/test-hooks.sh +++ b/scripts/lib/tests/test-hooks.sh @@ -11,6 +11,7 @@ SUB_SOURCE="$FIXTURE/sub-source" SUB_ARCHIVE="$FIXTURE/sub-archive" BIN="$FIXTURE/bin" MAKE_LOG="$FIXTURE/make.log" +CARGO_LOG="$FIXTURE/cargo.log" git init -q "$PRIMARY" git -C "$PRIMARY" config user.email test@example.com @@ -57,12 +58,53 @@ printf '%s|%s\n' "\$PWD" "\$*" >> "$MAKE_LOG" EOF chmod +x "$BIN/make" +cat > "$BIN/cargo" <> "$CARGO_LOG" +EOF +chmod +x "$BIN/cargo" + +cat > "$BIN/timeout" <<'EOF' +#!/usr/bin/env bash +while [ "$#" -gt 0 ]; do + case "$1" in + --signal=*|--kill-after=*|*[smhd]) shift ;; + *) break ;; + esac +done +exec "$@" +EOF +chmod +x "$BIN/timeout" + (cd "$PRIMARY" && PATH="$BIN:$PATH" "$EXPECTED_HOOKS/pre-commit") (cd "$LINKED" && PATH="$BIN:$PATH" "$EXPECTED_HOOKS/pre-commit") -test "$(grep -c 'check WHAT=lint' "$MAKE_LOG")" -eq 2 -test "$(grep -c 'check WHAT=validate QUICK=1' "$MAKE_LOG")" -eq 2 -grep -q "^$PRIMARY|check WHAT=lint$" "$MAKE_LOG" -grep -q "^$LINKED|check WHAT=lint$" "$MAKE_LOG" +test "$(grep -c 'check WHAT=staged' "$MAKE_LOG")" -eq 2 +if grep -qE 'check WHAT=(lint|validate)' "$MAKE_LOG"; then + printf 'pre-commit invoked an unrelated full-workspace gate\n' >&2 + exit 1 +fi +grep -q "^$PRIMARY|check WHAT=staged$" "$MAKE_LOG" +grep -q "^$LINKED|check WHAT=staged$" "$MAKE_LOG" + +: > "$MAKE_LOG" +(cd "$PRIMARY" && PATH="$BIN:$PATH" "$EXPECTED_HOOKS/pre-push") +grep -q "^$PRIMARY|check WHAT=all$" "$MAKE_LOG" + +mkdir -p "$PRIMARY/crates/sample/src" +cat > "$PRIMARY/crates/sample/Cargo.toml" <<'EOF' +[package] +name = "sample" +version = "0.1.0" +EOF +printf 'pub fn staged() {}\n' > "$PRIMARY/crates/sample/src/lib.rs" +git -C "$PRIMARY" add crates/sample +(cd "$PRIMARY" && PATH="$BIN:$PATH" bash scripts/lib/mcb.sh check-staged) +grep -q '^fmt -p sample -- --check$' "$CARGO_LOG" +grep -q '^clippy -p sample --all-targets -- -D warnings$' "$CARGO_LOG" +if grep -q -- '--workspace' "$CARGO_LOG"; then + printf 'staged crate check expanded to unrelated workspace scope\n' >&2 + exit 1 +fi git init -q "$SUB_SOURCE" git -C "$SUB_SOURCE" config user.email test@example.com