From 1e2a24b9618d28125dea4bdb344ce77a0b4f74ba Mon Sep 17 00:00:00 2001 From: blooop Date: Sun, 30 Aug 2026 16:56:33 +0000 Subject: [PATCH 1/4] Say what the terminal is actually named README said `dl blooop/devlaunch` names the pane `devlaunch-main-3j1t`, "the workspace id, the same string `dl --ls` prints and the container's hostname carries". docs/workspace-tools.md:511 says the opposite about the same command: the pane is `devlaunch@main`, the id read for a person with the hashed suffix off, *where* `dl --ls` and the hostname say `devlaunch-main-3j1t`. The docs page is right. `titled()` in flows/launch.rs:2850 answers `workspace.label()` whenever the devpod id is the derived one, which is every launch that does not name a recorded id, and `an_id_metadata_recorded_is_titled_ by_that_id_and_not_by_the_triples_label` pins the one arm that differs. The README sentence is the pre-change behaviour that workspace-tools.md:530 already describes as what it "used to be". Nothing caught it: the README's guard (test_readme_cli_doc.py) holds flags to being mentioned, not claims to being true, so the most read sentence about this feature was the least checked one. Found by a clean-room reconstruction of the spec from the CLI surface, the docs and the test names alone, with the implementation withheld. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5a0f68..576689d 100644 --- a/README.md +++ b/README.md @@ -355,8 +355,9 @@ anything to its `devcontainer.json`. `DEVLAUNCH_ZELLIJ=1` is the ask, once in a shell profile or per launch; it costs 2.2s to 3.5s of a cold launch, which is why it waits to be asked. - **A terminal named after the workspace.** `dl blooop/devlaunch` names the pane - `devlaunch-main-3j1t` in zellij, tmux, or a plain terminal window: the workspace id, - the same string `dl --ls` prints and the container's hostname carries. + `devlaunch@main` in zellij, tmux, or a plain terminal window: the workspace id read for + a person, with the hashed suffix off, where `dl --ls` and the container's hostname both + say `devlaunch-main-3j1t`. - **A shared pixi package cache**, bound in from the host, so dotfiles that provision tools with `pixi global sync` download each package once per machine instead of once per container. On one measured profile that is 18s to 28s instead of 62s to 113s and 1.2 GB. From ee0119a7a50067aa776a1f76ff53093f731e8576 Mon Sep 17 00:00:00 2001 From: blooop Date: Sun, 30 Aug 2026 17:25:59 +0000 Subject: [PATCH 2/4] State where --force may sit, since every example writes it there anyway docs/cli.md writes `dl rm --force` a dozen times and never says that the position is load-bearing. It is: `--force` has to follow both the workspace and the verb. In the verb slot it is read as the verb (`Unknown command '--force'`) and in the workspace slot as the workspace (`Unknown workspace '--force'`), both exit 1 with nothing deleted. A global command has no slot for it to fall into, so `dl --force --prune` and `dl --prune --force` are one line. Two rules for one spelling, discoverable only by being refused, and written down until now only in the names of the tests that pin them. This says it once beside the docker paragraph that already explains which `--force` is which, and cites those tests so the rule and its guard are one lookup apart. --- docs/cli.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/cli.md b/docs/cli.md index 6896553..2f03cb1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -167,6 +167,19 @@ too: `dl rm` deletes now, `dl --rm` deletes after, and neither has to twice to work out which was meant. `--force` follows docker as well. It belongs to `dl rm --force`, never to `--rm`. +**On a workspace verb `--force` is positional, and on a global command it is not.** +It has to follow both the workspace and the verb, which is the position every example +here writes it in. Put it in the verb slot and it is read as the verb, so +`dl --force rm` answers `Unknown command '--force'`; put it in the workspace slot +and it is read as the workspace, so `dl --force rm` answers +`Unknown workspace '--force'`. Both exit 1 and delete nothing, which is the point: a +flag that has landed somewhere it cannot mean what you meant refuses rather than +being dropped. A global command has no slot for it to fall into, so there the +placement stops mattering and `dl --force --prune` and `dl --prune --force` are one +line. Pinned by `force_after_the_verb_still_deletes` and its two neighbours in +`rust/dl/tests/grammar.rs`, and by `a_globals_force_reads_the_same_wherever_it_sits` +in `rust/dl/src/cli.rs`. + **It stops at work that is nowhere else.** The removal is `dl rm`'s, guard included, so a clone holding uncommitted or unpushed work, or one git could not read to find out, refuses, says which, and leaves the workspace standing: From 25cb71c421baa288c81fad790552d45d5b8b78f8 Mon Sep 17 00:00:00 2001 From: blooop Date: Sun, 30 Aug 2026 17:25:59 +0000 Subject: [PATCH 3/4] Diff the two copies of what the terminal is named The terminal title is stated on two pages: the README's feature list, where a reader meets it, and docs/workspace-tools.md, where the reasoning for the spelling lives. That is a second hand-maintained copy of one fact, which this repository allows only with a test beside it that diffs the copies. The rule earned the test rather than being applied on principle. The two pages had already drifted apart on this exact sentence, in opposite directions, about the same example command, and nothing failed: the README's guard holds a flag to being mentioned, not a claim to being true. The instrument is the sentence both pages already write, "names the pane ``", so neither carries a marker for this test's benefit and a page that drops the claim fails here rather than passing quietly. Reverting the README to its old wording reproduces the failure. --- test/test_title_claim_agrees.py | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/test_title_claim_agrees.py diff --git a/test/test_title_claim_agrees.py b/test/test_title_claim_agrees.py new file mode 100644 index 0000000..4a7efb9 --- /dev/null +++ b/test/test_title_claim_agrees.py @@ -0,0 +1,60 @@ +"""The terminal title is stated on two pages, so the two statements are diffed. + +The README orients and `docs/` explains, and the terminal title is one of the +facts that has to appear in both: the README's feature list is where a reader +first meets it, and `docs/workspace-tools.md` is where the reasoning for the +spelling lives. That makes it a second hand-maintained copy of one fact, which +this repository allows only with a test beside it that diffs the copies. + +The rule earned its test rather than being applied on principle. The README said +`dl blooop/devlaunch` names the pane `devlaunch-main-3j1t`, "the workspace id", +where the docs page said `devlaunch@main` and contrasted it against exactly the +string the README asserted. `titled()` in +`rust/devlaunch-core/src/flows/launch.rs` answers the label whenever the devpod +id is the derived one, so the docs page was right and the README was describing +behaviour that same page already called what it "used to be". Nothing caught it: +`test_readme_cli_doc.py` holds a flag to being mentioned, not a claim to being +true, so the most read sentence about this feature was the least checked one. + +The instrument is the sentence both pages already write. Each says +"names the pane ``" of the same example command, so the name is +extractable without either page carrying a marker for this test's benefit, and +a page that stops making the claim fails here rather than passing quietly. +""" + +from __future__ import annotations + +import re +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent + +# The pages that state it. Both, or the diff below has nothing to compare. +PAGES = (REPO_ROOT / "README.md", REPO_ROOT / "docs" / "workspace-tools.md") + +# "names the pane `devlaunch@main`", across the line break either page may wrap at. +CLAIM = re.compile(r"names the pane\s+`([^`]+)`") + + +def _claims(page: Path) -> list[str]: + return CLAIM.findall(page.read_text(encoding="utf-8")) + + +def test_both_pages_still_state_the_pane_name(): + """A claim that vanished would make the diff below vacuously true.""" + for page in PAGES: + assert _claims(page), ( + f"{page.relative_to(REPO_ROOT)} no longer says what the pane is named. " + "Either restore the claim or retire this guard with the copy it diffs" + ) + + +def test_the_pages_agree_on_what_the_pane_is_named(): + readme, reference = (set(_claims(page)) for page in PAGES) + + assert readme == reference, ( + f"README.md says the pane is named {sorted(readme)} and " + f"docs/workspace-tools.md says {sorted(reference)}. One of them has " + "drifted from what `titled()` actually writes; the docs page owns the " + "reasoning, so check it against the code before editing either" + ) From f0d2e4d67393bca611bc1f4c15fb9ba878eb1055 Mon Sep 17 00:00:00 2001 From: blooop Date: Sun, 30 Aug 2026 17:47:40 +0000 Subject: [PATCH 4/4] Answer the review: --force is dropped, not refused, past the verb A fresh-context review of this branch found the `--force` paragraph asserting a rule the grammar does not implement, and the title guard pinning something weaker than it claimed. Both are fixed here. **The paragraph generalised two cases into a law.** It said a misplaced `--force` "refuses rather than being dropped". True of the workspace and verb slots; false of everything past the verb, where only `rm` and `rme` read the flag and `up`, `stop`, `kill`, `code`, `restart`, `recreate`, `reset` and `dotfiles` take it and discard it in silence. cli.rs says so twice already ("the flag is the no-op it is on `up` and `stop`"; "every other verb drops it on the floor") and README says there is no `dl kill --force` to type, which in fact parses. Verified against the built binary rather than inferred. The section now states both halves, names `kill` as the one worth remembering, and adds the selector form, which cannot be forced at all: `dl rm --force` is the verb-slot refusal and its suggested `dl rm -- --force` would run the flag as a shell command. It also moved. Sitting inside `### --rm: the throwaway workspace` it separated "It stops at work that is nowhere else" from the `--rm` that sentence's "It" refers to, and handed the reader `--force` as the antecedent: the exact thing that goes past that check. It is its own section now, beside the other grammar sections, and cites the 259-ordering matrix test that is its thesis. **The guard pinned agreement, not truth.** It diffed two prose pages, so the inverted repair, editing the docs page down to the README's wrong string, would have passed it while reporting that `titled()` had been consulted. It had not. `flows/launch.rs` carries the same sentence verbatim and is the one copy that cannot be wrong without the behaviour being wrong, so the prose is now held against it. Both drift directions are checked, including that inverted one. Anchoring the regex on the example command fixes two more: it no longer matches inside "every prompt *renames* the pane", which the docs and provision.rs both say of the prompt's own later write, and set equality no longer forbids `docs/` from documenting the examples that are titled differently, which it does. Also: the README named one of the title's two transformations, and dropping the suffix alone gives `devlaunch-main`, not `devlaunch@main`. Both are named now, with the id-named case the docs page already covers. And all three copies cite the guard, so whoever edits one learns the other two exist. --- README.md | 7 +- docs/cli.md | 51 +++++++++--- docs/workspace-tools.md | 3 +- rust/devlaunch-core/src/flows/launch.rs | 6 ++ test/test_title_claim_agrees.py | 103 +++++++++++++++--------- 5 files changed, 115 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 576689d..2d50d36 100644 --- a/README.md +++ b/README.md @@ -356,8 +356,11 @@ anything to its `devcontainer.json`. of a cold launch, which is why it waits to be asked. - **A terminal named after the workspace.** `dl blooop/devlaunch` names the pane `devlaunch@main` in zellij, tmux, or a plain terminal window: the workspace id read for - a person, with the hashed suffix off, where `dl --ls` and the container's hostname both - say `devlaunch-main-3j1t`. + a person, with the hashed suffix off and the dash before the branch spelled `@`, where + `dl --ls` and the container's hostname both say `devlaunch-main-3j1t`. A workspace you + name by its id keeps the id. + - **A shared pixi package cache**, bound in from the host, so dotfiles that provision tools with `pixi global sync` download each package once per machine instead of once per container. On one measured profile that is 18s to 28s instead of 62s to 113s and 1.2 GB. diff --git a/docs/cli.md b/docs/cli.md index 2f03cb1..439a5f1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -167,19 +167,6 @@ too: `dl rm` deletes now, `dl --rm` deletes after, and neither has to twice to work out which was meant. `--force` follows docker as well. It belongs to `dl rm --force`, never to `--rm`. -**On a workspace verb `--force` is positional, and on a global command it is not.** -It has to follow both the workspace and the verb, which is the position every example -here writes it in. Put it in the verb slot and it is read as the verb, so -`dl --force rm` answers `Unknown command '--force'`; put it in the workspace slot -and it is read as the workspace, so `dl --force rm` answers -`Unknown workspace '--force'`. Both exit 1 and delete nothing, which is the point: a -flag that has landed somewhere it cannot mean what you meant refuses rather than -being dropped. A global command has no slot for it to fall into, so there the -placement stops mattering and `dl --force --prune` and `dl --prune --force` are one -line. Pinned by `force_after_the_verb_still_deletes` and its two neighbours in -`rust/dl/tests/grammar.rs`, and by `a_globals_force_reads_the_same_wherever_it_sits` -in `rust/dl/src/cli.rs`. - **It stops at work that is nowhere else.** The removal is `dl rm`'s, guard included, so a clone holding uncommitted or unpushed work, or one git could not read to find out, refuses, says which, and leaves the workspace standing: @@ -416,6 +403,44 @@ reporting an unknown workspace called `prune`, and a workspace that really is ca `prune` is still reachable as `dl stop prune`. Use `dl rm` from now on. +### Where `--force` may sit + +`--force` is read in one position only: after both the workspace and the verb, which +is where every example on this page writes it. The two slots ahead of it are already +spoken for, so a `--force` that lands in either is read as the word that belongs +there and refused: + +``` +dl --force rm -> Unknown command '--force'. (exit 1, nothing deleted) +dl --force rm -> Unknown workspace '--force'. (exit 1, nothing deleted) +``` + +**Past the verb it is read, but only `rm` and `rme` do anything with it.** Every +other verb takes the flag and drops it: `dl up --force`, `dl stop --force` +and `dl recreate --force` all run exactly as they would without it, silently. +That is the one place this grammar discards a word rather than refusing it, and it is +worth knowing because the two halves of the rule read as though they were one: a +misplaced `--force` refuses, a meaningless one does not. `kill` is the case to +remember, since it is the verb whose whole point is going ahead anyway. It has no +`--force` to type and ignores one offered. + +**The selector form cannot be forced at all.** `dl rm` with no workspace opens the +picker, which leaves no slot after the verb, so `dl rm --force` is the verb-slot +refusal above. There is no spelling of "pick some workspaces and force the removal"; +name the workspace, or answer the refusal the guard prints. The diagnostic's +suggestion (`dl rm -- --force`) is the generic one for an unknown verb word and would +run `--force` as a shell command, which is not what anybody typing that meant. + +**A global command has no slots, so placement stops mattering there.** +`dl --force --prune` and `dl --prune --force` are one line, and the same holds for +every other global. Note this is about the literal word `--force`: `--devcontainer`, +`--force-worktrees` and `-y` have rules of their own. + +Pinned by `force_deletes_only_where_it_follows_both_the_name_and_the_verb` and +`a_globals_force_reads_the_same_wherever_it_sits` in `rust/dl/src/cli.rs`, and by +`force_after_the_verb_still_deletes` with its two neighbours in +`rust/dl/tests/grammar.rs`. + ## Remote Control: every `aid` session, on your phone too Every `aid` launch of claude starts with Claude Code's Remote Control on. There is no diff --git a/docs/workspace-tools.md b/docs/workspace-tools.md index 09a1fa7..2ced5ca 100644 --- a/docs/workspace-tools.md +++ b/docs/workspace-tools.md @@ -511,7 +511,8 @@ session; this one writes an escape sequence and two lines into a profile. `@`, with the hashed suffix off.** `dl blooop/devlaunch` names the pane `devlaunch@main` where devpod, the container's hostname and the `WORKSPACE` column of `dl --ls` all say `devlaunch-main-3j1t`. One string with two characters changed, so a -tab and a listing row still match by eye. +tab and a listing row still match by eye. The same sentence is in `README.md` and in +`flows/launch.rs`, and `test_title_claim_agrees.py` holds all three to one answer. Two characters, and they are the two a glance cannot use. The suffix carries the workspace's identity and none of its meaning: it is what keeps two branches whose diff --git a/rust/devlaunch-core/src/flows/launch.rs b/rust/devlaunch-core/src/flows/launch.rs index 7d45046..b0a4ed7 100644 --- a/rust/devlaunch-core/src/flows/launch.rs +++ b/rust/devlaunch-core/src/flows/launch.rs @@ -2237,6 +2237,12 @@ pub(crate) fn dotfiles_update( /// devpod, the hostname and the `WORKSPACE` column of `dl --ls` all say /// `devlaunch-main-3j1t`. /// +/// That sentence is also written in `README.md` and `docs/workspace-tools.md`, and +/// `test_title_claim_agrees.py` diffs both of them against this one: the prose is +/// held against the module that computes the title rather than against itself, +/// because two pages can agree and both be wrong. Changing the name here means +/// changing it there in the same commit. +/// /// **It is the id, not a second derivation of the spec.** The slugs and the /// truncation are [`WorkspaceId::label`]'s, which are [`WorkspaceId::value`]'s, so a /// tab and a listing row still match by eye: one is the other with a suffix removed diff --git a/test/test_title_claim_agrees.py b/test/test_title_claim_agrees.py index 4a7efb9..ba967ac 100644 --- a/test/test_title_claim_agrees.py +++ b/test/test_title_claim_agrees.py @@ -1,25 +1,34 @@ -"""The terminal title is stated on two pages, so the two statements are diffed. - -The README orients and `docs/` explains, and the terminal title is one of the -facts that has to appear in both: the README's feature list is where a reader -first meets it, and `docs/workspace-tools.md` is where the reasoning for the -spelling lives. That makes it a second hand-maintained copy of one fact, which -this repository allows only with a test beside it that diffs the copies. - -The rule earned its test rather than being applied on principle. The README said -`dl blooop/devlaunch` names the pane `devlaunch-main-3j1t`, "the workspace id", -where the docs page said `devlaunch@main` and contrasted it against exactly the -string the README asserted. `titled()` in -`rust/devlaunch-core/src/flows/launch.rs` answers the label whenever the devpod -id is the derived one, so the docs page was right and the README was describing -behaviour that same page already called what it "used to be". Nothing caught it: +"""The terminal title is stated in three places, so the three statements are diffed. + +The title is a fact that has to appear more than once: the README's feature list +is where a reader first meets it, `docs/workspace-tools.md` is where the +reasoning for the spelling lives, and `flows/launch.rs` is where it is computed +and where the doc comment explains the derivation to whoever changes it. That +makes two hand-maintained copies of what the third one does, which this +repository allows only with a test beside them that diffs the copies. + +The rule earned the test rather than being applied on principle. The README and +the docs page had already drifted apart on this exact sentence, in opposite +directions, about the same example command, and nothing failed: `test_readme_cli_doc.py` holds a flag to being mentioned, not a claim to being -true, so the most read sentence about this feature was the least checked one. +true. + +**`launch.rs` is in the list, and it is the reason this guard means anything.** +Diffing the two prose pages against each other would only pin that they agree, +which the wrong pair of edits satisfies as easily as the right one: had the +drift been repaired by editing the docs page down to the README's wrong string, +a two-page guard would have blessed it. The module that computes the title is +the one copy that cannot be wrong without the behaviour being wrong, so it is +the one the prose is held against. -The instrument is the sentence both pages already write. Each says -"names the pane ``" of the same example command, so the name is -extractable without either page carrying a marker for this test's benefit, and -a page that stops making the claim fails here rather than passing quietly. +The instrument is the sentence all three already write about one example +command, so no page carries a marker for this test's benefit. Anchoring on the +command is also what keeps the guard honest in both directions: `docs/` documents +the cases that are titled *differently* (a path spec, a bare id), and those +sentences name a different command, so documenting one more of them cannot fail +this test. It also cannot collide with "every prompt *renames* the pane", which +`docs/workspace-tools.md` and `flows/provision.rs` both say about the shell +prompt's own later write. """ from __future__ import annotations @@ -29,32 +38,48 @@ REPO_ROOT = Path(__file__).resolve().parent.parent -# The pages that state it. Both, or the diff below has nothing to compare. -PAGES = (REPO_ROOT / "README.md", REPO_ROOT / "docs" / "workspace-tools.md") +# The three places the fact is written. `launch.rs` is not optional: see the +# module docstring for why a prose-only diff would pass the inverted repair. +SOURCES = ( + Path("README.md"), + Path("docs") / "workspace-tools.md", + Path("rust") / "devlaunch-core" / "src" / "flows" / "launch.rs", +) -# "names the pane `devlaunch@main`", across the line break either page may wrap at. -CLAIM = re.compile(r"names the pane\s+`([^`]+)`") +# The claim, anchored on the one example command all three use, across whatever +# line break each happens to wrap at. Anchored rather than matched loosely so +# that a page documenting a differently-titled example cannot fail this test. +CLAIM = re.compile(r"`dl blooop/devlaunch` names the pane\s+(?://[/!]?\s*)?`([^`]+)`") -def _claims(page: Path) -> list[str]: - return CLAIM.findall(page.read_text(encoding="utf-8")) +def _claims(relative: Path) -> list[str]: + return CLAIM.findall((REPO_ROOT / relative).read_text(encoding="utf-8")) -def test_both_pages_still_state_the_pane_name(): - """A claim that vanished would make the diff below vacuously true.""" - for page in PAGES: - assert _claims(page), ( - f"{page.relative_to(REPO_ROOT)} no longer says what the pane is named. " - "Either restore the claim or retire this guard with the copy it diffs" +def test_every_source_still_states_the_pane_name(): + """A claim that vanished would make the comparison below vacuously true.""" + for relative in SOURCES: + assert _claims(relative), ( + f"{relative} no longer says what `dl blooop/devlaunch` names the pane. " + "Either restore the claim or retire this guard along with the copy it " + "diffs" ) -def test_the_pages_agree_on_what_the_pane_is_named(): - readme, reference = (set(_claims(page)) for page in PAGES) +def test_the_prose_agrees_with_the_module_that_computes_the_title(): + truth = Path("rust") / "devlaunch-core" / "src" / "flows" / "launch.rs" + computed = set(_claims(truth)) - assert readme == reference, ( - f"README.md says the pane is named {sorted(readme)} and " - f"docs/workspace-tools.md says {sorted(reference)}. One of them has " - "drifted from what `titled()` actually writes; the docs page owns the " - "reasoning, so check it against the code before editing either" + assert len(computed) == 1, ( + f"{truth} states more than one pane name for the same command: " + f"{sorted(computed)}. This guard reads it as the answer, so it has to be " + "one answer" ) + + for relative in SOURCES: + assert set(_claims(relative)) == computed, ( + f"{relative} says `dl blooop/devlaunch` names the pane " + f"{sorted(set(_claims(relative)))}, and {truth} says " + f"{sorted(computed)}. The module is where the title is computed, so " + "the prose is what moves unless `titled()` itself changed" + )