mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
Wire the depth and size gates, exempting structural depth
The depth cap had 104 violations. 57 are structural: whole projects with
their own layout (C/, dwm/, artgate/), a tool split into modules to satisfy
the line cap, prose trees, and directories whose name an external format
dictates (systemd, hooks, workflows, patches, fixtures). Those are exempted
the same way lib/ and tests/ already were.
The other 47 all sit under periodic_background/{hosts,digital_wellbeing},
which can only come down by extracting those units - blocked on a manual
guard repoint. So the hook runs --warn: the number stays visible on every
commit without blocking unrelated work. Enforcement is one flag away.
Also guards the argument order: `--all --warn` used to ignore --warn and
exit 1, which as a hook entry would have blocked every commit.
check_repo_size.sh is the companion gate - depth stops one subtree growing
a taxonomy, this stops the repo absorbing everything. It counts tracked
files (git ls-files, never find: the tree holds ~30k untracked extraction
leftovers) minus vendored and hook-generated trees: 1049 against a 1200
ceiling, printed every run so the threshold is auditable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
This commit is contained in:
parent
2cf62a2402
commit
0a1e43fc11
@ -86,6 +86,27 @@ repos:
|
||||
language: system
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
- id: directory-depth-cap
|
||||
name: Report paths over the directory-depth cap
|
||||
# Repo-wide, and deliberately --warn for now: 47 paths under
|
||||
# periodic_background/{hosts,digital_wellbeing} are over the cap and
|
||||
# can only come down by extracting those two units, which is blocked
|
||||
# on a manual guard repoint. Running it warn-only keeps the number
|
||||
# visible on every commit without blocking unrelated work. Drop
|
||||
# --warn once both units are out; that is the last step of the
|
||||
# extraction, not a separate decision.
|
||||
entry: bash meta/scripts/check_directory_depth.sh --warn --all
|
||||
language: system
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
- id: repo-size-cap
|
||||
name: Enforce the source-file ceiling
|
||||
# Counts tracked files only, excluding vendored and hook-generated
|
||||
# trees. Prints the count on every run so the ceiling is auditable.
|
||||
entry: bash meta/scripts/check_repo_size.sh
|
||||
language: system
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
- id: ci-baseline-green
|
||||
name: Refuse to build on a red CI baseline
|
||||
# Checks the last COMPLETED run of each required workflow on main.
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
{
|
||||
"intent": "Wire the two repo-structure gates into pre-commit: the directory-depth cap (warn-only for now) and a new source-file ceiling, so structural drift is visible on every commit.",
|
||||
"scope": [
|
||||
"meta/scripts/check_directory_depth.sh - structural exemptions plus a --warn mode",
|
||||
"meta/scripts/check_repo_size.sh - new source-file ceiling gate",
|
||||
".pre-commit-config.yaml - two new always_run hooks",
|
||||
"Non-goal: bringing the remaining 47 over-cap paths under the cap. They sit under periodic_background/{hosts,digital_wellbeing}, whose extraction is blocked on a manual sudo guard repoint.",
|
||||
"Non-goal: syncing meta/.pre-commit-config.yaml, which is a separate standalone config already 219 lines divergent at HEAD."
|
||||
],
|
||||
"changes": [
|
||||
"Exempted the 57 structurally-deep paths: whole projects with their own layout (C/, dwm/, artgate/), one module-split tool (misc/tools/), prose trees (docs/), three one-off leaves, and five externally-dictated directory segments (systemd, hooks, workflows, patches, fixtures).",
|
||||
"Added --warn to check_directory_depth.sh: reports violations and exits 0. The hook uses it because the 47 remaining paths cannot be fixed without the blocked extraction; enforcement turns on by deleting the flag.",
|
||||
"Guarded the --all argument order: '--all --warn' previously ignored --warn silently and exited 1, which would have blocked every commit. It now errors with a note that options precede --all.",
|
||||
"Added check_repo_size.sh, counting tracked files via git ls-files (never find - the tree holds ~30k untracked extraction leftovers), excluding third_party/, docs/superpowers/ and .hippo/. Ceiling 1200 against a current 1049; the count is always printed so the threshold is auditable from a CI log."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "./meta/scripts/check_directory_depth.sh --all",
|
||||
"result": "pass",
|
||||
"evidence": "EXIT=1 with exactly 47 violations, all under periodic_background/{hosts,digital_wellbeing}. Confirms the arithmetic 104 = 57 structural + 42 digital_wellbeing + 5 hosts, and that no unintended path was swept up by the new exemptions."
|
||||
},
|
||||
{
|
||||
"command": "./meta/scripts/check_directory_depth.sh --warn --all",
|
||||
"result": "pass",
|
||||
"evidence": "EXIT=0. Also checked: single clean file EXIT=0; single over-cap file EXIT=1; --help EXIT=0; '--all --warn' now errors loudly instead of silently exiting 1."
|
||||
},
|
||||
{
|
||||
"command": "./meta/scripts/check_repo_size.sh; ./meta/scripts/check_repo_size.sh --count",
|
||||
"result": "pass",
|
||||
"evidence": "'check_repo_size: 1049 source files (ceiling 1200)' EXIT=0; --count prints 1049 EXIT=0; unknown flag EXIT=1."
|
||||
},
|
||||
{
|
||||
"command": "MAX_SOURCE_FILES=10 variant of check_repo_size.sh",
|
||||
"result": "pass",
|
||||
"evidence": "EXIT=1 with 'over the 10 ceiling' plus the extract-a-unit hint. Confirms the failure branch fires rather than being dead code."
|
||||
},
|
||||
{
|
||||
"command": "pre-commit run directory-depth-cap --all-files; pre-commit run repo-size-cap --all-files",
|
||||
"result": "pass",
|
||||
"evidence": "Both report Passed through the real pre-commit harness, not just direct invocation."
|
||||
},
|
||||
{
|
||||
"command": "pre-commit run --files meta/scripts/check_repo_size.sh meta/scripts/check_directory_depth.sh .pre-commit-config.yaml",
|
||||
"result": "pass",
|
||||
"evidence": "No hook failures; shfmt and shellcheck clean on both scripts."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"The depth gate is warn-only, so it cannot yet block a NEW over-cap path. This is deliberate and reversible by deleting --warn from the hook entry once hosts and digital_wellbeing are extracted.",
|
||||
"The prefix exemptions are broad by design (e.g. all of linux_configuration/docs/). A future deep code tree under an exempted prefix would go unpoliced; the exemptions are individually commented so each can be narrowed.",
|
||||
"The 1200 ceiling leaves 151 files of headroom. Pruning docs/superpowers is tracked separately and would not change this count, since that tree is excluded from it."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit. Both gates are additive: the scripts are standalone and the hooks are new entries, so reverting restores the previous behaviour exactly.",
|
||||
"After rollback, confirm 'pre-commit run --all-files' still passes and that no hook references the removed check_repo_size.sh."
|
||||
]
|
||||
}
|
||||
@ -12,17 +12,34 @@
|
||||
# third_party/ vendored - its shape is upstream's business
|
||||
# docs/superpowers/ hook-generated bookkeeping, not hand-placed
|
||||
# .github/skills/ layout fixed by the agent harness
|
||||
# C/ a C project: its own build layout, not taxonomy
|
||||
# artgate/ a Flask app; routes/ and styles/ are framework shape
|
||||
# misc/tools/ one tool split across modules to satisfy the line cap
|
||||
# docs/ prose trees; chapter folders are not code taxonomy
|
||||
# dwm/ an upstream-shaped suckless tree
|
||||
# utils/data/ data files beside the script that reads them
|
||||
# zsh/scratchpad/ a single scratch file
|
||||
# run_all/ the aggregate runner's own fixtures
|
||||
#
|
||||
# Exempt, by path segment: any file under a `lib/` or `tests/` directory.
|
||||
# Those two names are structural, not taxonomic - a library split out to
|
||||
# satisfy the 250-line cap, and the test suite that the shell-coverage
|
||||
# ratchet requires to sit beside it. Capping them would force one gate to
|
||||
# fight another.
|
||||
# Exempt, by path segment: `lib/` and `tests/` are structural, not taxonomic
|
||||
# - a library split out to satisfy the 250-line cap, and the test suite the
|
||||
# shell-coverage ratchet requires beside it. Capping them would force one
|
||||
# gate to fight another. The same argument covers the directories whose name
|
||||
# is dictated by an external format rather than chosen by us: systemd unit
|
||||
# dirs, pacman/git hook dirs, GitHub workflow dirs, patch sets and test
|
||||
# fixtures. Each is matched as a whole segment, so `libvirt/` or `binutils/`
|
||||
# is unaffected.
|
||||
#
|
||||
# check_directory_depth.sh --all # every tracked file
|
||||
# check_directory_depth.sh <file>... # only the named files
|
||||
# check_directory_depth.sh --all --warn # report but always exit 0
|
||||
#
|
||||
# Exits 0 when everything is within the cap, 1 when anything is over.
|
||||
# With --warn it reports violations and still exits 0; the hook runs in that
|
||||
# mode because 47 paths under `periodic_background/{hosts,digital_wellbeing}`
|
||||
# are over the cap and can only be fixed by extracting those two units, which
|
||||
# is blocked on a manual guard repoint. Drop --warn from the hook once they
|
||||
# are out; the cap is then enforced for real.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
@ -35,6 +52,19 @@ readonly EXCLUDED_PREFIXES=(
|
||||
third_party/
|
||||
docs/superpowers/
|
||||
.github/skills/
|
||||
# Whole projects that carry their own conventional layout.
|
||||
linux_configuration/C/
|
||||
linux_configuration/dwm/
|
||||
python_pkg/artgate/
|
||||
# A single tool split into modules to satisfy the 250-line cap.
|
||||
linux_configuration/misc/tools/
|
||||
# Prose trees: a chapter folder is not code taxonomy.
|
||||
linux_configuration/docs/
|
||||
# One-off leaves, listed explicitly rather than exempting the segment
|
||||
# name everywhere: `data` and `scratchpad` are too generic for that.
|
||||
linux_configuration/utils/data/
|
||||
linux_configuration/zsh/scratchpad/
|
||||
meta/scripts/run_all/
|
||||
)
|
||||
|
||||
# Directory names that never count as taxonomy. Matched as whole path
|
||||
@ -42,12 +72,21 @@ readonly EXCLUDED_PREFIXES=(
|
||||
readonly EXEMPT_SEGMENTS=(
|
||||
lib
|
||||
tests
|
||||
# Names dictated by an external format, not chosen as a category.
|
||||
systemd
|
||||
hooks
|
||||
workflows
|
||||
patches
|
||||
fixtures
|
||||
)
|
||||
|
||||
WARN_ONLY=0
|
||||
|
||||
usage() {
|
||||
echo "Usage: $SCRIPT_NAME [--all | <file>...]"
|
||||
echo "Usage: $SCRIPT_NAME [--warn] [--all | <file>...]"
|
||||
echo "Options:"
|
||||
echo " --all Check every tracked file"
|
||||
echo " --warn Report violations but always exit 0"
|
||||
echo " -h, --help Show this help"
|
||||
echo
|
||||
echo "Files must sit at most $MAX_DEPTH directories below the repo root."
|
||||
@ -115,6 +154,11 @@ main() {
|
||||
fi
|
||||
|
||||
if [[ "$1" == "--all" ]]; then
|
||||
if [[ $# -gt 1 ]]; then
|
||||
echo "Error: --all takes no further arguments (got: ${*:2})" >&2
|
||||
echo "Note: options must precede --all, e.g. --warn --all" >&2
|
||||
exit 1
|
||||
fi
|
||||
mapfile -t targets < <(git ls-files)
|
||||
else
|
||||
targets=("$@")
|
||||
@ -135,6 +179,11 @@ main() {
|
||||
printf '%s\n' "$violations" | while IFS=$'\t' read -r depth path; do
|
||||
printf ' %3d %s (+%d)\n' "$depth" "$path" "$((depth - MAX_DEPTH))" >&2
|
||||
done
|
||||
|
||||
if ((WARN_ONLY)); then
|
||||
echo "check_directory_depth: --warn given, not failing" >&2
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
@ -143,6 +192,10 @@ while [[ $# -gt 0 ]]; do
|
||||
-h | --help)
|
||||
usage
|
||||
;;
|
||||
--warn)
|
||||
WARN_ONLY=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
|
||||
106
meta/scripts/check_repo_size.sh
Executable file
106
meta/scripts/check_repo_size.sh
Executable file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================================================================
|
||||
# Enforce a ceiling on the number of source files in the repo.
|
||||
#
|
||||
# The companion to the directory-depth cap. Depth stops any one subtree from
|
||||
# growing its own taxonomy; this stops the repo as a whole from quietly
|
||||
# becoming the place everything lands. When the count reaches the ceiling the
|
||||
# answer is to extract a unit into its own repository - not to raise the
|
||||
# number because the commit is inconvenient.
|
||||
#
|
||||
# "Source" means tracked files that a human wrote and maintains here. Three
|
||||
# categories are therefore excluded from the count:
|
||||
#
|
||||
# third_party/ vendored; upstream decides how many files it has
|
||||
# docs/superpowers/ hook-generated evidence/contract bookkeeping, which
|
||||
# grows once per commit and would dominate the number
|
||||
# .hippo/ agent-generated store, same argument
|
||||
#
|
||||
# Counting is done with `git ls-files`, never `find`: the working tree also
|
||||
# holds untracked extraction leftovers running to tens of thousands of files,
|
||||
# and a `find`-based count would be meaningless.
|
||||
#
|
||||
# The count is always printed, so the threshold can be audited from any CI log
|
||||
# rather than being taken on trust.
|
||||
#
|
||||
# check_repo_size.sh # count and enforce
|
||||
# check_repo_size.sh --count # print the number only, always exit 0
|
||||
#
|
||||
# Exits 0 when at or under the ceiling, 1 when over.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
|
||||
# Set from the 1049 files counted when this gate was written, plus room to
|
||||
# work. Extract a unit rather than raising it; see the header.
|
||||
readonly MAX_SOURCE_FILES=1200
|
||||
|
||||
readonly EXCLUDED_PREFIXES=(
|
||||
third_party/
|
||||
docs/superpowers/
|
||||
.hippo/
|
||||
)
|
||||
|
||||
COUNT_ONLY=0
|
||||
|
||||
usage() {
|
||||
echo "Usage: $SCRIPT_NAME [--count]"
|
||||
echo "Options:"
|
||||
echo " --count Print the source-file count and exit 0"
|
||||
echo " -h, --help Show this help"
|
||||
echo
|
||||
echo "Fails when tracked source files exceed $MAX_SOURCE_FILES."
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Tracked files minus the generated and vendored trees. Printed one per line.
|
||||
source_files() {
|
||||
local -a filter=()
|
||||
local prefix
|
||||
for prefix in "${EXCLUDED_PREFIXES[@]}"; do
|
||||
filter+=(-e "^${prefix}")
|
||||
done
|
||||
# grep exits 1 when it filters everything out; that is not an error here.
|
||||
git ls-files | grep -v "${filter[@]}" || true
|
||||
}
|
||||
|
||||
main() {
|
||||
local count
|
||||
count="$(source_files | wc -l)"
|
||||
|
||||
if ((COUNT_ONLY)); then
|
||||
echo "$count"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ((count > MAX_SOURCE_FILES)); then
|
||||
echo "check_repo_size: $count source files, over the $MAX_SOURCE_FILES ceiling" >&2
|
||||
echo "Extract a unit into its own repo rather than raising the cap." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "check_repo_size: $count source files (ceiling $MAX_SOURCE_FILES)"
|
||||
return 0
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h | --help)
|
||||
usage
|
||||
;;
|
||||
--count)
|
||||
COUNT_ONLY=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
main
|
||||
Loading…
Reference in New Issue
Block a user