oficina / memoria Public

Clone
git clone https://amadan.net/oficina/memoria

Plain git — no account needed to clone.

Download

Download this file

1package memoria
2
3import (
4 "context"
5 "testing"
6
7 "amadan.net/oficina/suite"
8
9 "amadan.net/oficina/memoria/internal/memoria/sibling"
10)
11
12// perItemCustody rules each candidate on its own item id.
13//
14// **fixedCustody cannot do this**, and that is why this type exists
15// rather than a field being added to it: it returns sibling.Decide(give)
16// for every candidate from ONE field, so every fixture it builds has all
17// its passages visible or none of them. Gate 34 is entirely about pages
18// where some are and some are not, and drafts 1 and 2 of §8.8 both said
19// the gate could run against "memoriatest's sibling stub, which exists".
20// No such stub exists. §10 opens by naming that failure — a gate naming a
21// test that was renamed away — and the section citing it committed it
22// twice.
23type perItemCustody struct {
24 apps map[string]sibling.Sibling
25 // give is keyed by item id. A candidate whose item id is absent is
26 // VISIBLE, so a fixture names only the exceptions it cares about.
27 give map[string]suite.Authorization
28}
29
30func (p *perItemCustody) Sibling(source string) (sibling.Sibling, bool) {
31 s, ok := p.apps[source]
32 return s, ok
33}
34
35func (p *perItemCustody) Rule(_ context.Context, _ suite.Subject, cands []sibling.Candidate) []sibling.Disposition {
36 out := make([]sibling.Disposition, len(cands))
37 for i, c := range cands {
38 a, ok := p.give[c.Item.ItemID]
39 if !ok {
40 a = suite.VISIBLE
41 }
42 out[i] = sibling.Decide(a)
43 }
44 return out
45}
46
47// eircodeCorpus stores one sibling document per (title, eircode) pair,
48// each under its own item id so the fake above can rule them apart.
49func eircodeCorpus(t *testing.T, cus Custody, docs [][3]string) *Store {
50 t.Helper()
51 s := storeForTest(t)
52 s.custody = cus
53 for _, d := range docs {
54 if _, err := s.Ingest(context.Background(), IngestSpec{
55 Source: "Docs", Key: d[0], Title: d[1],
56 Body: "## Contact\n\nEircode: " + d[2],
57 ItemID: d[0],
58 SiblingRevision: 1,
59 }); err != nil {
60 t.Fatalf("ingest %s: %v", d[0], err)
61 }
62 }
63 return s
64}
65
66func searchFacts(t *testing.T, s *Store, q string) Page {
67 t.Helper()
68 p, err := s.Search(context.Background(),
69 suite.Subject{Kind: suite.SubjectAddress, Value: "paul@tito.io"}, q, 25)
70 if err != nil {
71 t.Fatalf("search: %v", err)
72 }
73 return p
74}
75
76func spans(p Page) []string {
77 var out []string
78 for _, f := range p.Facts {
79 out = append(out, f.Span)
80 }
81 return out
82}
83
84// GATE 34 — the box never speaks a withheld passage, and every count
85// happens after the ruling (§8.8, §10).
86//
87// **Asserts the struct, never the rendered copy.** Two dispositions that
88// render an identical page is how Outcome.Record filed silent rulings
89// undetected (§1.9); a page assertion here could not tell case 2 from
90// case 3 at all.
91//
92// Mutations run, each verified red and reverted, 2026-09-14:
93// - addFacts ranging the pre-ruling hits instead of p.Results — cases
94// 2, 4, 5 and 6 go red.
95// - the cap counted before the ruling — case 4 goes red alone.
96// - the collision check computed over the unruled set — case 6 alone.
97// - citing r.DocumentID of the first hit rather than the first RESULT —
98// case 5 alone, and no other fixture can see it because every other
99// one uses distinct values per document.
100func TestGate34TheFactBoxNeverSpeaksAWithheldPassage(t *testing.T) {
101 roster := docsRoster()
102
103 // 1. PRESENCE. Both visible, two distinct eircodes, two candidates.
104 // Without this every absence below is an assertion about an empty
105 // world — §10's second rule, and gate 4's lesson.
106 t.Run("presence: two visible passages give two candidates", func(t *testing.T) {
107 s := eircodeCorpus(t, &perItemCustody{apps: roster}, [][3]string{
108 {"a", "Office A", "D02 X285"},
109 {"b", "Office B", "A65 F4E2"},
110 })
111 if got := spans(searchFacts(t, s, "eircode")); len(got) != 2 {
112 t.Fatalf("spans = %q, want two", got)
113 }
114 })
115
116 // 2. NOT_VISIBLE IS SILENT. §4.1's table makes OmitSilently the only
117 // disposition that may be silent, and Outcome.Record implements
118 // exactly that. Draft 1 of §8.8 demanded a notice here, which is
119 // either unreachable against correct code or an instruction to build
120 // the existence oracle §4.1 exists to deny. **The empty-notice
121 // assertion is the load-bearing half of this case.**
122 t.Run("not_visible: the survivor is boxed and nothing is said", func(t *testing.T) {
123 s := eircodeCorpus(t, &perItemCustody{
124 apps: roster,
125 give: map[string]suite.Authorization{"b": suite.NOT_VISIBLE},
126 }, [][3]string{
127 {"a", "Office A", "D02 X285"},
128 {"b", "Office B", "A65 F4E2"},
129 })
130 p := searchFacts(t, s, "eircode")
131 if got := spans(p); len(got) != 1 || got[0] != "D02 X285" {
132 t.Fatalf("spans = %q, want just the visible one", got)
133 }
134 if len(p.Notices) != 0 {
135 t.Errorf("notices = %q, want none: a NOT_VISIBLE is the one value that may be silent", p.Notices)
136 }
137 })
138
139 // 3. UNKNOWN IS LOUD. The same fixture, the other disposition. This
140 // is what draft 1 meant and attached to the wrong one.
141 t.Run("unknown: the survivor is boxed and the page says so", func(t *testing.T) {
142 s := eircodeCorpus(t, &perItemCustody{
143 apps: roster,
144 give: map[string]suite.Authorization{"b": suite.UNKNOWN},
145 }, [][3]string{
146 {"a", "Office A", "D02 X285"},
147 {"b", "Office B", "A65 F4E2"},
148 })
149 p := searchFacts(t, s, "eircode")
150 if got := spans(p); len(got) != 1 || got[0] != "D02 X285" {
151 t.Fatalf("spans = %q, want just the visible one", got)
152 }
153 if len(p.Notices) == 0 {
154 t.Error("want a notice: an UNKNOWN is withheld loudly (§6.5)")
155 }
156 })
157
158 // 4. THE CAP COUNTS AFTER THE RULING. Four values, one withheld,
159 // three left — a box. Counted before the ruling there are four, the
160 // cap fires, and the reader loses the box because of a document they
161 // may not see. **No two-document fixture can tell those apart**,
162 // which is why this one has four.
163 t.Run("the cap counts the ruled set", func(t *testing.T) {
164 s := eircodeCorpus(t, &perItemCustody{
165 apps: roster,
166 give: map[string]suite.Authorization{"d": suite.NOT_VISIBLE},
167 }, [][3]string{
168 {"a", "Office A", "D02 X285"},
169 {"b", "Office B", "A65 F4E2"},
170 {"c", "Office C", "D6W 1234"},
171 {"d", "Office D", "T12 XY45"},
172 })
173 if got := spans(searchFacts(t, s, "eircode")); len(got) != 3 {
174 t.Fatalf("spans = %q, want three", got)
175 }
176 })
177
178 // 5. EVERY FIELD OF THE CITATION IS THE SURVIVOR'S. The SAME eircode
179 // in a visible and a withheld document: one candidate, and all five
180 // of its fields from the visible one.
181 //
182 // **Asserting Title alone was not enough**, and a review mutation
183 // proved it: taking Title from the ruled result and DocumentID,
184 // Ordinal, Label and Trail from the first UNRULED occurrence passed
185 // this case, and would have rendered an href to a document the
186 // reader may not open, under the withheld document's own heading and
187 // label text. The two bodies were identical, so nothing could tell
188 // the fields apart. They differ now, and §10 says assert the tuple.
189 t.Run("every field of the citation is the visible passage's", func(t *testing.T) {
190 cus := &perItemCustody{
191 apps: roster,
192 give: map[string]suite.Authorization{"hidden": suite.NOT_VISIBLE},
193 }
194 s := storeForTest(t)
195 s.custody = cus
196 ctx := context.Background()
197 // The withheld one is ingested first AND carries the query's
198 // words three times, so it ranks above the visible one: if any
199 // field came from the hits rather than the results, it would be
200 // this document's.
201 hidden, err := s.Ingest(ctx, IngestSpec{
202 Source: "Docs", Key: "hidden", Title: "Hidden office",
203 Body: "## Hidden trail\n\nHidden eircode eircode eircode label: D02 X285",
204 ItemID: "hidden", SiblingRevision: 1,
205 })
206 if err != nil {
207 t.Fatalf("ingest hidden: %v", err)
208 }
209 shown, err := s.Ingest(ctx, IngestSpec{
210 Source: "Docs", Key: "shown", Title: "Visible office",
211 Body: "## Contact\n\nEircode: D02 X285",
212 ItemID: "shown", SiblingRevision: 1,
213 })
214 if err != nil {
215 t.Fatalf("ingest shown: %v", err)
216 }
217 if hidden == shown {
218 t.Fatal("the two documents share an id; this fixture cannot be wrong about anything")
219 }
220 p := searchFacts(t, s, "eircode")
221 if len(p.Facts) != 1 {
222 t.Fatalf("facts = %+v, want one", p.Facts)
223 }
224 got := p.Facts[0]
225 for _, c := range []struct{ name, got, want string }{
226 {"title", got.Title, "Visible office"},
227 {"trail", got.Trail, "Contact"},
228 {"label", got.Label, "Eircode"},
229 } {
230 if c.got != c.want {
231 t.Errorf("%s = %q, want %q: the box took a field from the passage this reader may not read", c.name, c.got, c.want)
232 }
233 }
234 if got.DocumentID != shown {
235 t.Errorf("citation links to document %d, want %d: the href points at the withheld document", got.DocumentID, shown)
236 }
237 // The fifth field, which the comment above claimed and the
238 // assertions did not make. Both documents here are single-chunk
239 // so it cannot currently differ — asserted anyway, because the
240 // claim should be true and the next fixture may have two.
241 if got.Ordinal != 0 {
242 t.Errorf("ordinal = %d, want 0", got.Ordinal)
243 }
244 })
245
246 // 7. A WITHHELD PASSAGE CANNOT SILENCE A KIND. §8.8's rule 4 — a
247 // document-authored label too long to show renders no box for that
248 // kind — is a count like the cap and the collision check, and it has
249 // to happen after the ruling for the same reason. Computed over the
250 // unruled set, a document the reader may not see takes the box away
251 // from them, which reports that document's existence.
252 //
253 // **No other fixture here has a NoBox candidate at all**, so a
254 // mutation OR-ing `silenced` from the unruled set passed all six of
255 // them. Found by review, 2026-09-14.
256 t.Run("a withheld over-long label does not silence the kind", func(t *testing.T) {
257 cus := &perItemCustody{
258 apps: roster,
259 give: map[string]suite.Authorization{"hidden": suite.NOT_VISIBLE},
260 }
261 s := storeForTest(t)
262 s.custody = cus
263 ctx := context.Background()
264 for _, d := range [][3]string{
265 // Over sixty characters, and carrying the query's word so it
266 // is actually retrieved — the first version of this fixture
267 // was green because nothing ever reached the box.
268 {"hidden", "Hidden office", "The eircode you must never give to any customer or supplier is: T12 XY45"},
269 {"shown", "Visible office", "Eircode: D02 X285"},
270 } {
271 if _, err := s.Ingest(ctx, IngestSpec{
272 Source: "Docs", Key: d[0], Title: d[1],
273 Body: "## Contact\n\n" + d[2],
274 ItemID: d[0], SiblingRevision: 1,
275 }); err != nil {
276 t.Fatalf("ingest %s: %v", d[0], err)
277 }
278 }
279 if got := spans(searchFacts(t, s, "eircode")); len(got) != 1 || got[0] != "D02 X285" {
280 t.Fatalf("spans = %q, want the visible one boxed", got)
281 }
282 })
283
284 // 6. COLLISION COUNTS AFTER THE RULING TOO. Two documents sharing a
285 // title and a trail, both falling to the trail for a label: over the
286 // unruled set they collide and the box vanishes, which is the same
287 // leak as case 4 wearing the collision rule's coat.
288 t.Run("collision counts the ruled set", func(t *testing.T) {
289 cus := &perItemCustody{
290 apps: roster,
291 give: map[string]suite.Authorization{"twin-b": suite.NOT_VISIBLE},
292 }
293 s := storeForTest(t)
294 s.custody = cus
295 ctx := context.Background()
296 for i, d := range [][2]string{{"twin-a", "D02 X285"}, {"twin-b", "A65 F4E2"}} {
297 if _, err := s.Ingest(ctx, IngestSpec{
298 Source: "Docs", Key: d[0], Title: "Offices",
299 // Mid-sentence and with no separator before it, so the
300 // label falls to the heading trail and the two rows
301 // would be identical but for their values.
302 Body: "## Contact\n\nThe eircode " + d[1] + " reaches us.",
303 ItemID: d[0],
304 SiblingRevision: int64(i) + 1,
305 }); err != nil {
306 t.Fatalf("ingest %s: %v", d[0], err)
307 }
308 }
309 if got := spans(searchFacts(t, s, "eircode")); len(got) != 1 {
310 t.Fatalf("spans = %q, want the one survivor boxed", got)
311 }
312 })
313}
314