Skip to content

Commit 8f7a622

Browse files
“Williamclaude
andcommitted
fix: you are never your own guest
GitHub logins are case-insensitive, but stats.jq compared them case-sensitively. $login arrives from the CLI in whatever casing was typed while API payloads carry the canonical spelling, so `gh-pet willjames` failed to recognize its own "WillJames/repo" events as its own. The visitor list filtered self out via startswith($login + "/") — a proxy for "not me" that silently stopped holding under a case mismatch, promoting you to a visitor on your own stage. On a case-insensitive filesystem the guest cache lookup then resolves to your own state.env, so the pet renders standing next to itself. Bind the login downcased once and state the invariant on the extracted owner rather than trusting the repo-prefix proxy. Same root cause fixed in two siblings: $outbound7 counted your own repos as outbound comments on others' repos (inflating social), and the $comms bond ledger could list you against yourself. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 91d9d62 commit 8f7a622

2 files changed

Lines changed: 35 additions & 5 deletions

File tree

gh-pet/lib/stats.jq

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@
3838
([ $EV[] | select((.created_at | fromdateiso8601) <= $NOW) ]) as $EV
3939
| ([ $ST[] | select((.starred_at // null) == null or ((.starred_at | fromdateiso8601) <= $NOW)) ]) as $ST
4040

41+
# who we are, for comparison against API payloads. GitHub logins are case-
42+
# insensitive, but $login arrives from the CLI in whatever casing was typed while
43+
# payloads carry the canonical spelling — compare downcased, or `gh-pet willjames`
44+
# fails to recognize its own "WillJames/repo" events as its own.
45+
| (($U.login // $login) | ascii_downcase) as $me
46+
4147
# ── account & identity facts ─────────────────────────────────────────────────
4248
| ($U.created_at // "2020-01-01T00:00:00Z") as $created
4349
| (dago($created)) as $acct_days
@@ -157,16 +163,19 @@
157163
| select(.type == "IssueCommentEvent" or .type == "PullRequestReviewEvent"
158164
or .type == "PullRequestReviewCommentEvent" or .type == "CommitCommentEvent")
159165
| select(dago(.created_at) < 7)
160-
| select((.repo.name // "") | startswith($login + "/") | not) ] | length) as $outbound7
166+
| select((.repo.name // "" | ascii_downcase) | startswith($me + "/") | not) ] | length) as $outbound7
161167
| ((7 * ($outbound7 | sqrt) + ([($FW | length), 20] | min)) | clamp(0; 100) | round) as $social
162168
# visitors: whoever you've interacted with in the LAST HOUR — their pets
163-
# drop by on the stage (owners of repos you commented/reviewed on)
169+
# drop by on the stage (owners of repos you commented/reviewed on).
170+
# You are never your own guest: the repo filter is only a proxy for that, so the
171+
# owner is checked against $me directly rather than trusting the proxy to hold.
164172
| ([ $EV[]
165173
| select(.type == "IssueCommentEvent" or .type == "PullRequestReviewEvent"
166174
or .type == "PullRequestReviewCommentEvent" or .type == "CommitCommentEvent")
167175
| select(dago(.created_at) < (1 / 24))
168-
| select((.repo.name // "") | startswith($login + "/") | not)
169-
| ((.repo.name // "") | split("/")[0]) | select(. != "") ]
176+
| select((.repo.name // "" | ascii_downcase) | startswith($me + "/") | not)
177+
| ((.repo.name // "") | split("/")[0])
178+
| select(. != "" and (ascii_downcase != $me)) ]
170179
| unique | .[0:3]) as $visitors
171180
# bond ledger: who you actually talk to — for every comment/review event,
172181
# the counterpart is the author of the issue/PR (person-level, so org-repo
@@ -178,7 +187,7 @@
178187
or .type == "PullRequestReviewCommentEvent" or .type == "CommitCommentEvent")
179188
| (.payload.issue.user.login // .payload.pull_request.user.login
180189
// ((.repo.name // "") | split("/")[0]))
181-
| select(. != "" and . != $login) ]
190+
| select(. != "" and (ascii_downcase != $me)) ]
182191
| group_by(.) | sort_by(-length) | .[0:30]
183192
| map("\(.[0])|\(length)") | join(";")) as $comms
184193

gh-pet/tests/run.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ _hap=$(sed -n "s/^HAPPINESS='\([0-9]*\)'.*/\1/p" <<<"$ASP")
7676
_cap=$(sed -n "s/^CAPPED_BY='\(.*\)'\$/\1/p" <<<"$ASP")
7777
if [[ -n $_hap ]] && (( _hap > 60 )) && [[ -z $_cap ]]; then ok "aspirational stats never hard-cap (happiness $_hap > 60, uncapped)"; else fail "an aspirational stat capped a healthy pet (happiness=${_hap:-none} capped_by='$_cap')"; fi
7878

79+
echo "· you are never your own guest: GitHub logins are case-insensitive, so"
80+
echo " 'gh-pet SELF' must still recognize self/app as its own — otherwise the"
81+
echo " owner of every repo you touch reads as a stranger and you turn up on your"
82+
echo " own stage as a visitor (and in your own outbound social score)"
83+
_vev=$(jq -n --argjson n "$_now" '[
84+
{type:"IssueCommentEvent",created_at:(($n-600)|todate),repo:{name:"self/app"}},
85+
{type:"IssueCommentEvent",created_at:(($n-600)|todate),repo:{name:"mona/hello"},
86+
payload:{issue:{user:{login:"mona"}}}}]')
87+
VIS=$(jq -n -r --arg now "$_now" --arg login SELF \
88+
--argjson user '[{"id":3151702,"login":"self","created_at":"2019-03-14T09:00:00Z"}]' \
89+
--argjson events "[$_vev]" \
90+
--argjson repos null --argjson merged null --argjson approved null --argjson changesreq null \
91+
--argjson reviewedby null --argjson starred null --argjson stale null --argjson alerts null \
92+
--argjson calendar null --argjson notifications null --argjson medals null \
93+
--argjson orgs null --argjson following null -f "$ROOT/lib/stats.jq" 2>&1)
94+
_vis=$(sed -n "s/^VISITORS='\(.*\)'\$/\1/p" <<<"$VIS")
95+
if [[ " $_vis " != *" self "* && " $_vis " != *" SELF "* ]]; then ok "self never appears in its own visitor list (VISITORS='$_vis')"; else fail "the pet is visiting itself (VISITORS='$_vis')"; fi
96+
if [[ " $_vis " == *" mona "* ]]; then ok "a real last-hour interaction still drops by (mona)"; else fail "guest list lost a real visitor (VISITORS='$_vis')"; fi
97+
_out=$(sed -n "s/^OUTBOUND7='\([0-9]*\)'\$/\1/p" <<<"$VIS")
98+
if [[ $_out == 1 ]]; then ok "outbound social counts others' repos only, whatever the login's casing"; else fail "own-repo comments leaked into outbound social (OUTBOUND7=${_out:-none}, want 1)"; fi
99+
79100
echo "· unauthenticated / public-only path (plan.md §9.2): the pure function still"
80101
echo " derives when health is unknown and the contribution calendar is absent"
81102
UARGS=(-r --arg now "$(date +%s)" --arg login octotest)

0 commit comments

Comments
 (0)