Skip to content

A tab is named after the spec, and keeps that name - #371

Merged
blooop merged 7 commits into
mainfrom
title-spec
Aug 22, 2026
Merged

A tab is named after the spec, and keeps that name#371
blooop merged 7 commits into
mainfrom
title-spec

Conversation

@blooop

@blooop blooop commented Aug 22, 2026

Copy link
Copy Markdown
Owner

dl blooop/devlaunch named its pane devlaunch-main-zovomobo. The id is a poor
name for two reasons: it carries no owner, so a fork and its upstream are two tabs
spelled the same, and it spells the branch as a slug, so feature/auth reads as
feature-auth — the name of a different branch the same repository could have.
Neither is recoverable from the id afterwards. The pane is now
blooop/devlaunch@feature/auth.

No reconstruction is involved: a launch resolved that triple on the way in, and
Launch now remembers it rather than keeping only the id its Placement collapsed
to. The other three placements have no triple and keep the id — a bare name is its
id, and a path or URL never had a branch for an @ to precede.

Keeping the name

That much lasts about a second in an interactive session. Ubuntu's stock ~/.bashrc
puts \e]0;\u@\h: \w\a at the front of PS1, so every prompt renames the pane
after the hostname, which is the id. So the setup pass appends one line to the
profile a login shell reads:

case $- in *i*) [ -n "$BASH_VERSION" ] && PS1="$PS1\[\e]2;"blooop/devlaunch@main"\a\]" ;; esac

Appended, because two escapes in one prompt are applied in order and the last one
sets the title — a PROMPT_COMMAND would lose, since bash runs that before it
prints PS1. Nothing is rewritten, so the visible user@host:path$ still says the
hostname and only the tab changes. It rides the hostname stage's existing round
trip, is deduped by the same content-hash mark the PATH writers use, and is inert in
the bash -lc one-shots that read the same profile. A test drives it through a real
bash and reads PS1 back, because the ordering is the whole feature.

DEVLAUNCH_NO_TITLE governs both halves.

Two bargains worth knowing

Installed when a workspace enters Running, not per attach. So
DEVLAUNCH_NO_TITLE=1 dl <ws> on a running workspace silences dl's escape but not
the prompt's; recreate re-decides it. Same bargain the hostname stage makes, for
the same round-trip reason (#157). README says so.

Only a spec is installed. A container told to title after its own id is told
nothing — the id is already its hostname. It also cannot be, safely: see the second
finding below.

Self-review found three defects, all fixed here

  • Opening a workspace by its id renamed its tab back to the hash. The line is
    deduped by a hash of its own text, so a second, different name for one workspace
    appends rather than replaces, and the last append is what every prompt obeys. One
    dl devlaunch-main-zovomobo was enough to undo the spec permanently. Keying on
    the spec alone makes the line a pure function of the triple, so a workspace has at
    most one, ever.
  • A dash login shell printed the escape instead of setting it. $PROFILE can be
    ~/.profile, which any POSIX login shell reads, and /bin/sh is dash on Debian
    and Ubuntu — where \[, \e and \a mean nothing, so the prompt showed
    \[\e]2;blooop/devlaunch@main\a\] at every line. A corrupted prompt is worse than
    an unnamed tab, and case $- in *i* does not catch it: dash's interactive shells
    are interactive too. Guarded on $BASH_VERSION, the same test Ubuntu's own
    ~/.profile makes before sourcing ~/.bashrc.
  • A ref's trailing newline reached the profile unfiltered. is_safe_name accepts
    one trailing newline deliberately, so main\n is a ref; the newline landed inside
    the quoted word, splitting one PS1 assignment over two physical lines of a file
    every login sources. Both halves now take the same filtered name.

Checks

cargo test --workspace (1614), clippy -D warnings, fmt --check. public-api.txt
is edited by hand for the two threaded signatures — this container has no nightly to
regenerate it with, so CI's diff is the real check.

Note for review: the effect inside a real workspace has not been driven end to end
here — the PS1 ordering and the profile edit are covered by real-bash tests, but no
dl run against a live container. Worth one manual check on the host.

🤖 Generated with Claude Code

Summary by Sourcery

Name launched repository workspaces after their resolved specifications and persist those names across interactive shell prompts.

New Features:

  • Name terminal panes after the resolved repository specification, such as owner/repo@branch, while retaining workspace IDs for non-repository placements.
  • Keep the specification-based terminal name across interactive Bash prompts by installing a deduplicated profile configuration during workspace setup.

Bug Fixes:

  • Prevent workspace-ID launches from overwriting specification-based title configuration.
  • Avoid emitting literal prompt escape sequences in non-Bash login shells.
  • Filter control characters, including trailing newlines, before writing terminal names to shell profiles.

Enhancements:

  • Apply DEVLAUNCH_NO_TITLE consistently to both the immediate terminal title and persistent shell configuration.
  • Install persistent title configuration only when a workspace enters the running state and only for launches with a repository specification.

Documentation:

  • Update terminal naming documentation to describe resolved specification names, persistence behavior, supported shell behavior, and title configuration trade-offs.

Tests:

  • Add coverage for specification and ID title selection, persistent title provisioning, deduplication, shell metacharacter handling, Bash prompt ordering, non-Bash behavior, headless launches, and title opt-out behavior.

Chores:

  • Thread the optional terminal name through launch and provisioning APIs and update the public API description.

blooop added 4 commits August 22, 2026 19:09
`dl blooop/devlaunch` named its pane `devlaunch-main-zovomobo`. The id is
a poor name for two reasons: it carries no owner, so a fork and its
upstream are two tabs spelled the same, and it spells the branch as a
slug, so `feature/auth` reads as `feature-auth` — the name of a different
branch the same repository could have. Neither is recoverable from the id
afterwards. The pane is now `blooop/devlaunch@feature/auth`.

No reconstruction is involved: a launch resolved that triple on the way
in, and `Launch` now remembers it rather than keeping only the id its
`Placement` collapsed to. The other three placements have no triple and
keep the id — a bare name *is* its id, and a path or URL never had a
branch for an `@` to precede.

That much lasts about a second in an interactive session. Ubuntu's stock
`~/.bashrc` puts `\e]0;\u@\h: \w\a` at the *front* of PS1, so every
prompt renames the pane after the hostname, which is the id. So the
setup pass now appends one line to the profile a login shell reads:

    case $- in *i*) PS1="$PS1\[\e]2;"blooop/devlaunch@main"\a\]" ;; esac

Appended, because two escapes in one prompt are applied in order and the
last one sets the title — a `PROMPT_COMMAND` would lose, since bash runs
that before it prints PS1. Nothing is rewritten, so the visible
`user@host:path$` still says the hostname and only the tab changes. It
rides the hostname stage's existing round trip, is deduped by the same
content-hash mark the PATH writers use, and is inert in the `bash -lc`
one-shots that read the same profile. A test drives it through a real
bash and reads PS1 back, because the ordering is the whole feature.

`DEVLAUNCH_NO_TITLE` governs both halves. It is installed when a
workspace enters Running rather than per attach, the same bargain the
hostname stage makes for the same round-trip reason; README says so.

`public-api.txt` is edited by hand for the two threaded signatures; CI
regenerates it.
The profile line is deduped by a hash of its own text, so a name that
varies for one workspace does not replace the line -- it appends another,
and the last append is what every prompt then obeys. A workspace opened
as `blooop/devlaunch@main` and later as `devlaunch-main-zovomobo` ended
up with both lines and permanently titled after the id, which is the
string the spec exists to replace. One launch by id was enough.

The container is now told the spec or nothing. Nothing is the honest
answer for an id: it is already the hostname, so the stock prompt writes
exactly that anyway and the line would buy nothing to lose. Keying on the
spec makes the line a pure function of the triple, so a workspace has at
most one, ever.

dl's own escape still falls back to the id -- it names a pane before any
prompt exists, and for a bare name the id is what the user typed.

The test that first caught this asserted the property of `setup_stages`,
which cannot provide it: two distinct titles really do append two lines.
It is replaced by one at the layer that decides there is only ever one
name -- `Launch::container_title`.
`$PROFILE` resolves to `~/.bash_profile`, `~/.bash_login` or
`~/.profile`, and the last of those is read by any POSIX login shell --
`/bin/sh` is dash on Debian and Ubuntu. `\[`, `\e` and `\a` mean nothing
to dash, which renders a prompt literally, so the appended line put
`\[\e]2;blooop/devlaunch@main\a\]` on screen at every prompt. A corrupted
prompt is worse than an unnamed tab, and the `case $- in *i*` guard does
not catch it: dash's interactive shells are interactive too.

Guarded on `$BASH_VERSION`, which is the same test Ubuntu's own
`~/.profile` makes before it sources `~/.bashrc`. The test asserts the
implication rather than the shell, so it reads the same wherever `sh` is
bash.
`is_safe_name` accepts one trailing newline — Python's `$` anchor did,
and the quirk is ported deliberately — so `main\n` is a ref and
`blooop/devlaunch@main\n` is a name a launch can carry. dl's own escape
drops controls at the boundary it forms bytes at, but the profile line is
written down a different path, and the newline landed inside the quoted
word: one PS1 assignment split across two physical lines of a file every
login sources.

Both halves now take the same filtered name, so a title cannot be safe in
the escape and unsafe in the profile.

@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 @blooop, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Terminal titles now use the resolved spec (owner/repo@ref) instead of the workspace id, and the spec-based name is propagated through launch, attach, and provisioning so shells keep the tab named correctly via a new profile-editing stage, with safeguards and tests around title content, shell behavior, and configuration switches.

Sequence diagram for resolved workspace terminal naming

sequenceDiagram
    participant User
    participant Launch
    participant Provision
    participant Container
    participant Terminal

    User->>Launch: run(owner/repo@ref)
    Launch->>Launch: place_triple()
    Launch->>Launch: titled(workspace_id)
    Launch->>Provision: provision_tools(..., title)
    Provision->>Container: setup_stages(..., title)
    Container-->>Provision: profile updated
    Launch->>Terminal: TerminalTitle::from_host(name)
    Terminal-->>User: pane named owner/repo@ref
Loading

Flow diagram for persistent terminal title setup

flowchart TD
    A["Launch resolves owner/repo@ref"] --> B["Launch remembers resolved WorkspaceId"]
    B --> C["container_title filters the spec"]
    C --> D{DEVLAUNCH_NO_TITLE}
    D -- enabled --> E["Skip title stage"]
    D -- disabled --> F["Append deduplicated PS1 title line"]
    F --> G["Interactive Bash appends OSC 2 after stock PS1"]
    G --> H["Prompts keep the pane named after the spec"]
Loading

File-Level Changes

Change Details Files
Use resolved workspace spec as the human-facing terminal title instead of the workspace id, and thread this name through launch and attach flows.
  • Add Launch.resolved field to remember the resolved WorkspaceId triple for spec-based launches.
  • Introduce Launch::titled to compute a human-facing name (spec when available, id otherwise) for terminal titles.
  • Change TerminalTitle::from_host to accept a generic name string instead of a workspace id and sanitize it.
  • Update attach_workspace to take a TerminalTitle argument from the caller rather than recomputing from workspace id.
  • Ensure devpod commands still use the workspace id while notices and titles use the spec when present.
  • Add tests covering spec vs bare-name title behavior and id/spec separation.
rust/devlaunch-core/src/flows/launch.rs
rust/devlaunch-core/public-api.txt
Add a provisioning stage that edits the container’s profile so interactive bash prompts keep the terminal titled after the spec, with proper quoting, shell guards, and deduplication.
  • Introduce TITLE_STAGE and include it in setup_stages when a title is provided, independent of tools/zellij switches.
  • Implement profile_title_line to append an OSC 2 escape to PS1 for interactive bash shells only, guarding on $- and $BASH_VERSION.
  • Extend provision_tools/setup_pass/setup_stages to accept a title Option<&str> and pass it to the title stage.
  • Ensure profile resolution/deduping is shared with existing PATH/hostname writers and that the stage is quiet on failure.
  • Add extensive tests for title stage presence, interaction with tools/zellij switches, shell behavior (bash vs sh/dash), quoting of metacharacters, profile selection, ordering of escapes in PS1, and deduplication of the appended line.
rust/devlaunch-core/src/flows/provision.rs
Connect dl’s top-level tool provisioning to the new title-aware provisioning API and update tests to expect title stage reporting.
  • Update dl::launch Provision implementation to pass the title parameter through to devlaunch-core::provision_tools.
  • Adjust integration tests in rust/dl/tests/launch.rs to assert presence of title setup stage notices alongside hostname and zellij stages.
  • Ensure DEVLAUNCH_NO_TITLE disables both dl’s OSC write and the profile-installed title via the same switch.
rust/dl/src/launch.rs
rust/dl/tests/launch.rs
Document the new terminal naming behavior and the distinction between workspace ids and specs in the README.
  • Rewrite the README section on terminal naming to describe spec-based names (owner/repo@branch) instead of ids and why ids are poorer names.
  • Add a new "What keeps it named" subsection describing the profile PS1 edit, its ordering relative to existing prompts, shell constraints, and installation timing.
  • Clarify that only spec launches install title behavior, that DEVLAUNCH_NO_TITLE governs both escape and profile edit, and that claude’s title behavior is disabled via environment.
  • Note that zellij pane titles now show the spec in the outer tab bar, and explicitly distinguish ids (addresses) from specs (names).
README.md

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

# Conflicts:
#	rust/devlaunch-core/src/flows/provision.rs
@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.35897% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.20%. Comparing base (3530cb5) to head (4eae993).

Files with missing lines Patch % Lines
rust/devlaunch-core/src/flows/launch.rs 99.23% 2 Missing ⚠️
rust/devlaunch-core/src/flows/provision.rs 99.50% 1 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)
rust 95.56% <99.35%> (+0.05%) ⬆️

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

Components Coverage Δ
shipped code (rust) 95.56% <99.35%> (+0.05%) ⬆️
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.

blooop added 2 commits August 22, 2026 20:05
The two features now sit together, so the Workspace IDs section can point at
both places a workspace is named rather than only at the tab.
@blooop
blooop merged commit a199061 into main Aug 22, 2026
14 checks passed
@blooop
blooop deleted the title-spec branch August 22, 2026 20:09

@blooop blooop left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This was generated by AI during review.

Retrospective wf-review of #358 and #371 as one surface — terminal tab/pane
naming — judged against current main (a5f7ed8). Both merged with no review
(Sourcery rate-limited). Every finding below was checked to still reproduce on main
and is attributed to the PR whose line survives; anything #371 already fixed in #358
is not listed. The two axes were run as independent passes and are not merged or
reranked.

The escape-safety hunt came back clean, and that is worth saying explicitly
because it was the likeliest place for a real defect. sanitize_title
(rust/devlaunch-core/src/flows/launch.rs:1837) drops the whole Cc category, so
ESC, BEL, DEL and the 8-bit ST are all gone before the OSC is formed; the same filter
runs on the profile half (launch.rs:2973); shell::quote interpolates the name as
its own word; and a spec cannot hold $ or a backtick because is_safe_name refuses
everything but word characters, ., / and -, which is what keeps the PS1
value out of reach of bash's prompt-time command substitution. Mutating the filter
to a no-op turns a_spec_cannot_smuggle_a_second_escape_into_the_title and
a_name_that_is_nothing_but_controls_is_no_title_at_all red; mutating \x1b]2; to
\x1b]0; reddens five unit tests plus the real-pty test; mutating the PS1 append
to a prepend reddens the ordering test and the real-bash test. Test strength on
this surface is good
— no vacuous test was found. Nothing herdr-shaped was left
dangling by #376 either: grep -ri herdr rust/ README.md docs/ is empty.


Standards

Repo-documented standards first. README.md §Naming the terminal after the
workspace
is the written spec for this surface, and CLAUDE.md requires it be kept
true; docs/rust-rewrite-plan.md supplies the layering rules.

1. #371 — the "keeps that name" mechanism does not survive a nested shell

README.md:704 promises the name survives "an interactive shell", and the whole of
§What keeps it named rests on one line appended to $PROFILE
(provision.rs:506, provision.rs:1025). $PROFILE resolves only to
.bash_profile/.bash_login/.profile (provision.rs:430) — files a login
shell reads. PS1 is not exported, and Ubuntu's ~/.bashrc sets it unconditionally
for every interactive shell. So the first nested interactive bash inside the
workspace — bash, tmux, or a pane of the zellij session this repo's own
DEVLAUNCH_ZELLIJ installs inside the container — reads ~/.bashrc only, gets the
stock \e]0;\u@\h prompt, and renames the pane back to the hostname (the id).

Driven through real bash, stock Ubuntu skeleton, our line appended to .profile:

login -i   : \[\e]0;\u@\h: \w\a\]\u@\h:\w\$ \[\e]2;blooop/devlaunch@main\a\]
nested -i  : \[\e]0;\u@\h: \w\a\]\u@\h:\w\$

The root cause is reusing profile_prepend — a writer built for PATH, which is
exported and is inherited — to install a variable that is neither.

Fix: append the same guarded line to ~/.bashrc (in addition to, or instead of,
$PROFILE). Ubuntu's ~/.profile sources ~/.bashrc, so a line at the end of
.bashrc lands after the stock PS1 in the login case too, and the existing
content-hash dedupe keeps it single.

2. #371 — "a workspace has at most one such line, ever" is false for case variants

README.md:734: "Keying on the spec alone means a workspace has at most one such
line, ever."
— and launch.rs:2949 says the same. But WorkspaceId::suffix()
lowercases owner and repo before hashing (deliberately: NVIDIA/cuda-samples and
nvidia/cuda-samples are one repository, one container), while spec_of
(launch.rs:1857) formats the owner and repo as typed. One workspace, two spec
strings, two content-hash marks, two appended lines:

id      = cuda-samples-main-libemaka        (identical for both)
spec A  = NVIDIA/cuda-samples@main
spec B  = nvidia/cuda-samples@main
line A  = case $- in *i*) [ -n "$BASH_VERSION" ] && PS1="$PS1\[\e]2;"NVIDIA/cuda-samples@main"\a\]" ;; esac
line B  = case $- in *i*) [ -n "$BASH_VERSION" ] && PS1="$PS1\[\e]2;"nvidia/cuda-samples@main"\a\]" ;; esac

Reached by launching the same workspace under two spellings across a stop/start (the
AfterUp pass always travels). This is the same shape as the defect #371's own
self-review fixed for ids — the last append wins permanently, so a later
dl NVIDIA/… gets a tab reading nvidia/… and no further launch can change it.

Fix: key spec_of on the same normalisation suffix() uses — lowercase owner and
repo, keep the ref verbatim — so the line is a pure function of the workspace
rather than of the spelling.

3. #371 — a trusted verdict swallows the title stage, and the feature silently never applies

provision.rs:1814 returns CachedProvisioned before setup_pass on a
PassOccasion::TopUp. The title is not part of the verdict key (trusted() is
workspace id + workspace_result.json mtime), so a workspace whose pass ran with no
title keeps no title for as long as it stays up. Proven with a test against the real
flow:

// 1. `dl <workspace-id>`: no triple -> title None; pass runs, verdict recorded.
provision_tools(&by_id, "myws", TopUp, INSTALLING, None, .., Some(&verdicts), ..)
    == Ok(Provisioning::AlreadyProvisioned);   // by_id.count() == 1

// 2. `dl owner/repo@main` on the still-running workspace:
provision_tools(&by_spec, "myws", TopUp, INSTALLING, Some("owner/repo@main"), .., Some(&verdicts), ..)
    == Ok(Provisioning::CachedProvisioned);    // by_spec.count() == 0

dl/src/launch.rs:167 passes Some(&self.verdicts) in production, so this is the
shipped path. It also covers the upgrade case: any workspace already running when a
user upgrades to 0.8.0+ never gets the line. README.md:737 documents "installed
when a workspace enters Running, not on every attach"
, which does not describe this
— the workspace did enter Running, just without a name to install.

Fix: either include the title in the marker the verdict is checked against, or
hoist the title stage in front of the cache short-circuit (it is one line in a script
that is already being composed, and the cache exists to save a trip, so the cheap
version is to let a Some(title) on a TopUp defeat trusted()).

4. #371 — the documented length bound does not exist

launch.rs:1780-1789 and README.md:689-694 both assert "a triple with an unsafe or
overlong part is refused before a session exists"
and "the title is short because a
workspace with a long name cannot exist"
. WorkspaceId::new enforces no length at
all; value() truncates to 47. Proven:

let ws = WorkspaceId::new("owner", "repo", &"a".repeat(300)).unwrap();
assert_eq!(ws.value().len(), 47);      // the id is capped
assert_eq!(spec_of(&ws).len(), 311);   // the title is not

So devpod's 48-character rule never sees the title, and a long branch name puts a
311-character OSC on the terminal and 311 characters into a PS1 re-emitted at every
prompt. Cosmetic, but the two paragraphs that say it cannot happen are load-bearing
documentation under CLAUDE.md's "keep README up to date".

Fix: truncate the title (keeping the ref's tail, which is the identifying half),
or correct both paragraphs to say the title is unbounded.

5. #358 — the title is never restored, and is written before the session is known to exist

attach_workspace (launch.rs:1903) says the notice unconditionally, ahead of
workspace_ssh. A refused session leaves the host terminal named after a workspace it
never entered, and a normal dl <ws> -- cmd (every aid launch) leaves the tab
reading blooop/devlaunch@main after dl has exited. Nothing writes a title on the
way out.

Fix: bracket the session with XTWINOPS — CSI 22;2t to push the title before the
OSC and CSI 23;2t to pop it after — which xterm, kitty, foot, wezterm and tmux
honour and everything else ignores. Writing an empty OSC 2 would blank the name rather
than restore it, which the code already correctly refuses to do.

6. Fowler baseline

  • Mysterious Name (#371). profile_prepend (provision.rs:460) appends, and its
    doc still opens "One PATH line appended to $PROFILE" (provision.rs:440) — it now
    also carries a PS1 line, which is the one case where the PATH-shaped assumption
    (exported, inherited) is wrong. See finding 1. Rename and restate the contract.
  • Primitive Obsession / long parameter list (#358 introduced, #371 widened).
    title: Option<&str> is threaded raw through seven signatures —
    Provision::provision_tools, workspace_up, up_under_stage,
    provision::provision_tools, provision, setup_pass, setup_stages — and paid for
    with two #[allow(clippy::too_many_arguments)] (provision.rs:1779, 1798). The
    sanitising that makes it safe lives in the private Launch::container_title, while
    the public provision::provision_tools (in public-api.rest.txt:1970) takes an
    unfiltered &str. A ContainerTitle(String) newtype whose only constructor runs
    sanitize_title would collapse the parameter and move the invariant into the type.
  • Illegal state representable (#358). TerminalTitle::Write(String)
    (launch.rs:1792) is a public tuple variant holding raw bytes that dl's Saying
    sink writes verbatim to stderr (dl/src/render.rs:1711). Any caller of the frozen
    public API can construct one with arbitrary escapes. Private field plus a constructor
    would make the sanitising unskippable.
  • Checked and not findings: Duplicated Code (titled and container_title differ
    on purpose and each says why), Speculative Generality, Middle Man, Refused Bequest.

Spec

No spec available — for either PR.

Neither #358 nor #371 closes a ticket: closingIssuesReferences is empty for both, no
commit on either branch (3703cf7, 2a8f4f9, 32703a9, 683da23; 816996b,
0caa214, 57960aa, 2ae964f) carries a Closes/Fixes/#n reference, and the
branch names (tabname, title-spec) are not wayfinder/devlaunch-N nodes. A search
of the tracker for title in:title, tab in:title, pane, terminal title,
DEVLAUNCH_NO_TITLE and which workspace returns nothing describing this behaviour.

Per the method, no requirements are inferred from the code, so this axis reports
nothing. The behavioural findings above are held against README.md, which is a
repo-documented standard rather than a spec, and are filed under Standards for that
reason.


Verdict

Comment (retrospective — both PRs are merged; --request-changes is unavailable on
a same-account PR anyway).

Nothing here is a security defect: the escape-injection surface the design worried
about most is genuinely closed, on both halves, and the tests hold under mutation.

Blocking, had this been reviewed before merge — two of them are the feature not
actually doing what its README says:

  1. Finding 1 — the name is lost at the first nested shell or zellij pane, which is
    most of what "and keeps that name" was for.
  2. Finding 3 — a trusted verdict means the line is never installed at all for a spec
    launch onto a running workspace.

Non-blocking: findings 2, 4, 5 and the four Fowler items. Finding 2 is a small, exact
fix (normalise spec_of the way suffix() normalises) and is worth taking with
finding 1.

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