feat(mention): offer every repo you contribute to in the picker, and converge it daily - #1369
Merged
Conversation
…converge it daily The picker was showing 339 of 388 repos. `repo_universe()` read the resolution mapping's `.values()`, so it silently inherited both of that mapping's filters — and both are correct for resolution and wrong for a picker. MEASURED on the workbench 2026-09-07: 53 repos the operator owns or collaborates on could not be reached by typing at the picker at all. 48 were dropped for `has_issues == false`, 7 bare names (14 repos) as ambiguous. Neither filter buys anything in a picker: a row is a full `owner/repo`, nothing opens without a selection, and `/issues/N` resolves a PULL REQUEST even where issues are disabled — measured 6/6 against the API with a positive control (2 issues-enabled repos first, both PULL). An earlier probe that saw 3 of 4 return 404 was confounded: an unauthenticated request 404s on a PRIVATE repo whatever the redirect does. The mapping keeps the filter on the narrower true reason. So the generator now writes a SECOND file, `known_universe.json` — a separate question deserves a separate corpus, and a second file has no flag day, which a new shape inside `known_repos.json` would (the collector reads that file from a nix `home.file` copy that only changes on a switch). Live: 392 rows, 52 of them reachable only through the picker. The source did not need widening. `user/repos` already uses the widest affiliation the endpoint offers, and the obvious worry was measured rather than assumed: `search/issues?q=author:<login> type:pr` returned 28 repos and ALL 28 were already in `user/repos`. Do not add a second API source; it adds nothing. Nothing converged the mapping, so a daily user timer now does — and the objection recorded beside `STALE_MAPPING_DAYS` was answered rather than overruled. It argued a scheduled `gh api` run would fail forever on a host without `gh auth`, toast daily, and that a permanently-red timer is worse than no timer. True of a generator with one failure code. It now exits 4 for "not configured on this host" (`SuccessExitStatus=4`, quiet) and 3 only for a real failure (still toasts). Readiness is asked of `gh auth status`'s exit code, not of its wording. Verified live: rc 4 on a PATH with no `gh`. The staleness note changes meaning with it — it said "nothing regenerates it", which is now false and would send the operator to re-run a generator by hand while a failing unit stayed invisible. It names the unit. Disclosure: `known_universe.json` names MORE private repos than the mapping does, being unfiltered. The incident guard could not see it — it counts `key: "owner/repo"` pairs and a JSON list has none, so the file would have scored 0 and sailed through the guard that exists to stop exactly this. Added a structural detector for the list shape, in both JSON and Python-literal spellings. Its first, textual version raised NINE false accusations (a package-lock's `node_modules/...` paths, eight test files' fixtures) and was replaced — a guard that fires on ordinary files is one everyone overrides. Also closes two holes in the test fixture while it is being extended: the autouse redirect now covers the new file AND `DEVRC_WORKSPACE`, which was per-test and one forgotten flag away from a `_run()` child walking the operator's real ~/workspace — the same class as the nine tests once measured reading the real mapping. Evidence: * generator suite: 14 new tests RED at origin/main (969f058), 46 green at HEAD * mutation battery: 8/8 mutants KILLED by their own named test, plus a harness control that SURVIVED as expected. Run under PYTHONDONTWRITEBYTECODE=1; each mutation required to match exactly once. * `mention-open` suite cannot run at base (its fixture names a symbol that does not exist there), so its evidence is the battery, not a base run — stated because an ERROR at base is not the same claim as a FAIL. * rendered unit inspected, not just built: `SuccessExitStatus=4` present. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
…n mapping (392 rows, 52 picker- Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
…the pins that keep the acknowledgements honest
The full gate caught this: 1 failed of 12875, in
test_no_real_launchers.py::test_every_hazardous_binary_the_scripts_reach_is_
stubbed_or_acknowledged. Not a flake and not environmental — a real
consequence of the previous commit.
`launcher_scan.hazard_hits` is a TEXTUAL scan (it regexes the file body),
deliberately, since erring toward reporting is right for a scan whose failure
mode is a missed launch. Two new prose mentions therefore register as reaches:
* mention-open.py -> systemctl. `staleness_note()` now tells the operator
`check systemctl --user status mention-known-repos-refresh`, which is the
whole point of the reworded note: past STALE_MAPPING_DAYS the cause is a
unit that has not landed, and the old wording sent them to re-run a
generator by hand instead.
* regen-known-repos.py -> home-manager. One clause explaining why the
universe is a SECOND FILE: session-tailer.py reads the mapping from a nix
`home.file` copy that only changes on a switch, so a reshaped file would
have a flag day.
Both re-justified rather than reworded to dodge the scanner, which is this
table's own stated norm — deleting the words would delete, respectively, the
only pointer from the symptom to its cause and the reason the design is what
it is.
🔴 AND BOTH ARRIVE WITH A PIN, because an acknowledgement BLINDS the guard it
is filed under. That is not a theory: the table's tmux-reply-agent entry
records it being measured — that entry rested on "verified by grep", and
injecting a real `subprocess.run(["systemctl", ...])` left the whole suite at
77 passed. Its remedy was an AST pin; this is the same remedy.
* test_mention_open_SPAWNS_these_argv0_AND_NOTHING_ELSE -> {git, tmux,
notify-send, xdg-open, rofi}
* test_regen_SPAWNS_these_argv0_AND_NOTHING_ELSE -> {gh, git}
Both grows-or-shrinks, both with a `<computed>` sentinel so a spawn built from
a variable fails loudly rather than silently leaving the set. Plus a test per
file asserting BOTH halves of the acknowledgement's claim: that the mention
still exists (so the table entry cannot outlive the sentence it describes) and
that the binary is not in the spawn set.
⚠ The neighbouring runtime ledger (test_the_resolution_path_spawns_ONLY_these_
local_commands) does NOT cover this and is explicitly not cited for it: it
records what the RESOLUTION PATH spawns, so a systemctl call added to notify()
or any branch it does not drive would never enter its ledger. The new pins
read the whole file.
Controls WATCHED, not asserted: clean tree passes both pins; an injected
`subprocess.run(["systemctl", ...])` / `(["home-manager", "switch"])` fails
2/2 with each pin's OWN message; restore verified afterwards.
⚠ The control harness itself was wrong first and reported the OPPOSITE of the
truth: its summary regex required the word "passed", which pytest omits when
every selected test fails, so a fully-killed injection scored NOT KILLED. Read
as "the pin is inert" when the pin was working. Fixed by counting each word
independently, and the parser was itself checked against all three summary
shapes before its verdict was believed.
Also found by sweeping: fixing the systemctl hit alone would have left the
home-manager one to fail the next run. Swept the whole hazard vocabulary
across both changed files at once instead of iterating per failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X
Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
The nix sandbox tier caught this; the dev-host tier could not, and the difference IS the finding. `test_main_exits_nonzero_and_writes_NOTHING_when_gh_fails` stubs `read_api_repos` to throw and asserts exit 3. Since the readiness split, `main()` calls `require_gh_ready()` FIRST — so the outcome depends on whether the MACHINE has `gh`: * dev host — gh present and authed, readiness passes, the stub throws -> 3, GREEN * sandbox — no gh at all, readiness fails first -> 4, RED Four consecutive dev-host gate runs were green on this. It is exactly the two-tier blindness CLAUDE.md names: each tier's environment silently decides what executes, so a defect can be structurally invisible in one of them. The arm the test NAMES is authenticated-but-broken — gh present, logged in, API call failed — which is exit 3 and must toast. Stubbing readiness explicitly is what makes the assertion about the code rather than about the machine it ran on. Also pinned to RG.EXIT_FAILED rather than a bare 3. Plus a guard on the class, because one fixed instance does not close it: test_this_suite_never_asks_the_HOST_whether_gh_is_installed drives main() with the REAL readiness check and gh made unreachable, asserting NOT_CONFIGURED and raising on any command attempted after readiness fails. Every future test that forgets to stub now gets that 4 with a sentence saying why, instead of a plausible wrong answer that only reddens in the tier nobody runs locally. Verified: 49 passed on the dev host; the sandbox derivation is the check that actually matters here and is rebuilt separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
ZacxDev
added a commit
that referenced
this pull request
Sep 7, 2026
…ive on both hosts by driving t Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
ZacxDev
added a commit
that referenced
this pull request
Sep 7, 2026
…ive on both hosts by driving t (#1374) Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
ZacxDev
added a commit
that referenced
this pull request
Sep 8, 2026
…h one wrong answer
Reported from the real click path 2026-09-07. Clicking `audit-pr 1291` in
Alacritty gave a ONE-ROW picker holding the tmux pane's repo, above the note
"audit-pr 1291 names no repository". The operator's words:
"in this case the guess is right, but in practice it's not"
That is the defect. #1336 was right to suppress the auto-open — a
`default`-sourced repo is evidence about the WINDOW, not about the reference,
so it must never open unconfirmed. But having declined to act on the guess, the
handler offered nothing else: confirm the wrong repo, or dismiss and type the
URL by hand. Both are worse than the refusal the picker replaced, and it
defeats the 392-row fuzzy universe added in #1369 — which was already built and
sitting right there, unreached by this one arm.
So the guess now leads a picker instead of being the whole of it. It stays
FIRST — it is the most likely answer and remains one Enter away — with the
universe appended and deduped beneath it.
🔴 SCOPED TO THE GUESS THAT IS *ALONE*, and that narrowing is the substance of
the change rather than a detail. A bare `#N` the pane attributes already offers
two rows (the clawgate task and the pane's repo), and
`test_a_bare_hash_N_that_the_PANE_already_attributes_does_NOT_get_the_universe`
pins that deliberately: it is the most common interaction in this handler, and
burying two good rows under several hundred is a regression dressed as a
feature. My first version was not scoped and broke exactly that test — the
suite caught it.
⚠ The same complaint does PARTLY apply to that two-row case: if the pane guess
is wrong there, the right repo is still unreachable. Fixing it costs every
ordinary click a full-height picker, which is the operator's trade to make, not
one to smuggle in beside a bug fix. Written down, not silently taken.
The note needed two wordings, because "Confirm, or dismiss" is now FALSE on the
common path — neither is what to do. With alternatives on offer it says the
first row is a guess and the rest are searchable. The one-row wording is KEPT,
not deleted: with no mapping file the universe is empty and a lone guessed row
is still reachable.
🔴 The note is chosen by BRANCH ORDER, and both conditions are now true at once.
`universe_note` opens "nothing here knows X", which is false when the first row
is a recommendation the handler is asking about. Swapping the branches leaves
the rows correct and only the words wrong, so no behavioural test sees it —
`test_a_guessed_picker_is_NOT_described_as_nothing_here_knows` is the guard.
Superseded rather than deleted: `test_the_one_row_picker_for_a_guessed_repo_
SAYS_WHY` asserted `n == 1`, which IS the reported defect. Renamed, and it now
asserts `n > 1`; its real claims — the note explains itself, and never names a
repository — are unchanged.
⚠ One of my own new tests was wrong in a way worth recording: `universe=[]` does
NOT empty the universe, because `repo_universe()` unions the generated file with
`discover_repos()`. It asserted 1 row and got 4. The helper now takes the
mapping as a parameter so the empty case is actually reachable.
Verified through the REAL click path with `rofi` stubbed (no window raised):
audit-pr 1291 -> 392 rows, guess FIRST, new note
#1291 -> 2 rows, no note (unchanged)
civitai/talos-infra#1065 -> opens directly, no picker (unchanged)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X
Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
ZacxDev
added a commit
that referenced
this pull request
Sep 8, 2026
…h one wrong answer (#1380) * fix(mention-open): a guessed repo you cannot override is a prompt with one wrong answer Reported from the real click path 2026-09-07. Clicking `audit-pr 1291` in Alacritty gave a ONE-ROW picker holding the tmux pane's repo, above the note "audit-pr 1291 names no repository". The operator's words: "in this case the guess is right, but in practice it's not" That is the defect. #1336 was right to suppress the auto-open — a `default`-sourced repo is evidence about the WINDOW, not about the reference, so it must never open unconfirmed. But having declined to act on the guess, the handler offered nothing else: confirm the wrong repo, or dismiss and type the URL by hand. Both are worse than the refusal the picker replaced, and it defeats the 392-row fuzzy universe added in #1369 — which was already built and sitting right there, unreached by this one arm. So the guess now leads a picker instead of being the whole of it. It stays FIRST — it is the most likely answer and remains one Enter away — with the universe appended and deduped beneath it. 🔴 SCOPED TO THE GUESS THAT IS *ALONE*, and that narrowing is the substance of the change rather than a detail. A bare `#N` the pane attributes already offers two rows (the clawgate task and the pane's repo), and `test_a_bare_hash_N_that_the_PANE_already_attributes_does_NOT_get_the_universe` pins that deliberately: it is the most common interaction in this handler, and burying two good rows under several hundred is a regression dressed as a feature. My first version was not scoped and broke exactly that test — the suite caught it. ⚠ The same complaint does PARTLY apply to that two-row case: if the pane guess is wrong there, the right repo is still unreachable. Fixing it costs every ordinary click a full-height picker, which is the operator's trade to make, not one to smuggle in beside a bug fix. Written down, not silently taken. The note needed two wordings, because "Confirm, or dismiss" is now FALSE on the common path — neither is what to do. With alternatives on offer it says the first row is a guess and the rest are searchable. The one-row wording is KEPT, not deleted: with no mapping file the universe is empty and a lone guessed row is still reachable. 🔴 The note is chosen by BRANCH ORDER, and both conditions are now true at once. `universe_note` opens "nothing here knows X", which is false when the first row is a recommendation the handler is asking about. Swapping the branches leaves the rows correct and only the words wrong, so no behavioural test sees it — `test_a_guessed_picker_is_NOT_described_as_nothing_here_knows` is the guard. Superseded rather than deleted: `test_the_one_row_picker_for_a_guessed_repo_ SAYS_WHY` asserted `n == 1`, which IS the reported defect. Renamed, and it now asserts `n > 1`; its real claims — the note explains itself, and never names a repository — are unchanged. ⚠ One of my own new tests was wrong in a way worth recording: `universe=[]` does NOT empty the universe, because `repo_universe()` unions the generated file with `discover_repos()`. It asserted 1 row and got 4. The helper now takes the mapping as a parameter so the empty case is actually reachable. Verified through the REAL click path with `rofi` stubbed (no window raised): audit-pr 1291 -> 392 rows, guess FIRST, new note #1291 -> 2 rows, no note (unchanged) civitai/talos-infra#1065 -> opens directly, no picker (unchanged) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61 * fix(mention): re-anchor K38/K19 — the new branch made their anchor match twice Caught by `test_every_mutation_anchor_occurs_exactly_once_in_its_target`, in BOTH tiers, and the guard is exactly right. Both rows anchor on `offered_universe = True`. The guessed-repo fix adds a second assignment of it, so each anchor went from 1x to 2x — and a 2x anchor mutates a site the row's description does not name, which is a mutation result about something nobody asked about. Re-anchored onto the dead-end-1 comment line directly above the original assignment, which is unique. 🔴 Re-anchoring is HALF the fix; the guard's own message says to re-run the battery and confirm the row still kills. Done, and both do: P1 control KILLED (the battery can observe) K38 disclosure KILLED(attributed) f=1 killer: ..._never_reaches_STDOUT_STDERR_... K19 deletion KILLED(attributed) f=6 killer: ..._ONE_ENTRY_universe_is_still_a_CHOICE_... 3/3 killed for the stated reason, restore OK. Without that re-run the anchors would parse and the rows could still have been pinning nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The picker was showing 339 of 388 repos. Asked for "all repos I contribute to,
cached + periodically refreshed, fuzzy searchable" — measured, the source was never
the problem and the filters were.
What was actually wrong
repo_universe()read the resolution mapping's.values(), so the fuzzy pickersilently inherited both of that mapping's filters:
has_issues == falsefoo#12owner/repobitdex#12alice/bitdexMEASURED on the workbench: 53 repos the operator owns or collaborates on could
not be reached by typing at the picker. Live after the change: 392 rows, 52 of
them reachable only through the picker.
🔴 The
has_issuesfilter's stated reason was false. The comment said a barerepo#N"404s for EVERY N" — untrue for pull requests. A first probe (4 repos,unauthenticated
curl, 3×404) appeared to confirm it and was confounded: anunauthenticated request 404s on a private repo whatever the redirect does.
Authenticated, with a positive control (2 issues-enabled repos first, both
PULL):6/6 issues-disabled repos resolved
/issues/<pr>to the PR. The mapping keeps thefilter on the narrower true reason; the universe drops it.
🔴 The source needed no widening — measured, so nobody re-adds one.
user/reposalready uses the widest affiliation GitHub offers. The plausible gap was checked:
search/issues?q=author:<login> type:prreturned 28 repos, all 28 already inuser/repos, 0 new.The timer, and the objection it had to answer
Nothing converged the mapping. A timer had been explicitly rejected in
mention-open.py: a scheduledgh apirun fails forever on a host withoutgh auth,toasting daily, and a permanently-red timer is worse than none. That was correct
against a generator with one failure code — so it was answered, not overruled:
SuccessExitStatus=4, quietReadiness comes from
gh auth status's exit code, never its wording. Verifiedlive: rc 4 on a PATH with no
gh; and the rendered unit was inspected forSuccessExitStatus=4rather than trusting that it built.Disclosure
known_universe.jsonnames more private repos than the mapping (it is unfiltered),and the incident guard could not see it — it counts
key: "owner/repo"pairs, anda JSON list has none, so the file scored 0 and would have passed the guard written
to stop exactly that. Added a structural detector (JSON + Python-literal). Its first,
textual version raised 9 false accusations and was replaced: a guard that fires on
ordinary files is one everyone overrides.
Two prose mentions (
systemctl,home-manager) tripped the textual launcher scan andare acknowledged with AST pins, because an acknowledgement blinds the guard it is
filed under — this table records that being measured. Both controls watched: clean tree
passes, injected call sites fail 2/2 with each pin's own message.
Evidence
969f0581, green at HEADrequired to survive; run under
PYTHONDONTWRITEBYTECODE=1, each mutation requiredto match exactly once
scripts/gate.sh --tier both: PASS (22,035 pytest + 1,449 node, 0 failed)git archiveextract:pytestsPASS (22,036 passed, 0 failed, floor met),nodetestsPASS (1,449)⚠ Honest limits. The
mention-opensuite cannot run at base at all — itsfixture names a symbol the old module lacks, so all 163 error at setup. That is
red-for-the-wrong-reason, so its evidence is the battery, not a base run; an ERROR at
base is not a FAIL at base. And the Alacritty click path itself is not verified —
--printcannot distinguish auto-open from a one-row picker. That needs a human click.Two defects found by the gates, not by review
Fixing only the first would have left the second to fail the next run — swept the
whole hazard vocabulary at once instead.
test_main_exits_nonzero_and_writes_NOTHING_when_gh_failsstubbedread_api_reposbut not readiness, so its result depended on whether the machine had
gh: green onthe dev host, red in the sandbox. Four consecutive green dev-host runs said nothing
about it. Fixed, plus a guard on the class so the next test that forgets to stub gets
a
4with a sentence saying why.🤖 Generated with Claude Code
https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X