coverage~bigbes/sr-ht-doltede3b0bbweb/memory.go

Coverage
88.9% 16/18 statements
Δ
Blob
dffa496
Uncovered L57-L58L91-L93
1 package web
2
3 import (
4 "context"
5 "log/slog"
6 "net/http"
7 "net/url"
8
9 "go.bigb.es/auxilia/scribe"
10
11 "sourcecraft.dev/bigbes/sr-ht-dolt/beads"
12 "sourcecraft.dev/bigbes/sr-ht-dolt/browse"
13 "sourcecraft.dev/bigbes/sr-ht-dolt/core"
14 )
15
16 // memoryView renders the memories `bd remember` writes: config rows keyed
17 // kv.memory.<slug>, each with the revision its value was last written at. The
18 // row itself is (key, value) and carries no timestamp, so the date comes out of
19 // the history — see beads.BuildMemories for the walk that finds it.
20 //
21 // The reading is not here. It lives in the beads package, which the MCP
22 // surface's list_memories shares; this type is only the View adapter — slug,
23 // label, template, and the hand-off of the request's ref and query.
24 type memoryView struct {
25 // links is the cross-database link index's cache, held for the same reasons
26 // beadsView holds one (see web/beads.go): the view is this rendering's one
27 // piece of per-process state, a zero value is a working cache, and what is
28 // cached is a fact about a database rather than about who may read it.
29 //
30 // It is a second cache and not the board's, because it is a second page: the
31 // two are read at different times and neither should be able to expire the
32 // other's entries. What they share is the projection's shape, which is why
33 // they share its type.
34 links beads.PrefixCache
35 }
36
37 48 func (*memoryView) Name() string { return "memory" }
38 30 func (*memoryView) Label() string { return "Memory" }
39 15 func (*memoryView) Template() string { return "memory.html" }
40
41 // Applies is the beads fingerprint plus a config table carrying key and value;
42 // see beads.AppliesMemories. Applies sees table shapes and never rows, so a
43 // tracker that has never had a memory written into it still gets the tab and
44 // renders an empty state — the same contract Milestones has.
45 37 func (*memoryView) Applies(tables []browse.TableInfo) bool { return beads.AppliesMemories(tables) }
46
47 // Build reads the memories and dates them from the history. The result is a
48 // *beads.MemoryView, handed to memory.html as its .Data.
49 //
50 // The clock is passed in rather than read inside the projection: staleness is
51 // the one thing on this page that depends on when it was rendered, and timeNow
52 // is the same swappable clock the freshness line uses, so a test pins both at
53 // once.
54 15 func (*memoryView) Build(ctx context.Context, sess BrowseSession, _ *core.Repo, ref string, query url.Values) (any, error) {
55 15 data, err := beads.BuildMemories(ctx, sess, ref, query, timeNow())
56 15 if err != nil {
57 0 return nil, err
58 0 }
59 15 return data, nil
60 }
61
62 // memoryCrossLinks builds the link index the memory bodies are rendered against,
63 // and returns nil for every page that is not the memory view. A nil one still
64 // renders the markdown; what it costs is the links, which is what an unreadable
65 // listing may cost and no more (see linkableDatabases).
66 //
67 // It is asked for by this page rather than built into the envelope because
68 // building it opens a store per database the caller may browse — the same reason
69 // the issue links are asked for by the detail pane alone.
70 //
71 // The index answers both halves of what a memory body cites: the [[slug]]
72 // references and the issue ids in the prose. They come out of one read of one
73 // table per database, which is why there is one index here and not two.
74 65 func (a *app) memoryCrossLinks(r *http.Request, view View, repo *core.Repo) *memoryLinks {
75 65 mv, ok := view.(*memoryView)
76 65 if !ok {
77 50 return nil
78 50 }
79 15 dbs, open, ok := a.linkableDatabases(r, repo)
80 15 if !ok {
81 1 return nil
82 1 }
83
84 14 index := beads.PrefixesAcross(r.Context(), dbs, open, &mv.links, timeNow())
85 14
86 14 // A store that cannot be read is a fact about this deployment and belongs in
87 14 // the log with its error. The page says nothing at all: the references that
88 14 // database holds simply stop resolving, exactly as they do for a database this
89 14 // caller may not browse.
90 14 for _, f := range index.Failed {
91 0 slog.Warn("reading a database for the memory link index failed",
92 0 "component", "web", "database", f.Database.Slug(), scribe.Err(f.Err))
93 0 }
94 14 return &memoryLinks{index: index}
95 }