Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
through. A verb that forwards no login says it is ignoring it, as `--devcontainer`
does; a global command refuses it.

- **A pane's row in [herdr](https://herdr.dev) says which account the agent in it is
running as**, reported as the display label `profile=<name>`. A profile is chosen per
launch and forwarded per session, so two tabs side by side can be two different
accounts with nothing on screen to tell them apart, and the failure that matters here
is not noticing: work pushed from the wrong identity is found out about later and
somewhere else. Reported under devlaunch's own source name, so it sits beside herdr's
labels rather than overwriting one, and cleared rather than left stale when a launch
forwards the default login.

**A pane opened beside an agent inherits the profile that agent started with.** The
pane shell already opens in the workspace its tab holds; a workspace is not an account,
so it read the default login while the agent one pane over ran as another. It is read
from the agent's own argv rather than from a note kept anywhere, for the reason the
pane shell keeps nothing: the argv is what is true, and a record would be a second copy
of it that can go stale. An exact element match, and it stops at a bare `--`, so a
prompt that contains the words `--claude-profile work` is a prompt.

**Nothing is inherited across a boundary.** A pane in a tab holding no agent has no
account to inherit and gets the default login, and the environment of an agent already
running is fixed at exec, so switching profile reaches the next session and never the
one on screen.

### Fixed

- **`$CLAUDE_CONFIG_DIR` is now honoured on the host, so a host that has moved its
Expand Down
4 changes: 3 additions & 1 deletion rust/devlaunch-core/public-api.rest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2756,7 +2756,9 @@ impl core::marker::StructuralPartialEq for devlaunch_core::flows::repo_manager::
pub mod devlaunch_core::flows::session_manager
pub enum devlaunch_core::flows::session_manager::PaneDestination
pub devlaunch_core::flows::session_manager::PaneDestination::HostShell
pub devlaunch_core::flows::session_manager::PaneDestination::Workspace(alloc::string::String)
pub devlaunch_core::flows::session_manager::PaneDestination::Workspace
pub devlaunch_core::flows::session_manager::PaneDestination::Workspace::claude_profile: core::option::Option<alloc::string::String>
pub devlaunch_core::flows::session_manager::PaneDestination::Workspace::workspace_id: alloc::string::String
impl core::clone::Clone for devlaunch_core::flows::session_manager::PaneDestination
pub fn devlaunch_core::flows::session_manager::PaneDestination::clone(&self) -> devlaunch_core::flows::session_manager::PaneDestination
impl core::cmp::Eq for devlaunch_core::flows::session_manager::PaneDestination
Expand Down
47 changes: 47 additions & 0 deletions rust/devlaunch-core/src/clients/herdr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,53 @@ pub(crate) fn pane_list_argv() -> Vec<String> {
vec!["pane".to_owned(), "list".to_owned()]
}

/// The source id devlaunch reports under, in herdr's `--source` sense.
///
/// One string, two callers: this module's argv builders and [`HOOK`], which spells it
/// as a shell literal because it is a shell script.
/// `the_hook_reports_under_the_source_this_module_names` is the test that diffs them.
pub(crate) const REPORT_SOURCE: &str = "devlaunch:claude";

/// The metadata token naming the Claude profile a pane's session forwards.
///
/// Display-only, and rendered as `$profile` in herdr's agent sidebar rows. A *token*
/// rather than `--display-agent`, which would fight the agent name `report-agent`
/// sets and which herdr uses to pick a detection manifest.
pub(crate) const PROFILE_TOKEN: &str = "profile";

/// The argv that labels a pane with the Claude profile its session runs as, or clears
/// the label when the session names no profile.
///
/// **Clearing matters as much as setting.** A pane is reused: a launch that named
/// `work` and a later launch in the same pane that named nothing would otherwise leave
/// the sidebar claiming an account the running session is not using, which is the exact
/// mislabelling `--claude-profiles` exists to prevent, moved somewhere more visible.
///
/// Verified against herdr 0.8.2: `pane report-metadata [OPTIONS] --source <ID>
/// <PANE_ID>`, with `--token NAME=VALUE` and `--clear-token NAME`. The pane id is given
/// first, as [`HOOK`] gives it to `report-agent`, which is the invocation measured
/// against a live herdr.
pub(crate) fn profile_metadata_argv(pane_id: &str, profile: Option<&str>) -> Vec<String> {
let mut args = vec![
"pane".to_owned(),
"report-metadata".to_owned(),
pane_id.to_owned(),
"--source".to_owned(),
REPORT_SOURCE.to_owned(),
];
match profile {
Some(name) => {
args.push("--token".to_owned());
args.push(format!("{PROFILE_TOKEN}={name}"));
}
None => {
args.push("--clear-token".to_owned());
args.push(PROFILE_TOKEN.to_owned());
}
}
args
}

/// The argv that asks herdr what one pane is running.
pub(crate) fn process_info_argv(pane_id: &str) -> Vec<String> {
vec![
Expand Down
9 changes: 9 additions & 0 deletions rust/devlaunch-core/src/flows/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2205,6 +2205,15 @@
pane_id: reporting.pane_id().to_owned(),
socket: reporting.container_socket(),
});
// Which account this session runs as, so the manager's sidebar says so
// rather than leaving the name of a directory as the only clue. Called
// even when no profile was named, because a pane is reused and a stale
// label is worse than none.
session_manager::report_profile(
session.runner,
&reporting,
session.host.claude.profile.as_deref(),

Check warning on line 2215 in rust/devlaunch-core/src/flows/launch.rs

View check run for this annotation

Codecov / codecov/patch

rust/devlaunch-core/src/flows/launch.rs#L2212-L2215

Added lines #L2212 - L2215 were not covered by tests
);
Some((reporting, forward))
}
session_manager::Prepared::Refused { reason } => {
Expand Down
Loading
Loading