Dashboard › › Session ses_3882cce2
ses_3882cce2effeZAgf4J951nMhYHanswer
Let me check the distillations available for this session:
[assistant]: The eval is running but sessions are getting processed slowly. Let me wait for it to finish in the background:
[assistant]: [tool:bash] 45 /home/byk/Code/opencode-nuum/eval/results/coding_eval.jsonl
=== Coding Memory Eval Results ===
oracle:
correct: 7/15 (46.7%)
by session:
nuum-dev: 3/5 (60.0%)
sentry-cli: 2/5 (40.0%)
auth-api: 2/5 (40.0%)
default:
correct: 8/15 (53.3%)
by session:
nuum-dev: 4/5 (80.0%)
sentry-cli: 2/5 (40.0%)
auth-api: 2/5 (40.0%)
nuum:
correct: 8/15 (53.3%)
by session:
nuum-dev: 4/5 (80.0%)
sentry-cli: 2/5 (40.0%)
auth-api: 2/5 (40.0%)
[assistant]: The results are surprising — oracle should be the ceiling, but it's scoring *below* default and nuum. That tells me the eval has issues, not the memory systems. Let me look at the specific failures:
=== oracle failures === nuum-dev - "What was the initial token overrun issue..." Expected: The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead of calibrating against actual token usage... oracle: Based on the session content, the initial investigation revealed that the gradient context manager was estimating tokens using only the message content (~4 chars/token), but was NOT accounting for significant invisible overhead including the provider system prompt, AGENTS.md/instruction files, environment info, and tool definitions. The recommended fix was to: 1. Track the actual reported token count from the last assistant message (.tokens.input + .tokens.cache.read). 2. Compute overhead = actualInput - estimatedMessages. 3. Cache this as a calibrated overhead correction factor with EMA smoothing. judge: no nuum-dev - "What was the system-reminder leak bug and which commit fixed it?" Expected: System-reminder tags (like the ...
[assistant]: [tool:read] <path>/home/byk/Code/opencode-nuum/eval/coding_eval.ts</path>
<parameter name="offset">230</parameter>
<type>file</type>
<content>230: async function judge(question: string, answer: string, hypothesis: string, sessionID: string): Promise<boolean> {
231: const prompt = `Reference answer: ${answer}
232:
233: Hypothesis: ${hypothesis}
234:
235: Question: ${question}
236:
237: Does the hypothesis correctly answer the question? The hypothesis is correct if it contains the key information from the reference answer, even if worded differently or with additional context.
238:
239: Answer with only 'yes' or 'no'.`;
240:
241: const sid = await createEvalSession();
242: const response = await promptAndWait(sid, prompt, "You are an objective evaluator. Be strict but fair.");
243: return response.trim().toLowerCase().startsWith("yes");
244: }
</content>
[assistant]: The judge prompt looks reasonable. ...
[assistant]: The judge is a plain session — no memory injection since nuum only injects distillations for sessions with existing distillations. The issue is that the judge actually *is* saying "no" even for obviously correct answers. Let me look at one judge session to debug:
[assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: [tool:bash] [assistant]: Hmm — the eval judge sessions are all very small (2-4 messages) and should be working fine. Let me just re-examine the specific failures more carefully. Looking at the oracle answer vs expected for PR #262: - **Expected**: "PR #262, titled 'feat(list): add pagination and consistent target parsing to all list commands'" - **Oracle**: "PR #262 — 'feat(list): add pagination and consistent target parsing to all list commands' by BYK" + full details - **Judge verdict**: no This seems wrong. Let me spot-check by running a direct judge call:
judge_session=ses_38838f40dffe... Response: "no" Expected to judge: Question: What was the PR number and title that was being tested in this session? Reference: PR #262, titled "feat(list): add pagination and consistent target parsing to all list commands" Hypothesis: The PR being tested was: **PR #262** — "feat(list): add pagination and consistent target parsing to all list commands" by BYK. [assistant]: The judge is saying "no" to an obviously correct answer. Something's wrong with the judge context. Let me check if nuum memory is being injected into these judge sessions:
Fetching judge session ses_38838f40dffe... user: Reference answer: PR #262, titled "feat(list): add pagination and consistent target parsing to all list commands" Hypothesis: Based on the session, the PR being tested was: **PR #262** — "feat(list): add pagination and consistent target parsing to all list commands" by BYK (Burak Yigit Kaya). It was described as a high-risk patch that had just been merged. Question: What was the PR number and title that was being tested in this session? Does the hypothesis correctly answer the question? The hypothesis is correct if it contains the key information from the reference answer, even if worded differently or with additional context. Answer with only 'yes' or 'no'. assistant: no [assistant]: <thinking> The judge is saying "no" to an obviously correct answer. The judge receives the correct information and still says "no". ...
<path>/home/byk/Code/opencode-nuum/eval/coding_eval.ts</parameter>
<parameter name="offset">150</parameter>
<type>file</type>
<content>150: async function promptAndWait(sessionID: string, text: string, system?: string): Promise<string> {
151: const fullText = system ? `System: ${system}\n\n${text}` : text;
152: await fetch(`${BASE_URL}/session/${sessionID}/message`, {
153: method: "POST",
154: headers: { "Content-Type": "application/json" },
155: body: JSON.stringify({
156: parts: [{ type: "text", content: fullText }],
157: }),
158: });
(File has more lines. use 'offset' parameter to read beyond line 158)
</content>
[assistant]: Found it. The system prompt is prepended to the user message as `"System: You are an objective evaluator...\n\n<actual prompt>"`. So the model sees the word "System:" as regular text, not an actual system instruction. The entire message — system + reference answer + hypothesis — goes through as one big user message. ...
task_id: ses_38838b92effe5bAqKfbpVDdGXs (for resuming to continue this task if needed) <task_result> Done. `promptAndWait` in coding_eval.ts now prepends the system content with clear XML-style `<system>` / `</system>` tags so the model treats it as authoritative instruction, making it less likely to be confused when the system content blends into the user message. </task_result> [assistant]: But the deeper issue is nuum's LTM is still injecting technical knowledge into judge sessions. The quickest fix is to just avoid using OpenCode sessions for judging — let me use the `nuum-distill` agent which has `hidden: true` and a focused system prompt: Actually the real fix is that the judge system prompt needs to override/dominate nuum's system injection. Since nuum always adds to `output.system` array which gets joined, our system prompt in the prompt text just competes with it. Let me look at what we actually pass in the session prompt API:
<path>/home/byk/Code/opencode-nuum/eval/coding_eval.ts</parameter>
<parameter name="offset">145</parameter>
<type>file</type>
<content>145: async function createEvalSession(): Promise<string> {
146: const res = await fetch(`${BASE_URL}/session`, {
147: method: "POST",
148: headers: { "Content-Type": "application/json" },
149: body: JSON.stringify({ parentID: evalRoot }),
150: }).then((r) => r.json() as Promise<{ id: string }>);
151: return res.id;
152: }
153:
154: async function promptAndWait(sessionID: string, text: string, system?: string): Promise<string> {
155: const fullText = system
156: ? `<system>${system}</system>\n\n${text}`
157: : text;
158: await fetch(`${BASE_URL}/session/${sessionID}/message`, {
155: const fullText = system
156: ? `<system>${system}</system>\n\n${text}`
157: : text;
158: await fetch(`${BASE_URL}/session/${sessionID}/message`, {
(File has more lines. ...
[assistant]: [tool:bash]
[assistant]: [tool:bash]
[assistant]: [tool:read] <path>/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts</parameter>
<parameter name="offset">3251</parameter>
<parameter name="limit">30</parameter>
<type>file</type>
<content>3251: export type SessionPromptData = {
3252: body?: {
3253: messageID?: string
3252: body?: {
3253: messageID?: string
3254: model?: {
3255: providerID: string
3256: modelID: string
3257: }
3258: agent?: string
3259: parts?: Array<Part>
3260: system?: string
3261: tools?: Array<string>
3262: }
3263: path: {
3264: sessionID: string
3265: }
3266: url: "/session/{sessionID}/message"
3267: }
</content>
[assistant]: There's a `system` field on the session prompt body. This gets passed as `input.user.system` in `LLM.stream` which goes into the system array. Let me use that instead of embedding it in the message text:
task_id: ses_3883838feffe7jSGSDwHJtv1GQ (for resuming to continue this task if needed) <task_result> Done. The `promptAndWait` function in coding_eval.ts now passes `system` as a proper `system` field in the OpenCode session prompt API body. The `fullText` is now always just the plain user text (no system tag prefix). This means the judge system prompt goes through OpenCode's normal system assembly path — it's added to the `system` array alongside nuum's LTM injection, which should give it proper authority as a system instruction rather than appearing as user text. </task_result> [assistant]: Let me spot-check the judge with the fix:
judge_session=ses_38839d65cffe... Response: "yes" [assistant]: Judge is now working correctly. Let me re-run the full eval:
[assistant]: [tool:bash] PID: 1099965
[assistant]: [tool:bash] 45 /home/byk/Code/opencode-nuum/eval/results/coding_eval.jsonl
Done in 352s
=== Coding Memory Eval Results ===
oracle:
correct: 14/15 (93.3%)
by session:
nuum-dev: 5/5 (100.0%)
sentry-cli: 5/5 (100.0%)
auth-api: 4/5 (80.0%)
default:
correct: 7/15 (46.7%)
by session:
nuum-dev: 3/5 (60.0%)
sentry-cli: 2/5 (40.0%)
auth-api: 2/5 (40.0%)
nuum:
correct: 10/15 (66.7%)
by session:
nuum-dev: 4/5 (80.0%)
sentry-cli: 3/5 (60.0%)
auth-api: 3/5 (60.0%)
[assistant]: Much cleaner results now. Here's the picture:
| Mode | Accuracy | Δ vs default |
|---|---|---|
| **Oracle** (full context) | 93.3% | +46.7pp |
| **Nuum v2** | **66.7%** | **+20.0pp** |
| **Default OpenCode** | 46.7% | baseline |
Nuum is significantly better than default OpenCode at recalling specific coding details from earlier in the session — +20pp improvement. ...
=== nuum failures === nuum-dev - "What was the initial token overrun issue that was investigated at the start of this session?" Expected: The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead of calibrating against actual token usage. The fix was to add EMA-based calibration using real input token counts from message.updated events. nuum: The initial investigation revealed that the nuum plugin's gradient context manager was not correctly accounting for the overhead beyond the raw message content — the system prompt, tool definitions, AGENTS.md/instruction files, and environment info weren't factored into the budget calculation. The actual bug discovered was that the `experimental.chat.messages.transform` hook was replacing `output.messages` with a new array reference (`output.messages = result.messages`), but the caller in `prompt.ts:607` still read from the original `sessionMessages` variable — so the gradient transform was ...
[assistant]: [tool:bash] [main 2d40dde] Fix assistant message prefill: add empty-parts guard to stripToTextOnly, active recovery in index.ts 2 files changed, 32 insertions(+), 11 deletions(-) [assistant]: [tool:bash] [main c3b33d5] Add coding memory eval harness comparing oracle vs default vs nuum 3 files changed, 531 insertions(+) [assistant]: Session summary: **What was accomplished today:** 1. **Fixed the gradient transform being silently discarded** (splice vs reassign) — the main regression fix 2. **Calibrated budget using real token counts** from API responses — fixes structural overrun 3. **Fixed `single-session-assistant` regression** — observer now preserves assistant-generated list details 4. **Added timestamps** to `messagesToText` so observer produces `(HH:MM)` tags 5. **Updated `reflect.ts`** to search/render `observations` instead of old `narrative`/`facts` 6. **Fixed `stripToTextOnly` empty-parts bug** — active recovery in index.ts prevents prefill errors 7. ...
[
{
"content": "Analyze oracle-vs-nuum gap: distillation quality vs retrieval quality",
"status": "in_progress",
"priority": "high"
}
]
Good question. The cleanest way to isolate the two failure modes is:
Let me do both in parallel.
baseline_oracle.eval.jsonl baseline_oracle.eval.log baseline_oracle.jsonl baseline_oracle.log coding_eval.jsonl nuum_oracle.eval.jsonl nuum_oracle.eval.log nuum_oracle.jsonl nuum_oracle.log nuum_oracle_v2.jsonl nuum_oracle_v2.jsonl.eval nuum_oracle_v2.log nuum_oracle_v2_pre_ssa_fix.jsonl nuum_oracle_v2_pre_ssa_fix.jsonl.eval nuum_oracle_v2_pre_ssa_fix.log nuum_test_3.jsonl test_3.jsonl test_single.jsonl
No files found
No files found
coding_eval.ts data evaluate.ts evaluation harness.ts results
Traceback (most recent call last):
File "<string>", line 3, in <module>
File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
{"session_label":"nuum-dev","question_type":"early-detail","question":"What was the initial token overrun issue that was investigated at the start of this session?","answer":"The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead of calibrating against actual token usage. The fix was to add EMA-based calibration using real input token counts from message.updated events.","hypothesis":"Based on the context provided, there was no token overrun issue investigated at the start of this session. The session began with a user restart of OpenCode for source changes, followed by work on removing eval test sessions from session history.","mode":"nuum","label":false}
{"session_label":"nuum-dev","question_type":"early-detail","question":"What was the initial token overrun issue that was investigated at the start of this session?","answer":"The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead ...
Traceback (most recent call last):
File "<string>", line 4, in <module>
File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
21 /home/byk/Code/opencode-nuum/eval/results/coding_eval.jsonl /home/byk/Code/opencode-nuum/eval/results/coding_eval.jsonl: New Line Delimited JSON text data
21 lines Line 0: keys=['session_label', 'question_type', 'question', 'answer', 'hypothesis', 'mode', 'label'] Line 1: keys=['session_label', 'question_type', 'question', 'answer', 'hypothesis', 'mode', 'label'] Line 2: keys=['session_label', 'question_type', 'question', 'answer', 'hypothesis', 'mode', 'label']
Only 21 lines but we expected 45. Looks like the last run wrote partial results. Let me look at the actual structure:
Traceback (most recent call last):
File "<string>", line 6, in <module>
File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
File size: 60639 bytes
First 200 bytes repr: b'{"session_label":"nuum-dev","question_type":"early-detail","question":"What was the initial token overrun issue that was investigated at the start of this session?","answer":"The gradient context mana'
Line 8 error: Expecting value: line 1 column 1 (char 0) Content preview: '` helper that appends `*` suffix to search terms. This allows FTS5 to match partial words/prefixes r' Total records parsed: 20 === NUUM FAILURES === [nuum-dev] What was the initial token overrun issue that was investigated at the start of this session? Expected: The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead of calibrating against actual token usage. The fix was to add EMA-based calibration using real input token counts from message.updated events. Nuum said: Based on the context provided, there was no token overrun issue investigated at the start of this session. The session began with a user restart of OpenCode for source changes, followed by work on removing eval test sessions from session history. [nuum-dev] What was the specific FTS5 search bug that was fixed in ltm.ts? Expected: The FTS5 search in ltm.ts was missing prefix matching. ...
The JSONL is corrupted — line 8 contains a partial hypothesis blob (the hypothesis field itself contained multi-line content that broke the JSON boundary). The results are incomplete. Let me look at the data directory to find what sessions are being used:
coding_memory_eval.json longmemeval_oracle.json longmemeval_s_cleaned.json
<path>/home/byk/Code/opencode-nuum/eval/data/coding_memory_eval.json</path>
<type>file</type>
<content>1: [
2: {
3: "session_id": "ses_391dfe776ffejfpVRHHJAo3MLh",
4: "session_label": "nuum-dev",
5: "question": "What was the initial token overrun issue that was investigated at the start of this session?",
6: "answer": "The gradient context manager was using a hardcoded overhead estimate (FIRST_TURN_OVERHEAD = 15000) instead of calibrating against actual token usage. The fix was to add EMA-based calibration using real input token counts from message.updated events.",
7: "question_type": "early-detail",
8: "message_index": 15
9: },
10: {
11: "session_id": "ses_391dfe776ffejfpVRHHJAo3MLh",
12: "session_label": "nuum-dev",
13: "question": "What was the specific FTS5 search bug that was fixed in ltm.ts?",
14: "answer": "The FTS5 search in ltm.ts was missing prefix matching. ...
Good. Now let me understand what's in the nuum LTM for these sessions to diagnose the distillation vs retrieval gap:
<path>/home/byk/Code/opencode-nuum/eval/coding_eval.ts</path>
<type>file</type>
<content>1: import { parseArgs } from "util";
2: import { Database } from "bun:sqlite";
3:
4: const BASE_URL = "http://localhost:4096";
5: const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" };
6: const POLL_INTERVAL = 2000;
7: const MAX_WAIT = 120000;
8:
9: const { values } = parseArgs({
10: args: Bun.argv.slice(2),
11: options: {
12: data: { type: "string", default: "eval/data/coding_memory_eval.json" },
13: out: { type: "string", default: "eval/results/coding_eval.jsonl" },
14: mode: { type: "string", default: "all" }, // "oracle", "default", "nuum", or "all"
15: concurrency: { type: "string", default: "3" },
16: },
17: });
18:
19: const concurrency = parseInt(values.concurrency!, 10);
20: const targetMode = values.mode!;
21:
22: type Question = {
23: session_id: string;
24: session_label: string;
25: question: string;
26: answer: string;
27: ...
nuum.db nuum.db-shm nuum.db-wal
/usr/bin/bash: line 1: sqlite3: command not found
=== Session ses_391dfe776ffejfpVRHHJAo3MLh ===
Distillation count: 5
[2026-02-19T12:23:01.901Z]
[2026-02-19T21:01:03.414Z]
Date: Feb 19, 2026
## Nuum v2 Eval — Session Cleanup & Infrastructure
* 🔴 (start+2) User restarted OpenCode for source changes
* 🔴 (start+2) User wanted eval test sessions removed from session history — specifically eval sessions, NOT nuum agent sessions ("nuum's agents don't cause any trouble")
* 🟡 (start+5) Session inventory before cleanup: 3,980 total — 3,949 root, 31 child (nuum workers). 3,943 identified as eval sessions (root, no children). 37 real sessions preserved.
* 🟡 (start+6) Su...
[2026-02-19T21:04:26.052Z]
Date: Feb 19, 2026
* 🔴 (start+18) User directed: fix the timestamp issue first (messagesToText in distillation.ts)
* 🟡 (start+18) Fix applied to messagesToText in distillation.ts (line 61-63): function now prepends (HH:MM) timestamps derived from m.created_at to each message line, changing format from `[role] content` to `[role] (HH:MM) ...
Immediately clear signal. The sentry-cli session (ses_38a242967ff...) has 6 distillations but 4 of them are empty (blank observations). The nuum-dev session has a first distillation that's also empty. This is a distillation quality problem — the observer is running but producing no content for some runs.
Let me dig deeper:
Total: 6 [2026-02-19T12:39:47.461Z] id=4fbfb622-c787-40c3-bf1d-f3cef5b89be3 length=0 [2026-02-19T13:30:41.057Z] id=cc184ef8-8a57-427b-8ea2-868f8331a512 length=0 [2026-02-19T13:33:46.091Z] id=ffa58120-ace9-4c02-9a25-8d94edf4c903 length=0 [2026-02-19T13:36:53.199Z] id=b304d22e-0a36-4a69-9d44-c96cb9a3ed11 length=0 [2026-02-19T14:04:21.532Z] id=94328e2c-b26f-47c0-b589-462f65905974 length=0 [2026-02-19T14:04:31.797Z] id=f38a8821-e2eb-418f-a762-ca5224c11022 length=0
Temporal message count: 199 First message (role=user): We have just merged a high-risk patch: https://github.com/getsentry/cli/pull/262 I want you to manually test all affected commands (assuming all `list` sub commands) Last message (role=assistant):
! Pull request getsentry/cli#265 was already merged failed to run git: fatal: 'main' is already used by worktree at '/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/neon-squid' === auth-api session === Temporal message count: 224
project_id: {
project_id: "60d71a64-de12-4267-8abf-a5f4eef1e9c4",
}
Distillations by session (same project):
session=ses_38a242967ffeiY2pRS7Qbh1x11 count=6 total_obs_length=0
session=ses_38f85e354ffepiQ7LAGIn1v9c4 count=2 total_obs_length=0
session=ses_389c23eddffeDeyoiOTNxMrPTr count=1 total_obs_length=3977
Now I see the full picture. For the sentry-cli project:
ses_38a242967ff) has 6 distillations all with 0 lengthses_38f85e354ff) also has 2 empty distillationsses_389c23eddf) has real content (3977 bytes)The sentry-cli distillations are completely empty. This is definitively a distillation quality problem — the observer ran 6 times but produced nothing. Let me check if there's a pattern in when empty distillations are written vs when content distillations are written:
=== nuum-dev === Total messages: 529 Total distillations: 5 dist at 2026-02-19T12:23:01.901Z: 175 messages available, obs_length=0 dist at 2026-02-19T21:01:03.414Z: 481 messages available, obs_length=5111 dist at 2026-02-19T21:04:26.052Z: 489 messages available, obs_length=1044 dist at 2026-02-19T21:07:43.585Z: 500 messages available, obs_length=1929 dist at 2026-02-19T21:11:35.624Z: 508 messages available, obs_length=2043 === sentry-cli === Total messages: 199 Total distillations: 6 dist at 2026-02-19T12:39:47.461Z: 41 messages available, obs_length=0 dist at 2026-02-19T13:30:41.057Z: 134 messages available, obs_length=0 dist at 2026-02-19T13:33:46.091Z: 137 messages available, obs_length=0 dist at 2026-02-19T13:36:53.199Z: 141 messages available, obs_length=0 dist at 2026-02-19T14:04:21.532Z: 197 messages available, obs_length=0 dist at 2026-02-19T14:04:31.797Z: 197 messages available, obs_length=0
Very clear pattern. The sentry-cli session has messages available at distillation time (41–197 messages) but all 6 distillations are empty. The nuum-dev session had one early empty distillation (at 12:23) but subsequent ones at 21:00+ all have content.
The timestamps tell the story: all sentry-cli distillations happened at 12:xx - 14:xx on Feb 19. The nuum-dev empty distillation was also at 12:23. The nuum-dev content distillations are all at 21:xx. This strongly suggests the observer was broken earlier in the day and then got fixed. Let me check the git log to confirm:
42b2619 Expose gradient budget fields in TransformResult and write stats to part metadata e2150b3 Phase 2: Observation-log format + temporal anchoring + eval harness a2a2b21 Default crossProject to true in ltm.create and curator 5fb7ecb Fix prefill error: replace fully-stripped messages with placeholder instead of dropping e6ebbe9 Fix prefill error: preserve messages that become empty after system-reminder stripping c054f64 Strip all system-reminder blocks from raw window, not just user-message wrappers 5ee1cca Enable cross-project LTM by default a3dafac Strip ephemeral system-reminder wrappers from raw window messages
42b2619 2026-02-19 21:37:39 +0000 Expose gradient budget fields in TransformResult and write stats to part metadata e2150b3 2026-02-19 21:26:57 +0000 Phase 2: Observation-log format + temporal anchoring + eval harness a2a2b21 2026-02-19 12:22:22 +0000 Default crossProject to true in ltm.create and curator 5fb7ecb 2026-02-19 11:32:16 +0000 Fix prefill error: replace fully-stripped messages with placeholder instead of dropping e6ebbe9 2026-02-19 00:56:33 +0000 Fix prefill error: preserve messages that become empty after system-reminder stripping c054f64 2026-02-19 00:51:57 +0000 Strip all system-reminder blocks from raw window, not just user-message wrappers 5ee1cca 2026-02-19 00:40:10 +0000 Enable cross-project LTM by default a3dafac 2026-02-19 00:32:36 +0000 Strip ephemeral system-reminder wrappers from raw window messages
commit e2150b3102fd35226bdbd73f37f9aa50ab2f9c5a
Author: Burak Yigit Kaya <byk@sentry.io>
Date: Thu Feb 19 21:26:57 2026 +0000
Phase 2: Observation-log format + temporal anchoring + eval harness
Replace narrative+facts JSON with OM-style dated observation log:
- New DISTILLATION_SYSTEM prompt: priority tags (🔴/🟡/🟢), timestamps,
temporal anchoring with (meaning DATE), [entity-type] markers
- RECURSIVE_SYSTEM updated for observation consolidation
- DB schema v2: adds observations TEXT column, keeps legacy columns
- messagesToText: prepends (HH:MM) timestamps for observer
- distillationUser: passes date + priorObservations context
- addRelativeTimeToObservations(): annotates Date: headers with
relative time (e.g. '3 weeks ago') and gap markers at read time
- expandInlineEstimatedDates(): adds relative time to (meaning DATE) lines
- formatDistillations: injects observations directly instead of markdown AST
- reflect.ts, ...
Now I have the complete picture. The commit e2150b3 at 21:26 UTC introduced the new observation-log format. All the sentry-cli distillations happened between 12:39 and 14:04 UTC — before the new observer format was deployed. They were written by the old observer code which apparently produced empty strings in that state.
Let me verify by checking what the old distillation schema stored (before e2150b3 added observations column):
Schema: CREATE TABLE distillations (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id),
session_id TEXT NOT NULL,
narrative TEXT NOT NULL,
facts TEXT NOT NULL,
source_ids TEXT NOT NULL,
generation INTEGER DEFAULT 0,
token_count INTEGER DEFAULT 0,
created_at INTEGER NOT NULL
, observations TEXT NOT NULL DEFAULT '')
All columns of first sentry-cli distillation:
[ "id", "project_id", "session_id", "narrative", "facts", "source_ids", "generation", "token_count",
"created_at", "observations"
]
id: 4fbfb622-c787-40c3-bf1d-f3cef5b89be3
project_id: 60d71a64-de12-4267-8abf-a5f4eef1e9c4
session_id: ses_38a242967ffeiY2pRS7Qbh1x11
narrative: User requested manual testing of all list subcommands after merging PR #262 (pagination and consistent target parsing). ...
The sentry-cli distillations have rich content in the old narrative and facts columns — they just have empty observations because they were written before observations was the active column. The data is there, just in the wrong column.
Now I can fully diagnose the gap:
The oracle-vs-nuum gap has two distinct causes:
Schema migration gap (sentry-cli): All 6 distillations for the sentry-cli session were written before e2150b3 switched from narrative/facts to observations. The formatDistillations() function now reads observations only, so all sentry-cli history is invisible to the model. This is 100% a retrieval problem — the information was captured but the reader is looking at the wrong column.
Observer was broken (nuum-dev early distillation): The first nuum-dev distillation at 12:23 is also empty in both old and new columns — that was likely a bug that got fixed later in the day.
Let me verify by checking what formatDistillations actually reads:
4:import { formatDistillations } from "./prompt";
71: observations: string;
84: ? "SELECT id, observations, generation, token_count, created_at, session_id FROM distillations WHERE project_id = ? AND session_id = ? ORDER BY created_at ASC"
85: : "SELECT id, observations, generation, token_count, created_at, session_id FROM distillations WHERE project_id = ? ORDER BY created_at ASC";
300: observations: addRelativeTimeToObservations(d.observations, now),
302: const formatted = formatDistillations(annotated);
Confirmed — gradient.ts:84-85 only selects observations, completely ignoring narrative and facts. All sentry-cli distillation content is invisible.
Now let me check what the narrative/facts columns actually contain for the sentry-cli session to understand what was lost:
Non-empty narrative/facts distillations: 6
[2026-02-19T12:39:47.461Z]
NARRATIVE: User requested manual testing of all list subcommands after merging PR #262 (pagination and consistent target parsing). All 7 list commands (project, issue, team, repo, trace, log, org) were tested across all applicable modes (auto-detect, explicit org/project, org-all with trailing slash, bare-slug project search, --json, --cursor pagination, and command-specific flags). All commands worked corre
FACTS (14):
- PR #262 (getsentry/cli) adds pagination and consistent target parsing to all list commands: project, issue, team, repo l
- BUG FOUND (CLI-72): pagination_cursors table created with single-column PK (command_key TEXT PRIMARY KEY) instead of com
- FIX for CLI-72 local workaround: DROP TABLE pagination_cursors then let migration recreate it. Production fix needs a mi
- issue list org-all JSON output shape changed from plain array to { data: [...], hasMore: bool, nextCursor?: string } — m
- ...
The sentry-cli session has rich, detailed distillations in the old narrative/facts columns — PR numbers, bug details, file paths, test counts, everything. This data exists but is completely invisible to the model because the code only reads observations.
This is the answer: the oracle-vs-nuum gap is primarily a retrieval problem, not a distillation quality problem. The data was captured correctly, but the migration to the new observation-log format left old distillations stranded in deprecated columns.
The fix is clear: when observations is empty but narrative/facts are not, fall back to reading from those columns (or better, run a migration to convert them to observations format).
Let me also quickly check the LTM (long-term knowledge) to see if that's accessible:
1 | import { Database } from "bun:sqlite";
2 | const db = new Database(`${process.env.HOME}/.local/share/opencode-nuum/nuum.db`, { readonly: true });
3 |
4 | // Check LTM entries for the sentry-cli project
5 | const projId = '60d71a64-de12-4267-8abf-a5f4eef1e9c4';
6 | const ltm = db.query("SELECT id, key, content, cross_project, created_at FROM ltm WHERE (project_id = ? OR cross_project = 1) ORDER BY created_at DESC LIMIT 10").all(projId) as any[];
^
SQLiteError: no such table: ltm
errno: 1,
byteOffset: -1,
at prepare (bun:sqlite:331:37)
at query (bun:sqlite:353:28)
at /home/byk/Code/[stdin]:6:16
at loadAndEvaluateModule (2:1)
Bun v1.3.9 (Linux x64)
Tables: [ "distillations", "knowledge", "knowledge_fts", "knowledge_fts_config", "knowledge_fts_data", "knowledge_fts_docsize", "knowledge_fts_idx", "projects", "schema_version", "temporal_fts", "temporal_fts_config", "temporal_fts_data", "temporal_fts_docsize", "temporal_fts_idx", "temporal_messages" ]
1 | import { Database } from "bun:sqlite";
2 | const db = new Database(`${process.env.HOME}/.local/share/opencode-nuum/nuum.db`, { readonly: true });
3 |
4 | // Check knowledge table (LTM) for sentry-cli project
5 | const projId = '60d71a64-de12-4267-8abf-a5f4eef1e9c4';
6 | const ltm = db.query("SELECT id, key, content, cross_project, created_at FROM knowledge WHERE (project_id = ? OR cross_project = 1) ORDER BY created_at DESC LIMIT 10").all(projId) as any[];
^
SQLiteError: no such column: key
errno: 1,
byteOffset: 11,
at prepare (bun:sqlite:331:37)
at query (bun:sqlite:353:28)
at /home/byk/Code/[stdin]:6:16
at loadAndEvaluateModule (2:1)
Bun v1.3.9 (Linux x64)
Knowledge schema: CREATE TABLE knowledge (
id TEXT PRIMARY KEY,
project_id TEXT,
category TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
source_session TEXT,
cross_project INTEGER DEFAULT 0,
confidence REAL DEFAULT 1.0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
metadata TEXT
)
Count: 55
Project-specific knowledge entries: 8 [project] source=ses_389c23eddffeDeyo Title: Non-essential DB cache writes should be guarded with try-catch Content: In the Sentry CLI, commands that write to the local SQLite cache as a side effect (e.g., setUserInfo() to update cached user identity) should wrap those writes in try-catch when the write is not essen [project] source=ses_389c23eddffeDeyo Title: Multiregion mock must include all control silo API routes Content: When changing which Sentry API endpoint a function uses (e.g., switching getCurrentUser() from /users/me/ to /auth/), the mock route must be updated in BOTH test/mocks/routes.ts (single-region) AND te [project] source=ses_389c23eddffeDeyo Title: Login --token flow: getCurrentUser failure must not block authentication Content: In src/commands/auth/login.ts --token flow, the token is saved via setAuthToken() before fetching user info via getCurrentUser(). ...
Now I have the complete picture. Here's the full diagnostic:
The oracle-vs-nuum gap: diagnosis
The gap has two causes, both confirmed:
1. Retrieval problem — old-format distillations are invisible (sentry-cli session)
The sentry-cli session's 6 distillations were written before the e2150b3 commit (21:26 UTC) that migrated from narrative/facts columns to observations. All 6 have empty observations but rich content in narrative/facts. The code now only reads observations, so 100% of the sentry-cli distillation history is invisible despite being in the DB.
2. Distillation quality problem — observer was broken earlier (nuum-dev first distillation)
The first nuum-dev distillation at 12:23 UTC has empty content in ALL columns. This was a separate observer bug before the format migration that produced genuinely empty distillations.
What nuum can access for sentry-cli:
knowledge entries (LTM) — all from the auth-api session, none from the sentry-cli test session itselfnarrative/facts but completely skippedThe fix is a one-time migration to convert existing narrative/facts content into observations format (or a fallback in the reader). Want me to implement that?
[
{
"content": "Analyze oracle-vs-nuum gap: distillation quality vs retrieval quality",
"status": "completed",
"priority": "high"
}
]