Names are everywhere in the forge and none of them could be clicked. This adds
/u/<name>, a directory at /u, and links from every name a credential
produced.
Closes #150.
What is on a profile
Issues opened, changes opened, reviews given, and comments, then the repositories that work is in, then the activity feed filtered to that account. The four numbers are links to the feed they count, and the feed has chips for All / Issues / Changes / Reviews / Comments.
A retired account keeps its page and says so. The page has a Mention button
that copies @name.
The visibility rule
Account names are public on this instance; activity is not. Every number and every event is computed over the repositories the reader may open, so an anonymous reader and a maintainer see different totals for the same person, and a private repository contributes nothing to a stranger’s view. The page says “Counted across the repositories you can read” rather than pretending the numbers are total.
An unknown name gets the same 404 every missing page gets. Both pages carry
noindex: the names are public, a scraped roster is not what public has to
mean.
Where the numbers come from
The events table already holds one row per opening, comment, approval and state
change. A profile is a grouped read of those rows (ActorActivity), and the feed
is ActorEvents; the directory reads ActorSummary for a page of names in one
query. Nothing is counted from the entity tables, so “12 issues opened” cannot
drift from the feed, and no profile is written to users.db.
Two supporting changes in forge/index: an events_actor index so a profile is
an actor range rather than a scan of everything the viewer can see, and
schemaVersion = 11 to rebuild the derived rows. The query-plan test now pins
that a profile pages by the actor index.
What deliberately does not happen
- Git commit authors are not linked and commits are not counted. A commit author is a name somebody typed, not the credential that proves an account, and the index keeps only the top eight authors per default branch. Release events are left out of a profile for the same reason: a tag’s tagger is a Git identity.
- No keys, passkeys, sessions, or view counters. The instance role is shown to instance admins only, because they can see it on the admin pages already.
@nameis linked only where CI evaluates mentions — issue and change bodies, comments, release notes. READMEs and documentation files are not linked, so an example in prose does not become a person.
Mention linking
The scanner in metadata is now EachMention, used by both Mentions (the
event payload) and the renderer, so what the page links and what wakes a workflow
cannot drift apart. The renderer rewrites the parsed tree rather than the
markdown source, which keeps inline code, fenced blocks, existing links and
backslash escapes intact. Only names that belong to an account become links, so a
typo does not produce a 404 somebody can click.
Names elsewhere come from a new user template helper and a shared userlink
partial. who is unchanged for the places a link would be wrong: inside another
link, a button, an <option>, or a data attribute. Clickable avatars are marked
aria-hidden and tabindex="-1" so the name stays the one stop.
Verification
vole workflow run verify passes: format, vet, build, license check,
reachability scan, packaging, link checks, and the race test suite.
New tests: profile totals and filters, the anonymous-vs-owner visibility rule on
a real two-repository instance, unknown names, retired accounts, directory
ordering and search, user resolving only real accounts, and mention linking
across emails, URLs, inline code, fenced blocks, escapes, unknown names, and
links. Index tests cover the actor reads, the release exclusion, and the query
plan.
Checklist
-
vole workflow check -
vole workflow run verify - Docs:
docs/accounts-and-access.md, the mention rule indocs/issues.md - No new dependency, so
licenses.tomlandNOTICEare unchanged