Skip to content

Commit f32a1f0

Browse files
author
dev-pd
committed
Memory UI: split playbook and diary into two independently scrollable grids
1 parent 9242b53 commit f32a1f0

2 files changed

Lines changed: 121 additions & 73 deletions

File tree

frontend/src/App.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,25 @@ h3 {
248248
padding: 1rem;
249249
}
250250

251+
.memory-grid {
252+
display: grid;
253+
grid-template-columns: 1fr 1fr;
254+
gap: 1rem;
255+
align-items: start;
256+
}
257+
@media (max-width: 900px) {
258+
.memory-grid {
259+
grid-template-columns: 1fr;
260+
}
261+
}
262+
.memory h3 {
263+
margin-top: 0;
264+
}
265+
.memory .scroll {
266+
max-height: 65vh;
267+
overflow-y: auto;
268+
}
269+
251270
.alert {
252271
border: 1px solid #f3c2c2;
253272
background: #fdf3f3;

frontend/src/components/MemoryPanel.tsx

Lines changed: 102 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,87 +7,116 @@ const CONF_HELP =
77

88
const BUCKET_CLASS = { low: 'sev-low', medium: 'sev-med', high: 'sev-high' } as const
99

10+
function Playbook({ playbook, diary }: { playbook: PlaybookEntry[]; diary: DiaryEntry[] }) {
11+
const diaryById = new Map(diary.map((d) => [d.id, d]))
12+
return (
13+
<section className="memory">
14+
<h3>
15+
Playbook — promoted rules ({playbook.length}){' '}
16+
<span className="info" title={CONF_HELP}>
17+
18+
</span>
19+
</h3>
20+
<div className="scroll">
21+
{playbook.length === 0 ? (
22+
<p className="placeholder">
23+
Nothing promoted yet. The janitor promotes a pattern once it recurs across ≥3 runs
24+
(confidence ≥ 40%). Patterns build up in the diary first.
25+
</p>
26+
) : (
27+
<ul className="rules">
28+
{playbook.map((entry, i) => {
29+
const cites = entry.source_ids
30+
.map((id) => diaryById.get(id))
31+
.filter(Boolean) as DiaryEntry[]
32+
return (
33+
<li key={`${entry.page_kind}-${entry.rec_type}-${entry.target}-${i}`} className="rule">
34+
<div className="rule-head">
35+
<span className="tag">
36+
{entry.page_kind} · {entry.rec_type}
37+
</span>
38+
<span
39+
className={`conf ${BUCKET_CLASS[entry.confidence_bucket]}`}
40+
title={CONF_HELP}
41+
>
42+
{entry.confidence}% · {entry.confidence_bucket}
43+
</span>
44+
<span className="seen">seen {entry.times_seen}×</span>
45+
</div>
46+
<p className="rule-text">{entry.rule}</p>
47+
<details className="cites">
48+
<summary>
49+
learned from {entry.source_ids.length} observation
50+
{entry.source_ids.length === 1 ? '' : 's'}
51+
{entry.source_ids.length > 0 && ` (rows ${entry.source_ids.join(', ')})`}
52+
</summary>
53+
<ul>
54+
{cites.map((d) => (
55+
<li key={d.id}>
56+
<code>{d.id}</code> · conf {d.confidence} · run {d.run}
57+
{d.keyword && ` · "${d.keyword}"`}
58+
<br />
59+
{d.rec_type} (target: {d.target}){d.why && ` — ${d.why}`}
60+
</li>
61+
))}
62+
</ul>
63+
</details>
64+
</li>
65+
)
66+
})}
67+
</ul>
68+
)}
69+
</div>
70+
</section>
71+
)
72+
}
73+
74+
function Diary({ diary }: { diary: DiaryEntry[] }) {
75+
return (
76+
<section className="memory">
77+
<h3>Diary — raw observations ({diary.length})</h3>
78+
<div className="scroll">
79+
{diary.length === 0 ? (
80+
<p className="placeholder">
81+
Nothing observed yet. Run an audit, or turn on background learning.
82+
</p>
83+
) : (
84+
<ul className="rules">
85+
{[...diary]
86+
.sort((a, b) => b.run - a.run)
87+
.map((d) => (
88+
<li key={d.id} className="rule">
89+
<div className="rule-head">
90+
<span className="tag">
91+
{d.page_kind} · {d.rec_type}
92+
</span>
93+
<span className="conf">conf {d.confidence}</span>
94+
<span className="seen">run {d.run}</span>
95+
</div>
96+
<p className="rule-text">
97+
target: {d.target}
98+
{d.why && ` — ${d.why}`}
99+
</p>
100+
</li>
101+
))}
102+
</ul>
103+
)}
104+
</div>
105+
</section>
106+
)
107+
}
108+
10109
export function MemoryPanel({
11110
playbook,
12111
diary,
13112
}: {
14113
playbook: PlaybookEntry[]
15114
diary: DiaryEntry[]
16115
}) {
17-
const diaryById = new Map(diary.map((d) => [d.id, d]))
18-
19116
return (
20-
<div className="memory">
21-
<h3>
22-
Agent B's playbook — learned rules ({playbook.length}){' '}
23-
<span className="info" title={CONF_HELP}></span>
24-
</h3>
25-
{playbook.length === 0 ? (
26-
<p className="placeholder">
27-
No rules learned yet. B writes one structured observation per run; the janitor
28-
promotes patterns that recur (≥3 runs, confidence ≥ 40%) into the playbook.
29-
</p>
30-
) : (
31-
<ul className="rules">
32-
{playbook.map((entry, i) => {
33-
const cites = entry.source_ids.map((id) => diaryById.get(id)).filter(Boolean) as DiaryEntry[]
34-
return (
35-
<li key={`${entry.page_kind}-${entry.rec_type}-${entry.target}-${i}`} className="rule">
36-
<div className="rule-head">
37-
<span className="tag">{entry.page_kind} · {entry.rec_type}</span>
38-
<span className={`conf ${BUCKET_CLASS[entry.confidence_bucket]}`} title={CONF_HELP}>
39-
{entry.confidence}% · {entry.confidence_bucket}
40-
</span>
41-
<span className="seen">seen {entry.times_seen}×</span>
42-
</div>
43-
<p className="rule-text">{entry.rule}</p>
44-
<details className="cites">
45-
<summary>
46-
learned from {entry.source_ids.length} observation
47-
{entry.source_ids.length === 1 ? '' : 's'}
48-
{entry.source_ids.length > 0 && ` (rows ${entry.source_ids.join(', ')})`}
49-
</summary>
50-
<ul>
51-
{cites.map((d) => (
52-
<li key={d.id}>
53-
<code>{d.id}</code> · conf {d.confidence} · run {d.run}
54-
{d.keyword && ` · "${d.keyword}"`}
55-
<br />
56-
{d.rec_type} (target: {d.target}){d.why && ` — ${d.why}`}
57-
</li>
58-
))}
59-
</ul>
60-
</details>
61-
</li>
62-
)
63-
})}
64-
</ul>
65-
)}
66-
67-
<h3>Raw observations — diary ({diary.length})</h3>
68-
{diary.length === 0 ? (
69-
<p className="placeholder">Nothing observed yet. Run an audit, or turn on background learning.</p>
70-
) : (
71-
<ul className="rules">
72-
{[...diary]
73-
.sort((a, b) => b.run - a.run)
74-
.map((d) => (
75-
<li key={d.id} className="rule">
76-
<div className="rule-head">
77-
<span className="tag">
78-
{d.page_kind} · {d.rec_type}
79-
</span>
80-
<span className="conf">conf {d.confidence}</span>
81-
<span className="seen">run {d.run}</span>
82-
</div>
83-
<p className="rule-text">
84-
target: {d.target}
85-
{d.why && ` — ${d.why}`}
86-
</p>
87-
</li>
88-
))}
89-
</ul>
90-
)}
117+
<div className="memory-grid">
118+
<Playbook playbook={playbook} diary={diary} />
119+
<Diary diary={diary} />
91120
</div>
92121
)
93122
}

0 commit comments

Comments
 (0)