housebroken-cli 0.2.7__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.
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/PKG-INFO +3 -2
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/README.md +2 -1
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/bin/housebroken +4 -2
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/pyproject.toml +1 -1
- housebroken_cli-0.3.0/scripts/branch-check.sh +140 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/skills/housebroken/SKILL.md +27 -4
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/.gitignore +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/LICENSE +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/hatch_build.py +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/check-ai-policy.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/comment-census.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/diff-defaults.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/distinct-outside.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/file-pr.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/fork-hygiene.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/inbox.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/notes.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/pr-sweep.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/prior-art.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/scripts/verify-filed-pr.sh +0 -0
- {housebroken_cli-0.2.7 → housebroken_cli-0.3.0}/src/housebroken/__init__.py +0 -0
- {housebroken_cli-0.2.7 → 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.
|
|
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
|
|
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
|
|
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 ;;
|
|
@@ -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."
|
|
@@ -71,11 +71,25 @@ report it in one line and do not proceed to the next step.
|
|
|
71
71
|
that fails on the default branch for a wrong value, a crash or an
|
|
72
72
|
out-of-bounds access, and "less memory" is not that test.
|
|
73
73
|
|
|
74
|
-
4. **Prove it red first.**
|
|
74
|
+
4. **Prove it red first, and prove the proof.**
|
|
75
75
|
On a fresh clone of the upstream default branch, not the tree you found
|
|
76
76
|
the bug in, write the test that fails. Run it and keep the failing output.
|
|
77
77
|
Apply the patch. Run it again. Both results go into the pull request as
|
|
78
|
-
the test itself; the body states the fact in one sentence.
|
|
78
|
+
the test itself; the body states the fact in one sentence.
|
|
79
|
+
Ask what each step would print if it did nothing, and make that answer
|
|
80
|
+
different from success: revert by content and compare hashes rather than
|
|
81
|
+
trusting a command's exit code, check the clone's line endings before the
|
|
82
|
+
first edit, export PATH explicitly in any detached script and report every
|
|
83
|
+
step's exit code. Two arms of an experiment that agree exactly have not
|
|
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
|
|
79
93
|
patched function serves several public entry points or has several
|
|
80
94
|
branches, the test is a grid: every entry point through every branch,
|
|
81
95
|
plus the zero, identity and signed-boundary rows. A reviewer lists the
|
|
@@ -87,7 +101,11 @@ report it in one line and do not proceed to the next step.
|
|
|
87
101
|
workflow file to find them. A gate you did not run is a red check the
|
|
88
102
|
maintainer sees before you do. When the repository ships a Dangerfile, a
|
|
89
103
|
pre-commit config or a commitlint config, those are its acceptance
|
|
90
|
-
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.
|
|
91
109
|
|
|
92
110
|
6. **Match the house style.**
|
|
93
111
|
Run `housebroken notes owner/repo` before writing the pull request and
|
|
@@ -117,7 +135,12 @@ report it in one line and do not proceed to the next step.
|
|
|
117
135
|
pull request until the project answers.
|
|
118
136
|
|
|
119
137
|
9. **File through the gate.**
|
|
120
|
-
`housebroken
|
|
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
|
|
121
144
|
pull request is opened. Never run the pull request creation command
|
|
122
145
|
directly. The gate refuses when the prior-art printout for that repository
|
|
123
146
|
is missing or older than a day, or when the body carries a footer, a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|