Skip to content

feat(mention): offer every repo you contribute to in the picker, and converge it daily - #1369

Merged
ZacxDev merged 6 commits into
mainfrom
feat/picker-universe
Sep 7, 2026
Merged

feat(mention): offer every repo you contribute to in the picker, and converge it daily#1369
ZacxDev merged 6 commits into
mainfrom
feat/picker-universe

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Sep 7, 2026

Copy link
Copy Markdown
Member

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 picker
silently inherited both of that mapping's filters:

filter right for wrong for
drop has_issues == false resolving a bare foo#12 a picker: rows are full owner/repo
drop a bare name two owners share resolving bitdex#12 a picker: there is no ambiguity in alice/bitdex

MEASURED 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_issues filter's stated reason was false. The comment said a bare
repo#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: an
unauthenticated 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 the
filter on the narrower true reason; the universe drops it.

🔴 The source needed no widening — measured, so nobody re-adds one. user/repos
already uses the widest affiliation GitHub offers. The plausible gap was checked:
search/issues?q=author:<login> type:pr returned 28 repos, all 28 already in
user/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 scheduled gh api run fails forever on a host without gh 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:

  • exit 4 = not configured on this host → SuccessExitStatus=4, quiet
  • exit 3 = configured and broken → still toasts

Readiness comes from gh auth status's exit code, never its wording. Verified
live: rc 4 on a PATH with no gh; and the rendered unit was inspected for
SuccessExitStatus=4 rather than trusting that it built.

Disclosure

known_universe.json names more private repos than the mapping (it is unfiltered),
and the incident guard could not see it — it counts key: "owner/repo" pairs, and
a 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 and
are 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

  • Generator suite: 14 new tests red at 969f0581, green at HEAD
  • Mutation battery: 8/8 killed by their own named test, plus a harness control
    required to survive; run under PYTHONDONTWRITEBYTECODE=1, each mutation required
    to match exactly once
  • Dev-host tier scripts/gate.sh --tier both: PASS (22,035 pytest + 1,449 node, 0 failed)
  • Sandbox tier, built one derivation at a time from a git archive extract:
    pytests PASS (22,036 passed, 0 failed, floor met), nodetests PASS (1,449)

Honest limits. The mention-open suite cannot run at base at all — its
fixture 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
--print cannot distinguish auto-open from a one-row picker. That needs a human click.

Two defects found by the gates, not by review

  1. The launcher ledger caught the two new prose mentions (1 failed of 12,875).
    Fixing only the first would have left the second to fail the next run — swept the
    whole hazard vocabulary at once instead.
  2. The sandbox tier caught what the dev tier structurally could not.
    test_main_exits_nonzero_and_writes_NOTHING_when_gh_fails stubbed read_api_repos
    but not readiness, so its result depended on whether the machine had gh: green on
    the 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 4 with a sentence saying why.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FENxxrZztNTsrKgChnGT1X

ZacxDev and others added 6 commits September 7, 2026 15:36
…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
ZacxDev merged commit 156b492 into main Sep 7, 2026
2 checks passed
@ZacxDev
ZacxDev deleted the feat/picker-universe branch September 7, 2026 22:39
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant