Skip to content

fix(mention-open): a bare #N the pane guesses is now overridable too - #1387

Merged
ZacxDev merged 1 commit into
mainfrom
fix/mention-open-bare-hash-n-universe
Sep 8, 2026
Merged

fix(mention-open): a bare #N the pane guesses is now overridable too#1387
ZacxDev merged 1 commit into
mainfrom
fix/mention-open-bare-hash-n-universe

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Sep 8, 2026

Copy link
Copy Markdown
Member

The defect, and what #1380 left open

A candidate the handler offers can come from several rungs of mention_scan's
attribution ladder. In the terminal profile only three occur: explicit
(the operator wrote owner/repo#N), mapped (the text named the repo, the
mapping supplied the owner) and default (the repo came from
tmux_pane_repo() or --default-repo). The first two are evidence about the
reference
; default is evidence about the window, and it is frequently
wrong.

The defect class is a guessed repository the operator cannot override.
#1380 closed it for the shape where the guess was alone (audit-pr N) and
deliberately left the bare #N alone, writing the trade down in the handler
and in test_a_bare_hash_N_that_the_PANE_already_attributes_does_NOT_get_the_universe:
burying two good rows under several hundred looked like "a regression dressed
as a feature", and the choice belonged to the operator.

The operator made it on 2026-09-08"fix the bare-#N and any other cases
left unfixed"
— because when the pane guess is wrong the right repository is
unreachable. Same defect, one rung along.

What changed

The offer is now scoped to repo_source == default alone, not to that rung
plus being the only row. Everything already in the list keeps its position;
the fuzzy universe is appended beneath it.

Two things the widening required:

  • guessed_note grew a rank. Its multi-row wording opened "The FIRST row
    is a guess"
    — true of audit-pr N, false of a bare #N, where row 1 is
    the clawgate task and the guess is row 2. Pointing the operator at the wrong
    row is worse than saying nothing: they would distrust the task and trust the
    guess. The note now names the measured row number.
  • The append is guarded on what it actually ADDS, not on the universe being
    non-empty. A host whose whole universe is the pane's own repo dedupes to
    nothing, and offered_universe would then have claimed rows that were not in
    the list — to the auto-open guard as well as to the note. guessed_note is
    keyed on below (rows genuinely searchable under the guess) rather than a row
    count for the same reason: "type to search" is nonsense with nothing to
    search.

The whole class, enumerated and classified

Every terminal path through main(). "Unoverridable?" means: does the handler
act on, or offer without alternatives, a repository the operator cannot correct?

# shape rung before after unoverridable?
1 868abc123 (ClickUp) n/a opens directly unchanged no — the id is the reference
2 owner/repo#N explicit opens directly, 0 keystrokes unchanged no — negative control
3 repo#N, mapping resolves mapped opens directly unchanged, deliberately — see below no (argued)
4 repo#N, mapping does not resolve none universe picker + universe_note unchanged no
5 #N + pane repo default 2 rows, no note, no way out clawgate, guess, then the universe; note names row 2 WAS YES — FIXED
6 #N, no pane repo none clawgate + universe, no note unchanged no
7 audit-pr N + pane repo default guess + universe (#1380) unchanged behaviour, note reworded no — re-verified
8 audit-pr N, no pane repo none universe picker unchanged no
9 --default-repo X with 5 or 7 default same rung as the pane same fix, via the same predicate covered
10 #282828 (six digits) n/a named toast, no picker unchanged no — deliberately not a picker
11 --print <unresolvable> n/a refuses, never lists repos unchanged no
12 --print <resolvable> any prints the URL(s) unchanged no
13 --no-discovery n/a text-only resolution unchanged no

Not fixed, on purpose

Case 3, mapped. A stale or wrong mapping row is an answer the operator
cannot override in the picker — but I think it is correct as-is, and I did not
change it:

  • the TEXT named the repository, so the answer is evidence about the
    reference
    , not about the window. That is the whole distinction the
    suppression is built on;
  • regen-known-repos.py drops a bare name two owners share rather than
    picking one (measured there: 7 collisions, 14 repos), so a mapped hit is
    unique on this host by construction — it is not a coin flip;
  • the mapping's staleness already reaches the operator: staleness_note() fires
    past STALE_MAPPING_DAYS and names the failing refresh unit, and
    universe_note carries the generation date above every universe picker;
  • the cost of "fixing" it is a several-hundred-row picker in front of every
    repo#N, which is exactly what the explicit negative control forbids.

The residual — a mapping that is silently wrong — is answered by writing
owner/repo#N (already the advice refuse() prints) or regenerating. Flagging
it rather than changing it.

Case 6, #N with no pane repo, gets the universe with no note. The
picker's rows are a real clawgate task plus a list, with nothing being
recommended, so there is no guess to explain — but the several-hundred-row list
is arguably unexplained. Left alone: it is a cosmetic question, not the defect
class, and it is a separate decision from the one asked for.

Case 12 observation, not changed: --print '#N' with a pane repo prints the
pane-guessed GitHub URL alongside the clawgate one. --print is non-interactive
and prints every candidate by contract; it never gets the universe (pinned).
Recording it here rather than silently altering a machine-readable contract.

Superseded, not deleted

Two tests pinned the old behaviour on purpose. Both are rewritten to assert the
new intent and to record who asked and when, keeping the half of their claim
that survives — the measured rows come FIRST, in that order:

  • test_a_bare_hash_N_that_the_PANE_already_attributes_does_NOT_get_the_universe
    test_a_bare_hash_N_that_the_PANE_attributes_gets_the_universe_BELOW_it
  • test_the_note_is_NOT_attached_to_the_ordinary_bare_hash_N_picker
    test_the_bare_hash_N_picker_SAYS_the_github_row_is_a_guess

test_the_ORDINARY_picker_gets_no_note keeps its name but its docstring is
rewritten: it used to claim the bare #N picker never carries a note, which
is now false on a real host. What it pins now is the degenerate universe its
fixture builds.

Test matrix — base 18bc1500, head b2190536

9 red at base, all 185 green at head. Measured by extracting 18bc1500 with
git archive | tar -x and dropping the NEW test file on the PRISTINE base
handler (blob 7afe7396, verified by git hash-object). Every one is a real
assertion FAILURE at base, not a collection error.

FAILED test_the_bare_hash_N_picker_SAYS_the_github_row_is_a_guess
FAILED test_a_bare_hash_N_that_the_PANE_attributes_gets_the_universe_BELOW_it
FAILED test_a_bare_hash_N_offers_the_universe_UNDER_the_two_measured_rows
FAILED test_the_bare_hash_N_ORDER_is_clawgate_then_the_guess_then_the_universe
FAILED test_the_bare_hash_N_note_names_the_GUESSED_ROWS_POSITION
FAILED test_the_audit_pr_note_still_names_ROW_1
FAILED test_the_guessed_row_is_not_offered_twice_on_the_bare_hash_N_path_either
FAILED test_the_DEFAULT_REPO_FLAG_rides_the_same_rung_as_the_pane
FAILED test_the_universe_reaches_ROFI_AND_NO_OTHER_SINK_on_the_bare_hash_N_path
9 failed, 176 passed          # at base
185 passed                    # at head

Two of these were vacuous on the first attempt and were strengthened after
watching them pass at base: the ordering test and the dedupe test are both
satisfied by a two-row picker (nothing at index 2 can be out of order; two rows
have no duplicates). Both now assert a row-count floor first, and both go red.

The remaining new tests are INVARIANT GUARDS, labelled as such in their own
docstrings
— green at base too, by design: the auto-open suppression, the
degenerate-universe boundary, the untouched #N-with-no-pane neighbour, and the
three negative controls.

Negative controls (all green at base and head)

  • owner/repo#N and repo#N still open with zero keystrokes, driven with a
    WRONG pane repo loaded so a guess leaking into those paths would be visible.
  • #282828 still produces the named colour-literal toast, no picker, with a
    pane repo loaded.
  • --print '#N' prints exactly its 2 candidates, raises no picker, and no
    universe token reaches stdout.

Disclosure

test_the_universe_reaches_ROFI_AND_NO_OTHER_SINK_on_the_bare_hash_N_path
drives the widened arm through the real notify() and folds stdout, stderr
and every notify-send argv through _every_sink, then asserts
_no_universe_token_anywhere plus the three UNIVERSE_ONLY spellings. Its
positive control asserts a universe row really did reach the picker rows, so
it cannot pass by scanning an empty universe. The note itself is checked with
_no_universe_token_anywhere too.

Mutation coverage

Three rows added to scripts/tests/mutation_battery_mentions.py, anchors
verified unique by test_mutation_battery_anchors.py, and watched to kill:

P1   control      KILLED              f=20  p=419   positive control — the battery can observe
K45  deletion     KILLED(attributed)  f=8   p=431   the offer narrows back to the guess that is ALONE
K46  deletion     KILLED(attributed)  f=3   p=436   guess_rank hardcoded to 1
K47  widening     KILLED(attributed)  f=5   p=434   the universe is PREPENDED, not appended
4/4 killed for the stated reason; problems: none

Tree restored afterwards and verified by git hash-object, not by the
battery's own restore: OK line.

Gate — both tiers, on the MERGED tree

origin/main is 18bc1500 and this branch fast-forwards from it, so the merged
tree is b2190536. Both tiers run against that sha.

Dev hostnix develop <worktree> --command bash scripts/gate.sh --tier both:

FAIL  scripts/tests  (collected=12877 passed=12870 skipped=0 failed=7 errors=0)
PASS  node  (suites=5 files=41 tests=1449 pass=1449 fail=0)
GATE: RESULT=FAIL exit=1

Sandbox (the tier Tekton runs), extracted with git archive HEAD | tar -x
into a dir with no .git, built one derivation at a time:

nix build path:<tmpdir>#checks.x86_64-linux.pytests    RESULT: FAIL (exit=1)
    FAIL  scripts/tests  (collected=12877 passed=12870 skipped=0 failed=7 errors=0)
nix build path:<tmpdir>#checks.x86_64-linux.nodetests  RESULT: PASS (exit=0)
    TOTAL suites=5 files=41 tests=1449 pass=1449 fail=0

🔴 The 7 pytest failures are PRE-EXISTING RED ON main, not this change
and that is a measurement, not a theory. The discriminating control was run in
both tiers against base 18bc1500:

  • dev host, base: the same 7 test names fail (7 failed, 406 passed);
  • sandbox, base: nix build path:<base>#…pytestsFAIL scripts/tests (collected=12863 passed=12856 skipped=0 failed=7 errors=0).

Base vs head in the sandbox tier: collected +14, passed +14, failed unchanged
at 7
— the +14 is exactly this PR's new tests.

The 7 are age-version behaviour and an opencode-version pin, in files this
diff does not touch:

test_analyze_service_index_escrow_verify.py::test_a_TAMPERED_or_TRUNCATED_artifact_is_ARTIFACT_CORRUPT_not_EMPTY  (x3)
test_analyze_service_index_escrow_verify.py::test_every_decrypt_family_VERDICT_is_pinned_WHOLE
test_analyze_service_index_escrow_verify.py::test_the_LEAKING_manglers_really_DO_make_age_keygen_echo_the_secret
test_analyze_service_index_backup.py::test_resolve_recipient_NEVER_quotes_age_keygens_INPUT_ECHOING_stderr
test_opencode_engine.py::test_engine_is_the_version_every_measurement_is_keyed_to   # opencode on PATH is 1.18.29, pin says 1.18.21

They are a real problem on main and somebody should own them; they are not
this diff's, and the delta above is what says so.

Live click path — measured, nothing raised

rofi, xdg-open and notify-send replaced by stubs on PATH (rofi dumps
stdin and exits 1 = dismissed), against the REAL host mapping, universe and tmux
pane. No window was raised and no workspace was switched. The handler blob
probed is c30ee598, byte-identical to HEAD:scripts/mention-open.py. Rows are
joined with \n, so the counts below add the one wc -l under-reports.

click rows (base → head) first row note
audit-pr 1291 392 → 392 github 1291 — …/innovation-upstream/devrc/issues/1291 (the pane guess) reworded: "…Row 1 is a guess from the tmux pane… the 391 rows below it are every repository this host knows. Type to search, or dismiss."
#1291 2 → 393 clawgate task 1291 — https://clawgate.zacx.dev/tasks/1291 (unchanged) new: "#1291 names no repository. Row 2 is a guess from the tmux pane… the 391 rows below it are every repository this host knows. Type to search, or dismiss."
civitai/talos-infra#1065 0 → 0 — (no picker) none; xdg-open https://github.com/civitai/talos-infra/issues/1065, rc 0
#282828 0 → 0 — (no picker) none; toast "cannot resolve '#282828' / #282828 is six digits — that looks like a colour literal…", rc 1

The #1291 row is the fix: 2 rows → 393, with row 1 and row 2 unmoved, so
the common case is still one Enter and the wrong guess is now escapable.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E1h5KrSQj42u746XYVWiGh

#1380 closed the shape where the pane's guess was ALONE (`audit-pr N`) and
deliberately left the bare `#N` two-row picker alone, writing the trade down in
both the handler and
`test_a_bare_hash_N_that_the_PANE_already_attributes_does_NOT_get_the_universe`:
burying two good rows under several hundred looked like "a regression dressed as
a feature". The operator overruled it on 2026-09-08 — "fix the bare-#N and any
other cases left unfixed" — because when the pane guess is wrong the right
repository is unreachable, which is the same defect one rung along.

So the offer is now scoped to `repo_source == default` alone, not to that rung
PLUS being the only row. The MEASURED rows keep their positions — clawgate,
then the pane's GitHub repo — so the common case is still one Enter, and the
fuzzy universe is appended beneath them.

Two things the widening required:

  * `guessed_note` grew a `rank`. Its multi-row wording said "The FIRST row is a
    guess", true of `audit-pr N` and FALSE of a bare `#N`, where row 1 is the
    clawgate task. Naming the wrong row is worse than saying nothing.
  * the append is guarded on what it actually ADDS, not on the universe being
    non-empty. A host whose whole universe is the pane's own repo dedupes to
    nothing, and `offered_universe` would then have claimed rows that were not
    in the list — to the auto-open guard as well as to the note.

DELIBERATELY UNCHANGED, each verified rather than assumed: `owner/repo#N` and
`repo#N` still open with zero keystrokes (they are evidence about the
REFERENCE); `#282828` is still a named toast; `--print` still refuses and never
lists a repository; a bare `#N` nothing attributed keeps PASS 3's own arm and
its silent picker.

Superseded on purpose, not deleted: the two tests that pinned the old behaviour
are rewritten to assert the new intent and to record who asked and when, keeping
the half of their claim that survives (the measured rows come FIRST, in that
order).

Test matrix — 9 red at 18bc150, 185 green at HEAD; invariant guards labelled as
such in their own docstrings. Mutation rows K45/K46/K47 added and watched to
kill for their stated reason, with P1 as the positive control.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1h5KrSQj42u746XYVWiGh
Claude-Session-Id: 3495c5a1-5b5f-491d-b47c-606eafc05a61
@ZacxDev
ZacxDev merged commit 72e59df into main Sep 8, 2026
1 of 2 checks passed
@ZacxDev
ZacxDev deleted the fix/mention-open-bare-hash-n-universe branch September 8, 2026 16:10
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