housebroken-cli 0.2.8__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/PKG-INFO +3 -2
  2. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/README.md +2 -1
  3. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/bin/housebroken +4 -2
  4. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/pyproject.toml +1 -1
  5. housebroken_cli-0.3.0/scripts/branch-check.sh +140 -0
  6. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/skills/housebroken/SKILL.md +20 -4
  7. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/.gitignore +0 -0
  8. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/LICENSE +0 -0
  9. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/hatch_build.py +0 -0
  10. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/check-ai-policy.sh +0 -0
  11. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/comment-census.sh +0 -0
  12. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/diff-defaults.sh +0 -0
  13. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/distinct-outside.sh +0 -0
  14. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/file-pr.sh +0 -0
  15. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/fork-hygiene.sh +0 -0
  16. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/inbox.sh +0 -0
  17. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/notes.sh +0 -0
  18. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/pr-sweep.sh +0 -0
  19. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/prior-art.sh +0 -0
  20. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/scripts/verify-filed-pr.sh +0 -0
  21. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/src/housebroken/__init__.py +0 -0
  22. {housebroken_cli-0.2.8 → housebroken_cli-0.3.0}/src/housebroken/cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: housebroken-cli
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: Agent-written pull requests that do not make a mess in someone else's house.
5
5
  Project-URL: Repository, https://github.com/lenamonj/housebroken
6
6
  License-Expression: MIT
@@ -109,7 +109,7 @@ Every organization gets a card before the first filing: CLA, DCO, signed-commit
109
109
  A memory-safety or remote-abort finding in a library goes by the project's SECURITY.md route, privately, and never becomes a public pull request until the project answers.
110
110
 
111
111
  **9. File through the gate.**
112
- `file-pr.sh` wraps the pull request creation and refuses when the prior-art printout for that repository is missing or older than a day, or when the body carries a footer, a trailer, or a typographic dash.
112
+ `branch-check.sh` reads the branch before anything leaves the machine: it refuses a dirty working tree, a file whose mode disagrees with its own siblings, a committed file the repository's own `.gitignore` excludes, a working artifact, and a branch that is not on top of its base, and it prints every absolute claim the added prose makes so each can be falsified against the code. Three of those came from one review round on a single pull request. `file-pr.sh` wraps the pull request creation and refuses when the prior-art printout for that repository is missing or older than a day, or when the body carries a footer, a trailer, or a typographic dash.
113
113
 
114
114
  **10. Watch it land.**
115
115
  `verify-filed-pr.sh` re-derives from GitHub that the head is the intended commit, the diff is exactly the intended files, and CI settled green. `pr-sweep.sh` lists every open pull request where the ball is in your court: a maintainer's comment unanswered, a review requesting changes, a red check, a conflict. Every maintainer comment gets a same-day answer. A ruling in a closed issue is never argued with. When the maintainer is right, concede and let them close it.
@@ -146,6 +146,7 @@ uv tool install git+https://github.com/lenamonj/housebroken
146
146
  | `scripts/prior-art.sh` | issues and pull requests on the touched files, type stated, rulings quoted |
147
147
  | `scripts/comment-census.sh` | added code versus added comments, per branch |
148
148
  | `scripts/diff-defaults.sh` | default arguments and one-line wrappers added to keep call sites untouched |
149
+ | `scripts/branch-check.sh` | the branch's mechanical facts: clean tree, file modes, artifacts, absolute claims |
149
150
  | `scripts/file-pr.sh` | the only way a pull request gets filed |
150
151
  | `scripts/verify-filed-pr.sh` | the filed pull request is what was meant, and CI settled |
151
152
  | `scripts/pr-sweep.sh` | every open pull request where the ball is in your court |
@@ -97,7 +97,7 @@ Every organization gets a card before the first filing: CLA, DCO, signed-commit
97
97
  A memory-safety or remote-abort finding in a library goes by the project's SECURITY.md route, privately, and never becomes a public pull request until the project answers.
98
98
 
99
99
  **9. File through the gate.**
100
- `file-pr.sh` wraps the pull request creation and refuses when the prior-art printout for that repository is missing or older than a day, or when the body carries a footer, a trailer, or a typographic dash.
100
+ `branch-check.sh` reads the branch before anything leaves the machine: it refuses a dirty working tree, a file whose mode disagrees with its own siblings, a committed file the repository's own `.gitignore` excludes, a working artifact, and a branch that is not on top of its base, and it prints every absolute claim the added prose makes so each can be falsified against the code. Three of those came from one review round on a single pull request. `file-pr.sh` wraps the pull request creation and refuses when the prior-art printout for that repository is missing or older than a day, or when the body carries a footer, a trailer, or a typographic dash.
101
101
 
102
102
  **10. Watch it land.**
103
103
  `verify-filed-pr.sh` re-derives from GitHub that the head is the intended commit, the diff is exactly the intended files, and CI settled green. `pr-sweep.sh` lists every open pull request where the ball is in your court: a maintainer's comment unanswered, a review requesting changes, a red check, a conflict. Every maintainer comment gets a same-day answer. A ruling in a closed issue is never argued with. When the maintainer is right, concede and let them close it.
@@ -134,6 +134,7 @@ uv tool install git+https://github.com/lenamonj/housebroken
134
134
  | `scripts/prior-art.sh` | issues and pull requests on the touched files, type stated, rulings quoted |
135
135
  | `scripts/comment-census.sh` | added code versus added comments, per branch |
136
136
  | `scripts/diff-defaults.sh` | default arguments and one-line wrappers added to keep call sites untouched |
137
+ | `scripts/branch-check.sh` | the branch's mechanical facts: clean tree, file modes, artifacts, absolute claims |
137
138
  | `scripts/file-pr.sh` | the only way a pull request gets filed |
138
139
  | `scripts/verify-filed-pr.sh` | the filed pull request is what was meant, and CI settled |
139
140
  | `scripts/pr-sweep.sh` | every open pull request where the ball is in your court |
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- # housebroken - one command name for the nine gate scripts.
2
+ # housebroken - one command name for the gate scripts.
3
3
  #
4
4
  # The skill file, and any future adapter for another agent host, must be able
5
5
  # to say `housebroken prior-art owner/repo term` without knowing where the
@@ -13,7 +13,7 @@
13
13
  # housebroken version
14
14
  #
15
15
  # Environment:
16
- # HOUSEBROKEN_SCRIPTS directory holding the nine *.sh gate scripts. When
16
+ # HOUSEBROKEN_SCRIPTS directory holding the *.sh gate scripts. When
17
17
  # unset, the scripts directory next to this file
18
18
  # (../scripts), then this file's own directory, then
19
19
  # $HOUSEBROKEN_HOME/scripts are tried in that order.
@@ -48,6 +48,7 @@ usage: housebroken <subcommand> [args...]
48
48
  1 outside count outside contributors merged in the last 120 days
49
49
  2 prior-art issues and pull requests on the touched files, rulings quoted
50
50
  6 census added code lines against added comment lines, per branch
51
+ 9 branch the mechanical facts about the branch: tree, modes, artifacts, claims
51
52
  9 file the only way a pull request gets filed
52
53
  10 verify the filed pull request is what was meant, and CI settled
53
54
  10 sweep every open pull request where the ball is in your court
@@ -89,6 +90,7 @@ case "$cmd" in
89
90
  outside) script=distinct-outside.sh ;;
90
91
  prior-art) script=prior-art.sh ;;
91
92
  census) script=comment-census.sh ;;
93
+ branch) script=branch-check.sh ;;
92
94
  file) script=file-pr.sh ;;
93
95
  verify) script=verify-filed-pr.sh ;;
94
96
  sweep) script=pr-sweep.sh ;;
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "housebroken-cli"
7
- version = "0.2.8"
7
+ version = "0.3.0"
8
8
  description = "Agent-written pull requests that do not make a mess in someone else's house."
9
9
  dynamic = ["readme"]
10
10
  license = "MIT"
@@ -0,0 +1,140 @@
1
+ #!/bin/bash
2
+ # branch-check.sh - the mechanical facts about the branch you are about to file.
3
+ #
4
+ # Three defects on PaloAltoNetworks/docusaurus-openapi-docs #1624 were facts
5
+ # about the branch rather than about the patch, and git could have printed all
6
+ # three before anything left the machine.
7
+ #
8
+ # A reviewer had just shown that a sentence in the added documentation
9
+ # overclaimed. The wording was corrected in the working tree, the branch was
10
+ # rebuilt with `git reset --soft <base>`, and the commits still carried the old
11
+ # sentence: a soft reset leaves the index at the previous commit, so the
12
+ # corrected file was never staged. The tree was dirty and nobody looked. A
13
+ # dirty tree is a refusal here, because what you tested is not what you file.
14
+ #
15
+ # The new test file was committed 100755 where every other test in that
16
+ # repository is 100644, which GitHub renders in the diff. A mode that disagrees
17
+ # with the file's own siblings is a refusal.
18
+ #
19
+ # The overclaiming sentence was an absolute: "removes only the files the plugin
20
+ # generated", which the code falsifies for two file names. Absolutes in added
21
+ # prose are printed for you to check one by one; they are not refused, because
22
+ # some of them are true.
23
+ #
24
+ # Usage:
25
+ # bash branch-check.sh [CLONE] [--base REF]
26
+ # bash branch-check.sh --help
27
+ #
28
+ # CLONE defaults to the current directory. REF defaults to the first of
29
+ # upstream/main, upstream/master, origin/main, origin/master that resolves.
30
+ #
31
+ # Exits 0 when nothing blocks, 1 on a refusal, 2 on a usage error.
32
+ set -u
33
+
34
+ usage() { awk 'NR>1 && /^#/ {sub(/^# ?/, ""); print; next} NR>1 {exit}' "$0"; }
35
+
36
+ clone="."
37
+ base=""
38
+ while [ "$#" -gt 0 ]; do
39
+ case "$1" in
40
+ --help|-h) usage; exit 0 ;;
41
+ --base)
42
+ shift
43
+ base="${1:-}"
44
+ [ -n "$base" ] || { echo "REFUSED: --base needs a ref" >&2; exit 2; }
45
+ ;;
46
+ -*) echo "REFUSED: unknown option $1" >&2; exit 2 ;;
47
+ *) clone="$1" ;;
48
+ esac
49
+ shift
50
+ done
51
+
52
+ cd "$clone" 2>/dev/null || { echo "REFUSED: $clone is not a directory" >&2; exit 2; }
53
+ git rev-parse --git-dir >/dev/null 2>&1 || { echo "REFUSED: $clone is not a git clone" >&2; exit 2; }
54
+
55
+ if [ -z "$base" ]; then
56
+ for candidate in upstream/main upstream/master origin/main origin/master; do
57
+ if git rev-parse --verify -q "$candidate" >/dev/null 2>&1; then base="$candidate"; break; fi
58
+ done
59
+ fi
60
+ [ -n "$base" ] || { echo "REFUSED: no base ref resolved - pass --base REF" >&2; exit 2; }
61
+ git rev-parse --verify -q "$base" >/dev/null 2>&1 || { echo "REFUSED: base $base does not resolve" >&2; exit 2; }
62
+
63
+ blocked=0
64
+ block() { echo "REFUSED: $*"; blocked=1; }
65
+
66
+ echo "== branch check: $(git rev-parse --abbrev-ref HEAD) against $base =="
67
+
68
+ # What you tested is what you file, or you do not know what you are filing.
69
+ dirty=$(git status --porcelain)
70
+ if [ -n "$dirty" ]; then
71
+ block "the working tree is dirty, so the commits are not what you tested"
72
+ printf '%s\n' "$dirty" | sed 's/^/ /'
73
+ fi
74
+
75
+ if ! git merge-base --is-ancestor "$base" HEAD 2>/dev/null; then
76
+ block "HEAD is not on top of $base - rebase before filing"
77
+ fi
78
+
79
+ files=$(git diff --name-only "$base..HEAD" 2>/dev/null)
80
+ if [ -z "$files" ]; then
81
+ block "no files differ from $base - there is nothing to file"
82
+ fi
83
+
84
+ while IFS= read -r f; do
85
+ [ -n "$f" ] || continue
86
+ entry=$(git ls-files -s -- "$f" 2>/dev/null)
87
+ [ -n "$entry" ] || continue
88
+ mode=$(printf '%s\n' "$entry" | awk '{print $1}')
89
+
90
+ # A mode is only wrong relative to the file's own neighbours.
91
+ if [ "$mode" = "100755" ]; then
92
+ case "$f" in
93
+ *.*) ext=".${f##*.}" ;;
94
+ *) ext="" ;;
95
+ esac
96
+ if [ -n "$ext" ]; then
97
+ sibs=$(git ls-files -s -- "*$ext" 2>/dev/null | awk -v self="$f" '$4 != self {print $1}')
98
+ norm=$(printf '%s\n' "$sibs" | grep -c '^100644$')
99
+ exec=$(printf '%s\n' "$sibs" | grep -c '^100755$')
100
+ if [ "$norm" -gt "$exec" ]; then
101
+ block "$f is committed 100755 but $norm of its $ext siblings are 100644"
102
+ fi
103
+ fi
104
+ fi
105
+
106
+ # The repository's own .gitignore is the authority on what is build output.
107
+ if git check-ignore -q --no-index -- "$f" 2>/dev/null; then
108
+ block "$f is committed but the repository's .gitignore excludes it"
109
+ fi
110
+
111
+ case "$f" in
112
+ .jeffy/*|*/.jeffy/*|PLAN.md|*/PLAN.md|JOURNAL.md|*/JOURNAL.md|*.orig|*.rej)
113
+ block "$f is a working artifact and has no place in the diff" ;;
114
+ esac
115
+ done <<EOF
116
+ $files
117
+ EOF
118
+
119
+ # Absolutes in added prose are the claims a maintainer can falsify in one grep.
120
+ claims=$(git diff -U0 "$base..HEAD" -- '*.md' '*.mdx' '*.rst' '*.txt' 2>/dev/null \
121
+ | grep '^+' | grep -v '^+++' | sed 's/^+//' \
122
+ | grep -nEi '\b(only|exactly|always|never|guaranteed|nothing|everything)\b')
123
+ if [ -n "$claims" ]; then
124
+ echo "CHECK: added prose makes absolute claims - falsify each one against the code:"
125
+ printf '%s\n' "$claims" | sed 's/^/ /'
126
+ fi
127
+
128
+ echo
129
+ echo "commits:"
130
+ git log --oneline "$base..HEAD" | sed 's/^/ /'
131
+ echo "diff:"
132
+ git diff --stat "$base..HEAD" | sed 's/^/ /'
133
+
134
+ if [ "$blocked" -ne 0 ]; then
135
+ echo
136
+ echo "branch-check: refused. Fix the causes above; a refusal is a result."
137
+ exit 1
138
+ fi
139
+ echo
140
+ echo "branch-check: nothing mechanical blocks this branch."
@@ -81,8 +81,15 @@ report it in one line and do not proceed to the next step.
81
81
  trusting a command's exit code, check the clone's line endings before the
82
82
  first edit, export PATH explicitly in any detached script and report every
83
83
  step's exit code. Two arms of an experiment that agree exactly have not
84
- tested the variable. Before filing, hash the changed file on disk against
85
- the blob in the commit: a branch can carry the test and not the fix. When the
84
+ tested the variable. Swap the file's content from `git show <base>:<path>`
85
+ rather than switching branches: a checkout refused because the tree is
86
+ dirty leaves the patched file in place, and the arm then reports success
87
+ under the label RED. Assert the two arms' hashes differ before believing
88
+ either result. Before filing, hash the changed file on disk against
89
+ the blob in the commit: a branch can carry the test and not the fix.
90
+ After rebuilding commits, grep the committed blob for the change, never
91
+ the file on disk: `git reset --soft` leaves the index at the previous
92
+ commit, so a correction made after that commit is not in the new one. When the
86
93
  patched function serves several public entry points or has several
87
94
  branches, the test is a grid: every entry point through every branch,
88
95
  plus the zero, identity and signed-boundary rows. A reviewer lists the
@@ -94,7 +101,11 @@ report it in one line and do not proceed to the next step.
94
101
  workflow file to find them. A gate you did not run is a red check the
95
102
  maintainer sees before you do. When the repository ships a Dangerfile, a
96
103
  pre-commit config or a commitlint config, those are its acceptance
97
- criteria in machine-readable form; run them.
104
+ criteria in machine-readable form; run them. When the project's own lint
105
+ aborts on its own configuration, that is a fact to state, not permission
106
+ to skip yours: run the linter over the changed files directly, and
107
+ reproduce the global failure on a pristine base so the body says whose
108
+ it is.
98
109
 
99
110
  6. **Match the house style.**
100
111
  Run `housebroken notes owner/repo` before writing the pull request and
@@ -124,7 +135,12 @@ report it in one line and do not proceed to the next step.
124
135
  pull request until the project answers.
125
136
 
126
137
  9. **File through the gate.**
127
- `housebroken file owner/repo --title ... --body-file ...` is the only way a
138
+ `housebroken branch <clone>` first. It refuses a dirty working tree, a file
139
+ whose mode disagrees with its own siblings, a committed file the repository
140
+ ignores, and a branch that is not on top of its base, and it prints every
141
+ absolute claim your added prose makes so you can falsify each one against
142
+ the code. Then
143
+ `housebroken file owner/repo --title ... --body-file ...`, the only way a
128
144
  pull request is opened. Never run the pull request creation command
129
145
  directly. The gate refuses when the prior-art printout for that repository
130
146
  is missing or older than a day, or when the body carries a footer, a
File without changes