personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4from __future__ import annotations
5
6from pathlib import Path
7
8from solstone.convey.day_grid import build_day_grid_payload
9
10
11def test_day_grid_payload_splits_on_watermark_cut_line():
12 counts = {
13 "20400101": 2,
14 "20400103": 5,
15 "20400104": 7,
16 }
17
18 payload = build_day_grid_payload(counts, "20400103")
19
20 assert set(payload) == {"coverage", "days", "pending"}
21 assert payload["days"] == {
22 "20400101": 2,
23 "20400103": 5,
24 }
25 assert payload["pending"] == {"20400104": 7}
26 assert payload["coverage"] == {"start": "20400101", "end": "20400104"}
27
28
29def test_day_grid_payload_uses_watermark_not_rollup_membership():
30 counts = {
31 "20400201": 4,
32 "20400205": 9,
33 }
34
35 payload = build_day_grid_payload(counts, "20400210")
36
37 assert payload["days"] == counts
38 assert payload["pending"] == {}
39 assert payload["coverage"] == {"start": "20400201", "end": "20400205"}
40
41
42def test_day_grid_payload_without_watermark_marks_everything_pending():
43 counts = {
44 "20400302": 8,
45 "20400309": 1,
46 }
47
48 payload = build_day_grid_payload(counts, None)
49
50 assert payload["days"] == {}
51 assert payload["pending"] == counts
52 assert payload["coverage"] == {"start": "20400302", "end": "20400309"}
53
54
55def test_day_grid_payload_empty_counts_has_no_coverage():
56 payload = build_day_grid_payload({}, "20400401")
57
58 assert payload == {"coverage": None, "days": {}, "pending": {}}
59
60
61def test_day_grid_payload_generalizes_to_apps_without_pending_days():
62 counts = {
63 "20400507": 3,
64 "20400511": 6,
65 }
66
67 payload = build_day_grid_payload(counts, max(counts))
68
69 assert payload["days"] == counts
70 assert payload["pending"] == {}
71 assert payload["coverage"] == {"start": "20400507", "end": "20400511"}
72
73
74def test_day_grid_payload_uses_explicit_coverage_override():
75 counts = {
76 "20400610": 4,
77 "20400612": 2,
78 }
79
80 payload = build_day_grid_payload(
81 counts,
82 max(counts),
83 coverage={"start": "20400101", "end": "20401231"},
84 )
85
86 assert set(payload) == {"coverage", "days", "pending"}
87 assert payload["coverage"] == {"start": "20400101", "end": "20401231"}
88 assert payload["days"] == counts
89 assert payload["pending"] == {}
90
91
92def test_day_grid_payload_omits_activity_by_default():
93 payload = build_day_grid_payload({"20400610": 4}, "20400610")
94
95 assert set(payload) == {"coverage", "days", "pending"}
96
97
98def test_day_grid_payload_includes_activity_when_supplied():
99 payload = build_day_grid_payload(
100 {"20400610": 4},
101 "20400610",
102 coverage={"start": "20400601", "end": "20400630"},
103 activity={"20400610": "8"},
104 )
105
106 assert payload == {
107 "coverage": {"start": "20400601", "end": "20400630"},
108 "days": {"20400610": 4},
109 "pending": {},
110 "activity": {"20400610": 8},
111 }
112
113
114def test_day_grid_payload_activity_does_not_expand_inferred_coverage():
115 payload = build_day_grid_payload(
116 {"20400610": 4},
117 "20400610",
118 activity={"20400601": 2, "20400630": 3},
119 )
120
121 assert payload["coverage"] == {"start": "20400610", "end": "20400610"}
122 assert payload["activity"] == {"20400601": 2, "20400630": 3}
123
124
125def test_day_grid_payload_empty_counts_accepts_empty_corpus_coverage_override():
126 payload = build_day_grid_payload(
127 {},
128 None,
129 coverage={"start": "20400701", "end": "20400731"},
130 )
131
132 assert payload == {
133 "coverage": {"start": "20400701", "end": "20400731"},
134 "days": {},
135 "pending": {},
136 }
137
138
139def test_day_grid_css_uses_neutral_pending_marker():
140 css_path = (
141 Path(__file__).resolve().parents[1]
142 / "solstone"
143 / "convey"
144 / "static"
145 / "day-grid.css"
146 )
147
148 assert "--warn" not in css_path.read_text(encoding="utf-8")
149
150
151def test_day_grid_css_pins_presence_tone():
152 css_path = (
153 Path(__file__).resolve().parents[1]
154 / "solstone"
155 / "convey"
156 / "static"
157 / "day-grid.css"
158 )
159 css = css_path.read_text(encoding="utf-8")
160
161 assert ".daygrid-cell--presence" in css
162 assert ".daygrid-legend-swatch--presence" in css
163 assert "color-mix(in srgb, var(--orange) 58%, var(--hairline))" in css
164 assert "color-mix(in srgb, var(--orange) 72%, var(--orange-wash))" in css
165
166
167def test_day_grid_js_pins_autoscroll_overflow_guard():
168 js_path = (
169 Path(__file__).resolve().parents[1]
170 / "solstone"
171 / "convey"
172 / "static"
173 / "day-grid.js"
174 )
175 js = js_path.read_text(encoding="utf-8")
176
177 assert "const MIN_AUTOSCROLL_OVERFLOW = 24;" in js
178 assert "if (maxScroll > MIN_AUTOSCROLL_OVERFLOW) {" in js