feat(unassigned-cli): label Main vs background-agent sessions, hide agents by default - #45
Merged
Merged
Conversation
…int (fail open to Main)
… strings (7 locales)
miguelrisero
marked this pull request as ready for review
July 23, 2026 13:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
entrypointfield on their records:entrypointclisdk-cli/sdk-pyentrypointis captured inadapt_native_claude_linebefore the sidechain/bookkeeping early-returns, so it is read even offattachmentrecords — which is exactly wherecli/sdk-cliland 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/futureentrypoint, 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 RustCliSessionKind::from_entrypointdefault arm is commented as load-bearing and guarded by a unit test (unknown/None→ Main), and the frontendpartitionCliSessionsByKindmirrors the same rule so a payload missingkinddegrades to Main.Changes
Backend
native.rs: parseentrypointintoNativeClaudeEnvelopeMetadata(all record dispositions; moved, not cloned).claude_transcript_ingest.rs: newCliSessionKind { Main, Subagent }(serializes"main"/"subagent") + fail-openfrom_entrypoint; newkindfield onUnassignedCliSession, derived in the unassigned scan. The scan reads snippet + entrypoint in a single 50-line pass (read_session_previewreplacesfirst_prompt_snippet; snippet behavior byte-identical).generate_types.rs+ regeneratedshared/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 agentstoggle (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 existingansweredCountplural 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):
--workspace --all-targets --exclude vibe-kanban-tauri -- -D warningscargo test --workspace(64 ok / 0 failed) — incl. new tests:from_entrypointfail-open,read_session_previewsnippet+kind, and the real-world case (entrypoint on anattachmentrecord before the prompt → Subagent)web-coretsc,local-web/uiESLint,check-unused-i18n-keyspartitionCliSessionsByKind5/5 (run vianpxfrom repo root — note: web-core*.test.tsare excluded from tsc and do not run in CI)scripts/check-i18n.sh: 0 literal-string violations (PR & base), no duplicate keys, locale key paritygenerate_typesclean (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: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
entrypointinto metadata instead of cloning; applied). Considered and declined: reusingCollapsibleSectionHeader(it persists expand-state to localStorage, which would contradict the owner's "collapsed on every open"); theshowAgentscosmetic edge (agents re-expand if the list repopulates while the dialog stays open — no data/correctness impact).Notes / deviations
pnpm run check/lintare fully green except thecrates/remoteworkspace's privatebillinggit 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.