Every /s/<token> hit was already recorded as a share-kind read, carrying
both a count and a timestamp — and then thrown away at the UI layer. The
Public links table showed only who shared a file and when, six inches
below a file header that already said "1 shared". Two personas filed it
independently on the same tour.
The number now rides the shares list:
* ReadLedger.ShareOpens(project) aggregates share-kind buckets per path,
all-time. Share-kind only is what makes Last mean *last opened* —
HeatEntry.LastRead is cross-kind, so a member viewing the file in the
hub would otherwise move the date.
* shareJSON takes the project's opens map, built ONCE per project by the
caller and indexed per row. Both callers — the project list and the
org-wide audit — hoist it above their loops; a per-share call would be
a full byKey scan per row.
* Counts, never openers. The share actor is token+"/"+IP, a public
credential joined to an IP, and it stays in the ledger. There is no
distinct-openers field, deliberately.
* Reads off means the keys are ABSENT, not zero: `0` would claim nobody
has opened a link on a hub that never looked.
shareDetail() is the leverage — the settings table, the org-wide audit and
the file page's share banner all render through it, so one string function
covers three surfaces. Once the row carried the receipt it truncated to
"3 op…", so the detail cell wraps instead of ellipsizing; the path keeps
its ellipsis, since it is a link with a tooltip and the column that can be
arbitrarily long.
Counted per FILE, not per link: heat is keyed by path, so two tokens on one
file report the same number. Worded that way in the section copy, alongside
the other honesty — opens are debounced visits, not requests.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Architecture diagrams
Mermaid diagrams of the current implementation, kept next to the code so PRs can update them alongside the change.
Convention: when a PR changes the structure drawn here (new/removed types, new seams, changed relationships), update the affected diagram in the same PR and add an "Architecture changes" section to the PR description that, per changed diagram:
- names exactly which types/relationships changed and how (one sentence);
- shows a Before and an After mermaid block — each an excerpt of only the affected classes and their immediate relationships, never the full diagram (Before comes from the diagram at the merge base).
The committed diagram file stays the full current state; the before/after
excerpts exist only in the PR description so reviewers see the structural
delta at a glance. A pre-PR hook (.claude/hooks/check-arch-diagrams.sh)
reminds Claude Code sessions when server code changed but no diagram did.
Together these cover every application package in the repo — every code change lands inside exactly one detail diagram's scope (plus the overview when the package map or cross-piece wiring changes):
- overview.md — system diagram: every package and surface on one page, and how they connect
- cli-sync.md — class diagram of the CLI and sync engine (
cmd/bdrive+internal/{syncer,store,journal,config,daemon,agenthooks,autostart}) - webapp-server.md — class diagram of the
bdrive serveserver (internal/webapp+ itsinternal/remoteseam) - webapp-frontend.md — module diagram of the hub's React SPA (
internal/webapp/frontend/src)
Not covered on purpose: web/docs (content site, no application code) and
cloud/ (private nested repo — its architecture lives there).