Skip to content

feat(sidebar): option B layout on the shared CortexKit frame (#839) - #869

Open
iceteaSA wants to merge 8 commits into
cortexkit:masterfrom
iceteaSA:feat/sidebar-option-b
Open

iceteaSA wants to merge 8 commits into
cortexkit:masterfrom
iceteaSA:feat/sidebar-option-b

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Approved issue: #839

Summary

Screenshots

Rendered from the compiled sidebar slot with OpenTUI's testRender, with the OpenCode dark theme colours and fixture values (the values of the #839 mocks).

Expanded Incident (Health in the warning tone)
Collapsed Collapsed, incident

Changes

Snapshot data

All new SidebarSnapshot fields are optional, so an older RPC server still works with the new view.

Field Source Cost per refresh
historianRun Kept in memory for each session (at most 256 sessions, cleared on session deletion). It records the outcome, the model that produced the published output, the fallback flag, the primary attempts and rejections, and the totals since the last clean run. none
waitingMessages The last value that the compartment trigger calculated. none
proactiveFloor getProactiveCompartmentTriggerPercentage(executeThreshold) none
queuedDropTokens One query. The plan uses idx_pending_ops_session_tag_id and the tags primary key. 1 query
cachedM0MaterializedAt, summariesSinceCache, memoriesSinceCache session_meta, then two counts. The plans use sqlite_autoindex_compartments_1 and idx_memories_project_status_expires. The memory count uses the same workspace visibility rules as m[1]. 2 counts
configReloadFailure The live config reader. none

A historian attempt counts against the model that produced it. A primary attempt counts as rejected when its output fails validation, or when the host answers with a different model. An invalid fallback output does not count against the primary. The run's primary is the model it started with, after master's start selection (selectHiddenModelStart).

OpenCode 1 and OpenCode 2 use the same snapshot builder. In Rust transform mode, historianRun and waitingMessages are missing, because the TypeScript historian does not run.

Refresh

  • A 200 ms debounce on host events, and at most one read each second while a step streams.
  • One read in flight at a time. After a failed read, the panel keeps the last snapshot and waits 30 s.
  • A 2 s poll only while the historian, the dreamer or a recomp runs. This replaces the recomp poll loop.
  • loadSidebarSnapshot has a new opt-in throwOnFailure, so that scheduled reads can see a failure. Other callers do not change.
  • The OpenCode 2 text fallback sidebar read data during render. It now uses the same scheduler.

View

  • The header toggles (▼/▶). Collapsed, the panel shows one row for each section in words: Context (45% left), Historian (idle · 1h55m), Cache (151 drops queued), and Health (2 issues, in the warning or error tone) only when something is wrong. The collapse preference that MC already stores is now used.
  • The Health wording follows the shared CortexKit sidebar standard: stat rows in the warning or error tone, no glyphs, and at most one muted hint row, /ctx-status shows details.
  • The context gauge is the shared SidebarGaugeRow: the warning tone at the session's execute threshold and the error tone at 85%.
  • An absent value shows —. An unknown usage shows no reading, not 100%.
  • Removed: the token legend, Hygiene, the dreamer backlog row for each task, the second dreamer progress line, and Stats · Total tokens.
  • The existing section preferences still apply: historian to History, status to Prompt cache and the notes rows, memory to Memories, dreamer to Dreamer. The preferences file format does not change.
  • With compaction off, Context shows the native window pressure, and History, Prompt cache and the historian Health rows are not shown (collapsed too).

Build (decision 3)

  • scripts/build-tui.ts bundles @cortexkit/opencode-sidebar (it ships TSX source) with the Solid transform bound to the host runtime ids, plus a raw variant. This is the same method that anthropic-auth uses. The script change is +69/−5 lines, and the two bundles are 28,792 B (raw) and 29,058 B (host).
  • bun.lock changes for the dependency swap. bun install on Bun 1.4.4 also wrote the current workspace versions (0.49.35 to 0.49.52) into it.
  • @cortexkit/opencode-sidebar 0.2.0 declares OpenTUI >=0.4.5, so the plugin now declares @opentui/core and @opentui/solid peers >=0.4.5 and development dependencies ^0.4.5 (lock: OpenTUI 0.4.2 to 0.4.5 and bun-ffi-structs 0.2.3 to 0.2.4). A test fails when the plugin's floor is below the shared sidebar's floor.
  • One risk: the committed bundle depends on the Bun version. Bun 1.4.0 renames one shadowed local, and Bun 1.4.4 does not. CI uses bun-version: latest, which is 1.4.4 now, so check:tui-compiled passes. A later Bun release can change the output for each contributor. A pinned Bun version in CI would prevent this.

Not in this PR

  • Rust historian. It records no data for each run. Only the TypeScript historian records it.

Tests

  • New tests: the historian attribution cases (host switch, repair, explicit fallback, invalid fallback output, unknown producer), the snapshot fields with their query plans, the refresh scheduler, the OpenCode 2 fallback without render reads, and the render cases of the compiled slot (healthy, incident, older snapshot, compaction off, preferences, unknown usage, recomp row, errors, gauge tones, theme change, raw frame bundle, badge contrast, collapsed). The render snapshots compare every cell of every row, trailing spaces included.
  • The collapsed render test failed before the collapsed view (13 pass, 1 fail) and passes after it (14 of 14). Against the previous expanded and incident renders, the only differences are the header toggle and one blank separator cell's foreground colour inside the shared gauge row, which draws nothing.
  • Review fixes (86c8aec, one commit for the 12 review findings): each code fix has a test that failed before it and passes after it (snapshot counts and the tag-number join, the stale waiting count, the native-window gauge, the drop-only queued count, the cumulative fallback row, stopped and failed rebuilds in Health, stale .js cleanup, the fixture clock and the preference writer in the render script).
  • Bun 1.4.4 on head 86c8aec: typecheck passes, Biome passes on the changed files, build:tui twice writes no file, check:tui-compiled passes, and the focused TUI, sidebar RPC, compartment-trigger and memory-storage tests pass (188 of 188).
  • Full plugin suite, Bun 1.4.4, on head 86c8aec over master dba0c4f4e (bun test --parallel=2, otherwise the package's test command): 10,756 pass, 11 fail, 106 skip across 1,084 files. None of the 11 comes from this branch:
    • 5: npm 12 returns an object from npm pack --json, and scripts/tui-pack-graph.test.ts and the 4 packed review-bundle tests read [0].files. They fail the same way without this branch.
    • 6: apply-operations.test.ts (1) and scripts/calibrate-tokenizer/credentials-cli.test.ts (5, new on master) spawn process.execPath. My Bun 1.4.4 runner is OpenCode's embedded Bun, so process.execPath is not a plain bun. Both files pass on Bun 1.4.0 (10 of 10) and this branch does not change them.

Harness coverage (required)

For each entry select: covered / not applicable — surface does not exist there / NOT covered.

  • OpenCode 1: covered. Shared snapshot builder and TUI slot.
  • OpenCode 2: covered. Same snapshot builder; the text fallback uses the same scheduler.
  • Pi: not applicable. Pi has no sidebar.
  • OMP: not applicable. OMP has no sidebar.
  • Rust module: not applicable. The sidebar is TypeScript; in Rust transform mode the historian fields are missing.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge, with the earlier non-blocking memory-count cost still present.

Findings

  1. P2 Counting loads full memories ▶

Summary

Moves the sidebar onto the shared CortexKit frame and adds optional readings for history, cache, and health.

  • The sidebar shows compact sections for context, history, cache, knowledge, and Dreamer.
  • Sidebar snapshots include readings for historian runs, waiting messages, cache changes, and health.
  • Sidebar readings refresh on a shared schedule in both OpenCode paths.
  • The build bundles the shared sidebar for host and raw rendering.

Reviews (3) · Last reviewed commit: "fix(sidebar): leave the drop token estim..." · Reviewed by Greptile

@iceteaSA
iceteaSA force-pushed the feat/sidebar-option-b branch from 331693b to 14ff34d Compare November 6, 2026 16:40
@socket-security

socket-security Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​cortexkit/​opencode-sidebar@​0.2.0771009991100
Updatednpm/​@​opentui/​core@​0.4.2 ⏵ 0.4.598 -110092 +198 +1100
Updatednpm/​@​opentui/​solid@​0.4.2 ⏵ 0.4.5971009998 +1100

View full report

Keep historian observations in bounded session memory and use indexed cache-delta counts. Share the refresh scheduler across OpenCode sidebar paths without changing their layout.
Move the sidebar frame from @cortexkit/common-auth/quota-sidebar to
@cortexkit/opencode-sidebar 0.2.0. Use its SidebarGaugeRow for the
context gauge in place of the local gauge, and its collapsible prop to
collapse the panel to one row for each section (Context, Historian,
Cache, and Health only when something is wrong).

The render snapshots store each row as an exact string, so trailing
cells are compared and no line ends in whitespace.
@iceteaSA
iceteaSA force-pushed the feat/sidebar-option-b branch from 14ff34d to 0f27af9 Compare November 12, 2026 07:44
@iceteaSA
iceteaSA marked this pull request as ready for review November 12, 2026 07:44

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 41 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/plugin/src/tui/data/context-db.test.ts">

<violation number="1" location="packages/plugin/src/tui/data/context-db.test.ts:94">
P2: This test does not exercise scheduled reads; every call supplies `{ throwOnFailure: true }` directly. It would pass if the sidebar callers stopped forwarding the option, so test the scheduler’s `read` callback or rename this as a `loadSidebarSnapshot` option test.</violation>
</file>

<file name="packages/plugin/src/hooks/magic-context/compartment-trigger.ts">

<violation number="1" location="packages/plugin/src/hooks/magic-context/compartment-trigger.ts:402">
P2: `waitingMessages` is updated only after the authoritative tail inspection, but the cheap pre-gate can return before reaching this code. After compaction, short or below-floor tails can leave the sidebar showing the old positive count; update this observation on the cheap-skip and other early-return paths as well.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

Comment thread packages/plugin/package.json Resolved
}

describe("TUI context RPC data", () => {
test("scheduled reads expose RPC failures instead of hiding them behind a cached snapshot", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This test does not exercise scheduled reads; every call supplies { throwOnFailure: true } directly. It would pass if the sidebar callers stopped forwarding the option, so test the scheduler’s read callback or rename this as a loadSidebarSnapshot option test.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/plugin/src/tui/data/context-db.test.ts, line 94:

<comment>This test does not exercise scheduled reads; every call supplies `{ throwOnFailure: true }` directly. It would pass if the sidebar callers stopped forwarding the option, so test the scheduler’s `read` callback or rename this as a `loadSidebarSnapshot` option test.</comment>

<file context>
@@ -91,6 +91,25 @@ async function startServer(
 }
 
 describe("TUI context RPC data", () => {
+    test("scheduled reads expose RPC failures instead of hiding them behind a cached snapshot", async () => {
+        const dataHome = makeDataHome();
+        const directory = "/repo-scheduled";
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86c8aec. The test is renamed to describe the loadSidebarSnapshot option it exercises; it no longer claims to cover scheduler forwarding.


const rawEligibility = getRawHistoryEligibility(db, sessionId);
if (!rawEligibility.hasRawBeyondLastCompartment) {
rememberWaitingMessages(sessionId, 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: waitingMessages is updated only after the authoritative tail inspection, but the cheap pre-gate can return before reaching this code. After compaction, short or below-floor tails can leave the sidebar showing the old positive count; update this observation on the cheap-skip and other early-return paths as well.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/plugin/src/hooks/magic-context/compartment-trigger.ts, line 402:

<comment>`waitingMessages` is updated only after the authoritative tail inspection, but the cheap pre-gate can return before reaching this code. After compaction, short or below-floor tails can leave the sidebar showing the old positive count; update this observation on the cheap-skip and other early-return paths as well.</comment>

<file context>
@@ -398,6 +399,7 @@ function getUnsummarizedTailInfo(
 
             const rawEligibility = getRawHistoryEligibility(db, sessionId);
             if (!rawEligibility.hasRawBeyondLastCompartment) {
+                rememberWaitingMessages(sessionId, 0);
                 return { ...TAIL_INFO_DEFAULTS, nextStartOrdinal: rawEligibility.offset };
             }
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86c8aec. The cheap gate returns only when usage is below the proactive floor and the tail's token upper bound is below the trigger budget, so it cannot know the exact eligible-message count. That path (and the spent-drain and inspection-failure paths) now invalidates the observation, and the sidebar shows no reading instead of a stale count or an invented zero. Counts from an inspected tail are unchanged. Regression: compartment-trigger.test.ts "invalidates a stale waiting count when a below-floor small tail cheap-skips".

Comment thread packages/plugin/scripts/build-tui.ts Resolved
Comment on lines +954 to +961
return readNewMemoriesForM1Union(
db,
projectPaths,
afterId,
expiryCutoff,
ownIdentities,
shareCategories,
).length;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Counting loads full memories

countNewMemoriesForM1Union now loads every matching memory, including its full text, sorts the rows, and creates memory objects just to count them. Each sidebar refresh repeats this work. When a session has no cache watermark, it reads all visible project and workspace memories.

This adds avoidable database reads and memory use as projects grow. Keep the count in SQL while preserving the reader's validity and visibility checks.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 20 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

In Rust transform mode the queued-drop count comes from the module's own
drop queue, but the token estimate came from the TypeScript pending_ops
table, which describes a different queue. The snapshot now omits the
estimate when the module supplies the count, and the sidebar shows the
count with "no reading" in place of the estimate.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant