Skip to content

refactor: the profile listing's columns move to where dl's rendering is tested - #580

Merged
blooop merged 5 commits into
mainfrom
refactor/claude-profile-columns
Sep 4, 2026
Merged

blooop merged 5 commits into
mainfrom
refactor/claude-profile-columns

Conversation

@JSmithRobotics

@JSmithRobotics JSmithRobotics commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #572, from the one comment on that stack that was worth acting on.

codecov flagged 65 uncovered lines at 0.00% in dl/src/commands.rs. Both
halves of that are true, and the second is the interesting one: commands.rs has
no test module at all and never has, because it is command plumbing -- dl's
rendering lives in render.rs, which carries nearly two hundred tests. So 0%
there is the house norm rather than a gap.

The gap was putting testable decisions in the untested file. Two of them, both
described at length in #572's own description and neither covered:

  • the account column's two absences. A profile with no credential has nobody
    to name and renders -; one that is logged in with no readable account renders
    unknown, because Claude Code's state file was absent or has moved on from the
    shape this reads. Neither is an error and neither stops a launch, so that word
    is the only thing distinguishing them for a reader.
  • the shared-account footnote. Said once per group even though both members
    carry the note, sorted so the sentence does not reorder itself between runs, and
    preceded by a blank line so it reads as a footnote rather than a row.

render::claude_profile_lines returns the lines and render_claude_profiles
prints them, which is the shape table_lines already has for the workspace
listing. Nine tests cover the header, the column width against both a long name
and one shorter than the header, the three-field join and its gaps, both
absences, and the footnote's count, order and blank line.

What deliberately does not move

warn_if_the_profiles_root_could_not_be_read stays where f98170c put it. The line
that commit drew is the right one: it asks the filesystem a question a pure
function cannot answer and needs a stderr to answer it on, and widening the return
type of summarise for a case only the binary can report would be the worse
trade. What moves here is only the part that is a function of the rows it was
handed.

No behaviour change

The output is byte-identical, checked against a fixture profile root before and
after -- including the footnote, and including the - for a profile with no
credential:

NAME     STATE          ACCOUNT
default  authed         me@example.com · Acme · max
base     authed         me@example.com
bear     authed         me@example.com
fresh    not logged in  -
work     authed         me@acme.example · Acme

'base', 'bear' are the same account, so all but one are spare.

Rebased onto main after the five-PR stack merged, so it carries f98170c's
canonicalised grouping and 485162c's changes rather than the versions those
fixed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AdSFnBdxie6TosHVmjLY28

Summary by Sourcery

Move Claude profile listing presentation into the tested rendering layer without changing its output.

Enhancements:

  • Move Claude profile listing formatting into the rendering layer so its account-column and shared-account footnote behavior is covered alongside other rendering logic.
  • Preserve the existing profile listing output, including account details, absence labels, alignment, and shared-account notes.

Tests:

  • Add rendering tests covering profile columns, Unicode and minimum name widths, missing-account distinctions, shared-account footnotes, and empty listings.

codecov flagged 65 uncovered lines in `dl/src/commands.rs` on #572, at 0.00%.
Both halves of that are true and the second is the interesting one:
`commands.rs` has no test module at all, and never has, because it is command
plumbing -- dl's rendering lives in `render.rs`, which carries nearly two hundred
tests. So 0% there is the house norm rather than a gap.

The gap was putting testable decisions in the untested file. Two of them:

- **the account column's two absences.** A profile with no credential has nobody
  to name and renders `-`; one that is logged in with no readable account renders
  `unknown`, because Claude Code's state file was absent or has moved on from the
  shape this reads. Neither is an error and neither stops a launch, so that word
  is the only thing distinguishing them for a reader.
- **the shared-account footnote.** Said once per group even though both members
  carry the note, sorted so the sentence does not reorder itself between runs,
  and preceded by a blank line so it reads as a footnote rather than a row.

`render::claude_profile_lines` returns the lines and `render_claude_profiles`
prints them, which is the shape `table_lines` already has for the workspace
listing. Nine tests cover the header, the column width against both a long name
and one shorter than the header, the three-field join and its gaps, both
absences, and the footnote's count, order and blank line.

`warn_if_the_profiles_root_could_not_be_read` stays in `commands.rs`, and the
line f98170c drew there is the right one: it asks the filesystem a question a
pure function cannot answer, and needs a stderr to answer it on. What moves here
is only the part that is a function of the rows it was handed.

No behaviour change: the output is byte-identical, checked against a fixture
profile root before and after.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @JSmithRobotics, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 6 days and 22 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors Claude profile listing formatting out of command plumbing into test-covered rendering helpers while preserving output exactly, and adds focused tests for column layout, account edge cases, empty listings, and shared-account footnotes.

File-Level Changes

Change Details Files
Move Claude profile listing rendering decisions into the tested rendering module.
  • Extract profile column formatting, account fallback values, column sizing, and empty-list text into pure rendering functions.
  • Keep command code responsible for loading profiles, filesystem warnings, stderr messaging, and printing returned lines.
  • Preserve byte-identical output, including account formatting, missing-account distinctions, and shared-account notes.
rust/dl/src/commands.rs
rust/dl/src/render.rs
Add focused coverage for Claude profile output behavior.
  • Test headers, row rendering, field joining, and name-column width.
  • Test - versus unknown account fallbacks and empty-list behavior.
  • Test shared-account note grouping, deduplication, stable ordering, spacing, and omission when unused.
rust/dl/src/render.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.17352% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.86%. Comparing base (79457e9) to head (548daf3).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
rust/dl/src/commands.rs 0.00% 3 Missing ⚠️
rust/dl/src/render.rs 99.53% 1 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)
rust 95.13% <98.17%> (+0.14%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 95.13% <98.17%> (+0.14%) ⬆️
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…-claude-profiles

f60da86 inserted `render_claude_profiles` and its doc *inside* the doc block that
already belonged to `render_repos`, which left `render_repos` -- the function that
actually reads the completion cache -- with no doc at all, and left its first four
lines making a claim about the function below them.

The claim is checkable and wrong. "**Divergence row 11 lands here:** a cache
document with no `repos` key reads as a cache with no repos" describes row 11 of
docs/rust-rewrite-plan.md, the completion-cache row. `render_claude_profiles`
touches no cache: it calls `claude_profiles::from_process()` and prints rows. A
reader chasing that citation lands on the wrong function and the divergence
looks unimplemented.

Not a sweep -- e9fbcdc rewrote a paragraph inside this same block, so it is the
diff's own comment.

Claude-Session: https://claude.ai/code/session_01U4SpX4ixqQG5R8hErsPMot
e9fbcdc replaced "No home directory and no override" with "`dl` runs with
`XDG_CACHE_HOME` set and no home directory at all, and then there is no profiles
root to enumerate." The second clause is false on an input anyone can name.

`DEVLAUNCH_CLAUDE_PROFILES_DIR=/somewhere/empty` with no `$HOME` and no
`$CLAUDE_CONFIG_DIR`: `xdg::claude_profiles_root` returns the override at
domain/xdg.rs:94 *before* it ever consults `home_dir()`, so there is a profiles
root, it is enumerated, and it is empty. `unnamed_config_dir_from_process()` is
`None`, so `summarise` builds no default row either and the listing is empty with
a root that very much exists.

The old wording named the override and was right to. The replacement dropped it
and asserted the opposite, which is the failure mode a comment has that code does
not: it goes stale and then lies. Says what actually empties the listing now --
no unnamed login to head it, and a root that is absent or empty.

Not testable as behaviour: the sentence is byte-identical either way, and what is
wrong is the explanation of when it prints.

Claude-Session: https://claude.ai/code/session_01U4SpX4ixqQG5R8hErsPMot
"both lived in a file with no test module at all until codecov pointed at 65
uncovered lines on this change" decays twice over. "This change" has no referent
to anyone reading the function later, and "a file with no test module at all"
stops being true the first time anybody writes one in commands.rs -- at which
point the sentence is a confident false claim about a file it does not name.

The why survives in the sentence above it, which is the part worth keeping: the
column decisions are the interesting part and they are worth a test. The rest is
commit-message material and e9fbcdc's message already carries it.

Claude-Session: https://claude.ai/code/session_01U4SpX4ixqQG5R8hErsPMot
…from bytes

`claude_profile_lines` measures the name column with `chars().count()`, and the
PR's premise is that the column decisions are worth a test. This one had none:
every name in the suite was ASCII, so `the_name_column_is_as_wide_as_the_widest_name`
and `a_short_name_does_not_shrink_the_column_below_the_header` both pass verbatim
against `row.name.len()`.

Proved by swapping in `len()` and running: green. So the test that goes with this
commit had to be written to fail, and the first attempt did not -- asserting the
header and the rows agree passes under a byte width too, because `{:<width$}` pads
by characters, so a byte width pads every line to the same wrong number and
misaligns nothing. It only opens a gap wider than the longest name.

Asserts the width itself instead. `ünïcødé` is seven characters and eleven bytes,
so the state column starts at 9 with a character count and at 13 with a byte one.
Red under `len()`, green under `chars().count()`.

Claude-Session: https://claude.ai/code/session_01U4SpX4ixqQG5R8hErsPMot
@blooop
blooop merged commit 01f71c7 into main Sep 4, 2026
15 checks passed
@blooop
blooop deleted the refactor/claude-profile-columns branch September 4, 2026 12:35
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.

2 participants