F issue-key opens its ABC-123.{md,txt,mkd}; log-view commit summaries mint the same F token
The tokenizers already fuse an issue key (ABC-123) into ONE F-tagged token — MKDTonKey/MDTonKey (dog/tok/MKDT.c:41, MDT.c.rl) and FREEonKey (sticky through the D comment overlay, dog/tok/FREE.c.rl:71 rule uc ucnum* "-" dgt+ => on_key), asserted by MKDTIssueKeyTest/MDTIssueKeyTest (dog/test/TOK01.c:1227,1259 — "ABC-123" → "F"). The tag exists SO the pager can navigate it (MKDT.c:41 comment: "one filename-tagged token so the pager can navigate it (BRO-012)"), but no consumer resolves it: a left-click on an F cell is DEAD because BRO-005's _uriAt only follows a token's adjacent hidden U target (be/views/bro/pager.js:414), and an F issue key carries none. Goal: (1) a click on an F issue-key token OPENS the ticket file named for it — ABC-123.{md,txt,mkd} — located under $TODO_ROOT, a NEW env var naming the ticket tree ON PAR with SRC_ROOT (default: the root of the FIRST worktree opened, i.e. the launch tree — tickets live in ONE tree, e.g. journal/todo/, not in the code worktree being browsed); (2) the log: view detects ticket codes in commit summaries and mints the SAME F token, so those are clickable too. JS (jab) side. Method: Issues.
F is emitted for issue keys by FREE (all langs,
sticky through the D comment overlay), MKDT (.mkd), MDT (.md) — see dog/tok/{FREE,MKDT,MDT}* and dog/test/TOK01.c (ABC-123 → F, decorated refs like ` ABC-123 stay H/G`). The CONSUMER is the whole gap.
_mouse → _uriAt(hunk,off) →
_runSpell push (be/views/bro/pager.js:346,414). _uriAt resolves a click to the NEXT token's hidden U bytes, or the line's lone U (_lineUri:432). An F issue-key token in a file body (cat/blob/tree of code or a wiki page) has NO adjacent U, so the click returns null — a dead link.
U
commit:?<sha> right after the sha8 token (be/views/log/log.js:368,392,397 via navlib.navUri), and the pager hides it + follows it on click. But the commit SUMMARY is emitted as a single flat TAG_S span (log.js:401), so a ABC-123 inside a commit message is plain text, not an F, not clickable.
//TREE vs // are the two nav roots: be.wtdir (be/core/discover.js:200)
maps //name[/sub] → $SRC_ROOT/name/sub and /////. → the LAUNCH tree (find(cwd).wt, "where jab started"); be.navCwd (discover.js:~220) gives the CURRENT view's //name/path context. URI-011/Nav scope a // authority through authorityRepo (be/core/loop.js:180).
F-tagged issue-key token opens
the ticket file whose basename is the key with a .md/.txt/.mkd extension — ABC-123 → ABC-123.mkd — via a view-stack push, like any U.
$TODO_ROOT (a new env var,
mirror of SRC_ROOT); (2) the CURRENT wt root (the view's nav authority); (3) the OPEN wt root (the first/launch wt). First root whose todo/<TOPIC>/<KEY>.<ext> exists wins; a total miss is a quiet no-op.
log: view scans each commit summary, fuses any ticket code into the
SAME F token the tokenizers mint, and makes it open the ticket — a click on ABC-123 in a commit message opens ABC-123.{md,txt,mkd}.
F tag/token the tokenizers already emit — NO new tag, NO new hunk
format. Reuse BRO-005 _uriAt/_screenToByte and BRO-006's hidden-U splice; producers otherwise unchanged.
be.wtdir/be.navCwd and the URI class (URI-011);never hand-compose a path or regex-parse a URI (work §"Things NOT TO DO").
md/txt/mkd under <root>/todo/; open the
first hit. todoRoot() (new core/discover.js helper, mirror of srcRoot() :185) yields the ordered roots: TODO_ROOT env, then current wt, then open wt.
bro/MAUS) parity may follow as its own ticket.
… but tickets live in a DIFFERENT repo, so the [URI-012] follow-inheritance grafting the current view's ?ref (a diff hash) onto it yields cat://journal/…?<hash> → the hash is absent there → "no hunks". Fix in _resolveSpell (views/bro/pager.js): inherit the context ?ref ONLY for a RELATIVE click (target OMITS its authority); an own-//authority (absolute, maybe cross-repo) target keeps its own (usually absent) ref. Diff-view ticket codes then resolve via the pager F`-fallback with no stale ref.
views/cat withLinks (BRO-006's port of
C bro_word_around, BRO.c:2968) blankets EVERY grepable token with a grep:#<word> U — the F issue key included (grepable() is byte-level, tag-blind) — so _uriAt's adjacent-U check opened grep BEFORE the ticket. Fix at the PRODUCER: withLinks emits a ticket U (ticketUri) for an F token, grep for the rest (unresolved F falls to grep). Not a C behavior — C emits no per-token U; it greps the word under the cursor on right-click.
F (FREE/MKDT/MDT tokens, no producer
U) is resolved by the PAGER from the token TEXT at click time; a log F is resolved by the PRODUCER, which splices a hidden U after it (BRO-006 style) so the existing _uriAt just works. Both converge on: key → ABC-123.{md,txt,mkd} under a wt root → a cat:/blob: nav URI.
ticketUri(key): todoRoot() (new core/discover.js helper,
mirror of srcRoot()) yields roots IN ORDER — getenv("TODO_ROOT"), the current wt root, the open/launch wt root — and the resolver probes each for todo/<TOPIC>/<KEY>.<ext>, composing the open URI via the URI class + be.navCwd(root) so it OPENS in the pager. TOPIC = chars before the - (ABC-123 → todo/ABC/ABC-123.mkd). The ordered roots cover both the common case (tickets in one tree via TODO_ROOT) and browsing within the ticket tree.
appendRow (log.js:357) to scan summary for the key
shape and split the one TAG_S span into S/F/S, splicing a hidden U ticket-file URI after each F (mirrors the sha8+U splice at log.js:392). Detect the key with the SHARED tokenizer grammar (FREE/MKDT uc ucnum* "-" dgt+), NOT a fresh JS regex, so the log's F can't drift from the body's.
be/test/bro/ticket/ (check.js + run.sh) — landed 9e33f053.F-cell click with no U derives the target from the token text via
TICKET.ticketUri(key) then follows (views/bro/pager.js:900) — landed 9e33f053.
todoRoot() in core/discover.js:198 (mirror of srcRoot(), BRO-012 comment
at :193), exported via be (:271) — landed 9e33f053.
shared/ticket.js — scanKeys/ticketUri/ticketDir (:37,:69,:87),
roots via be.todoRoot()+be.navCwd, no hand-composed path — landed 9e33f053; reused by COMMIT-007 (ae6ac86f) as designed.
appendRow scans the summary with the SHARED ticket.scanKeys
grammar and splices the hidden U ticket URI per key (views/log/log.js:424-436) — landed 9e33f053.
9f15c6c8
(beagle pin 3293eaf9); tests ride be/test/bro/ticket/.
commit:-view producer twin (the metadata
message is a log-summary analogue): it REUSES this ticket's shipped shared/ticket.js (scanKeys/ticketUri) + be.todoRoot() to splice an F + hidden U per message key. This ticket still owns the pager body-token F-fallback path + the log: splice.
binary (same gate as BRO-005).
$TODO_ROOT/todo/<TOPIC>/<KEY>.mkd — NOT a flat
<KEY>.mkd; TOPIC is derived from the chars before the -.
be/ (jab
upward be/-scan, jab:__main) — navigating //other does NOT swap code. So the fix only shows once it is the RUNNING tree: launch jab from the fixed worktree (set TODO_ROOT to the ticket tree) or land it into journal/be.
9e33f053 ("BRO-012: make ticket codes clickable", 2026-07-05): pager F-fallback + shared/ticket.js resolver + be.todoRoot() + the log: summary splice; test be/test/bro/ticket/.9f15c6c8 / beagle 3293eaf9 ("BRO-012: make the StrictMark parser recognize ticket codes", 2026-07-03): the tokenizer half.ae6ac86f) reuses the shipped shared/ticket.js + be.todoRoot() for commit:-view message keys, per plan.