Skip to content

feat(unassigned-cli): label Main vs background-agent sessions, hide agents by default - #45

Merged
miguelrisero merged 8 commits into
mainfrom
chief/bc-cli-session-labeling
Jul 23, 2026
Merged

feat(unassigned-cli): label Main vs background-agent sessions, hide agents by default#45
miguelrisero merged 8 commits into
mainfrom
chief/bc-cli-session-labeling

Conversation

@miguelrisero

Copy link
Copy Markdown
Owner

What & why

In the Unassigned CLI conversations panel every session looked identical, so you couldn't tell a real interactive session apart from a programmatically-spawned background agent. This labels each one Main vs Agent and, per the owner decision ("hide agents by default"), shows Main sessions by default while tucking background agents behind a collapsed "Show N background agents" toggle.

Owner ask (verbatim): "add that … so it's easy to recognize what's a main session and what a subagent?" → binding decision: hide agents by default — label each row, main visible, agents behind a toggle, collapsed by default.

The signal

Claude native-store transcripts carry an entrypoint field on their records:

entrypoint classification
cli Main (interactive human session)
sdk-cli / sdk-py Subagent (programmatically spawned)
anything else / missing / unreadable Main (see fail-open below)

entrypoint is captured in adapt_native_claude_line before the sidechain/bookkeeping early-returns, so it is read even off attachment records — which is exactly where cli/sdk-cli land in real transcripts.

Fail open to visibility (the one invariant)

The classifier is written as "default Main unless positively identified as a subagent (sdk-cli/sdk-py)" — never the reverse. An unknown/missing/future entrypoint, or a transcript we fail to read, classifies as Main (visible). Hiding a real conversation behind the agents drawer is the one unacceptable failure; a spurious Main label is harmless. The Rust CliSessionKind::from_entrypoint default arm is commented as load-bearing and guarded by a unit test (unknown/None → Main), and the frontend partitionCliSessionsByKind mirrors the same rule so a payload missing kind degrades to Main.

Changes

Backend

  • native.rs: parse entrypoint into NativeClaudeEnvelopeMetadata (all record dispositions; moved, not cloned).
  • claude_transcript_ingest.rs: new CliSessionKind { Main, Subagent } (serializes "main"/"subagent") + fail-open from_entrypoint; new kind field on UnassignedCliSession, derived in the unassigned scan. The scan reads snippet + entrypoint in a single 50-line pass (read_session_preview replaces first_prompt_snippet; snippet behavior byte-identical).
  • generate_types.rs + regenerated shared/types.ts (CliSessionKind, kind).

Frontend

  • partitionCliSessionsByKind.ts (pure, unit-tested) splits main vs agents, fail-open to main.
  • UnassignedCliSessions.tsx: Main/Agent badge on each row; main rows shown by default; agents collapsed behind a ▸ Show N background agents toggle (no toggle when zero agents; still reachable when there are only agents). The toggle is not persisted — agents collapse on every open, matching the owner's "not there by default" instinct.

i18n — 6 new keys (mainBadge, agentBadge, showBackgroundAgents(+_other), hideBackgroundAgents(+_other)) across all 7 locales (en, es, fr, ja, ko, zh-Hans, zh-Hant), mirroring the repo's existing answeredCount plural convention.

Scope is identity/visibility only: no change to the assign flow, cwd matching, or how sessions are discovered.

Verification

Automated (all green on the rebased branch):

  • CI-exact clippy --workspace --all-targets --exclude vibe-kanban-tauri -- -D warnings
  • cargo test --workspace (64 ok / 0 failed) — incl. new tests: from_entrypoint fail-open, read_session_preview snippet+kind, and the real-world case (entrypoint on an attachment record before the prompt → Subagent)
  • web-core tsc, local-web/ui ESLint, check-unused-i18n-keys
  • vitest partitionCliSessionsByKind 5/5 (run via npx from repo root — note: web-core *.test.ts are excluded from tsc and do not run in CI)
  • scripts/check-i18n.sh: 0 literal-string violations (PR & base), no duplicate keys, locale key parity
  • generate_types clean (no diff)

Live evidence — real owner data through the real endpoint on a scratch instance (isolated BC_DATA_DIR, port 7777, never production 4111), the three sessions from the owner's own case:

GET /api/workspaces/…/native-cli-sessions/unassigned
7c024707 → kind "main"      "make the smallest screen win when 2 people have tmux open…"
ff360725 → kind "subagent"  "Review this change for security vulnerabilities…"   (sdk-py)
295c8e60 → kind "subagent"  "<local-command-caveat>…"                              (sdk-cli)

So the panel shows 7c024707 (the interactive session) by default and collapses the two agents behind the toggle — exactly the intended behavior.

Review

Three independent passes, consensus that the change is correct: a read-only Codex sweep (gpt-5.6) — no material findings; a deep-review correctness pass — no bugs; a deep-review cleanup pass — one micro-efficiency (move entrypoint into metadata instead of cloning; applied). Considered and declined: reusing CollapsibleSectionHeader (it persists expand-state to localStorage, which would contradict the owner's "collapsed on every open"); the showAgents cosmetic edge (agents re-expand if the list repopulates while the dialog stays open — no data/correctness impact).

Notes / deviations

  • Draft by design — low-medium risk, but it touches the seamless feature's data model, so a human verifies the rendered panel and flips to ready.
  • The frontend component is not captured in an automated screenshot here; both halves are proven independently (real-data endpoint + vitest partition logic) and the composition is covered by code review.
  • CodeRabbit CLI is unusable non-interactively in this environment; multi-model review was done via the Codex sweep + deep-review instead.
  • pnpm run check/lint are fully green except the crates/remote workspace's private billing git dependency (BloopAI/vibe-kanban-private), which is unauthenticated in this environment — unrelated to this change (which touches executors/services/server only). CI with credentials covers it.

@miguelrisero
miguelrisero marked this pull request as ready for review July 23, 2026 13:15
@miguelrisero
miguelrisero merged commit 36f6c85 into main Jul 23, 2026
7 checks passed
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