From 1e2a24b9618d28125dea4bdb344ce77a0b4f74ba Mon Sep 17 00:00:00 2001 From: blooop Date: Sun, 30 Aug 2026 16:56:33 +0000 Subject: [PATCH 01/10] 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 e5a0f680..576689d3 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 02/10] 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 6896553f..2f03cb1b 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 03/10] 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 00000000..4a7efb9a --- /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 04/10] 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 576689d3..2d50d364 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 2f03cb1b..439a5f19 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 09a1fa7f..2ced5ca4 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 7d450463..b0a4ed79 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 4a7efb9a..ba967acc 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" + ) From f1bd1a604aa4f33394f7f996be9b4e180c631755 Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 08:52:14 +0000 Subject: [PATCH 05/10] fix: the title guard called a doc comment the computed answer The guard added in 25cb71c and rebuilt in f0d2e4d claimed to hold the prose against the module that computes the title. It did not. What it read out of `flows/launch.rs` is a `///` comment, matched by the same regex it runs over the two Markdown pages, so all three sources were prose and the third only looked different. The commit message and the PR description both rest on the sentence "the one copy that cannot be wrong without the behaviour being wrong", and that was the copy the compiler never reads. Reproduced rather than argued. Change the separator in `WorkspaceId::label`, update the one assertion that pins it so `cargo test` goes green again, leave the prose alone: the old guard passes on three pages that are now all wrong. That is the same blind spot the two-page version had, needing one more simultaneous edit. The answer now comes from that assertion. `a_label_is_the_id_with_the_suffix_off_ and_an_at_where_the_dash_was` gains the example every page writes about, and the guard reads the literal out of it. It is the only copy that cannot be wrong without a test failing, because it is compared against what `label()` returns every time the suite runs. Rerunning the mutation now fails the guard, as do drifts in each of the four prose copies taken one at a time. Three smaller defects in the same six lines, fixed here because the rewrite lands on top of them: - `docs/workspaces.md` states the same name twice, in the table of the three renderings and the sentence under it, and nothing read it. Its table row is now a source, so the count is four. - The one-answer check said "states more than one pane name" when it had found none, which is the reachable case: reword the assertion and the message names a number it is not. It now reports the count it saw. - `truth` was a second hand-written spelling of a path already in `SOURCES`. If they drifted the guard raised `FileNotFoundError` instead of failing by name. One literal now, and a missing file fails with a sentence. The prose moves in the same commit because it describes the guard: `launch.rs` said the pages are diffed "against this one", the README and `docs/workspace-tools.md` said "the three", and all three said the guard diffs the *sentence* when it diffs one backticked name. Splitting those out would leave a commit whose tree states something false. The README also cited `rust/.../flows/launch.rs`, a path that greps to nothing, which the standing rule on citations forbids and `test_citations_resolve.py` does not reach. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- README.md | 5 +- docs/workspace-tools.md | 7 +- docs/workspaces.md | 3 + .../devlaunch-core/src/domain/workspace_id.rs | 8 + rust/devlaunch-core/src/flows/launch.rs | 11 +- test/test_title_claim_agrees.py | 139 +++++++++++------- 6 files changed, 110 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 2d50d364..aae4707c 100644 --- a/README.md +++ b/README.md @@ -359,8 +359,9 @@ anything to its `devcontainer.json`. 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/workspace-tools.md b/docs/workspace-tools.md index 2ced5ca4..22ec14a2 100644 --- a/docs/workspace-tools.md +++ b/docs/workspace-tools.md @@ -511,8 +511,11 @@ 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. The same sentence is in `README.md` and in -`flows/launch.rs`, and `test_title_claim_agrees.py` holds all three to one answer. +tab and a listing row still match by eye. `README.md`, `docs/workspaces.md` and +`rust/devlaunch-core/src/flows/launch.rs` name the pane too, and +`test_title_claim_agrees.py` holds the name all four write against the `label()` +assertion in `rust/devlaunch-core/src/domain/workspace_id.rs`, which `cargo test` +checks against what `label()` returns. 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/docs/workspaces.md b/docs/workspaces.md index 4b685797..d1a816b6 100644 --- a/docs/workspaces.md +++ b/docs/workspaces.md @@ -62,6 +62,9 @@ The other two are renderings of that same id, cut to what their surface is for: They are renderings and not separate derivations, which is what keeps them matchable: the tab is the id with the suffix off and one dash spelled `@`, so `devlaunch@main` and `devlaunch-main-3j1t` are recognisably the same workspace. +The tab's name in that table is one of four copies, and +`test_title_claim_agrees.py` holds them to the `label()` assertion in +`rust/devlaunch-core/src/domain/workspace_id.rs`. Which dash the `@` replaces is not readable off the id, since a repo slug holds dashes of its own, so the tab's name travels with the launch that resolved it rather diff --git a/rust/devlaunch-core/src/domain/workspace_id.rs b/rust/devlaunch-core/src/domain/workspace_id.rs index f3370a41..7c51a845 100644 --- a/rust/devlaunch-core/src/domain/workspace_id.rs +++ b/rust/devlaunch-core/src/domain/workspace_id.rs @@ -1415,6 +1415,14 @@ mod tests { assert_eq!(parsed.value(), "devlaunch-feature-auth-np10"); assert_eq!(parsed.label(), "devlaunch@feature-auth"); + // The example the README, `docs/workspaces.md`, `docs/workspace-tools.md` + // and `flows::launch`'s doc comment all name. `test_title_claim_agrees.py` + // reads this literal as the answer it holds those four to, so it is here + // rather than there: a separator that moves fails this line first, and the + // prose follows it. Written as its own assertion, and not folded into the + // table below, because that is what the guard's regex anchors on. + assert_eq!(id("blooop", "devlaunch", "main").label(), "devlaunch@main"); + // Stated as a *difference*, not as a reconstruction, because which dash the // `@` replaces cannot be read off the id: a repo slug holds dashes of its // own, so `my-repo@main` and `my@repo-main` are the same id read two ways. diff --git a/rust/devlaunch-core/src/flows/launch.rs b/rust/devlaunch-core/src/flows/launch.rs index b0a4ed79..dc643e3c 100644 --- a/rust/devlaunch-core/src/flows/launch.rs +++ b/rust/devlaunch-core/src/flows/launch.rs @@ -2237,11 +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. +/// The pane name is written here, in `README.md`, `docs/workspaces.md` and +/// `docs/workspace-tools.md`, and `test_title_claim_agrees.py` holds all four to one +/// answer. The answer is not this comment, which nothing checks: it is the `label()` +/// assertion in `rust/devlaunch-core/src/domain/workspace_id.rs`, compared against +/// what `label()` returns every time the suite runs. A separator that moves fails +/// there first, and these four follow it. /// /// **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 diff --git a/test/test_title_claim_agrees.py b/test/test_title_claim_agrees.py index ba967acc..da1149e6 100644 --- a/test/test_title_claim_agrees.py +++ b/test/test_title_claim_agrees.py @@ -1,34 +1,47 @@ -"""The terminal title is stated in three places, so the three statements are diffed. +"""The terminal title is written in four prose copies, held to one checked answer. 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. +is where a reader first meets it, `docs/workspaces.md` tabulates it beside the +other two renderings of the id, `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 is four +hand-maintained copies of one fact, 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: +`docs/workspace-tools.md` 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. -**`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 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 +**None of those four can be the answer, `launch.rs` included.** Diffing prose +against prose only pins that it agrees, which the wrong set of edits satisfies as +easily as the right one: repair the drift by editing a page down to the wrong +string and a prose-only guard blesses it. `launch.rs` looks like it escapes that, +being the module that computes the title, but what this guard can read there is a +`///` comment -- prose that sits beside the code rather than prose the compiler +checks. Move the separator in `WorkspaceId::label`, fix the assertion that pins +it, and every prose copy is stale with `cargo test` green. + +So the answer is read from that assertion instead. `TRUTH_CLAIM` matches the one +line in `a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` that +states the label for this example, which is the only copy that cannot be wrong +without a test failing: it is compared against `label()`'s real output every time +the suite runs. A separator change fails there first and reaches the prose here +second. + +The instrument in each prose copy is the sentence or row it already writes about +this one example, so no page carries a marker for this test's benefit. Anchoring +on the example 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 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. + +What this guard does not claim is to have found every copy. It pins one example's +answer in the four places that answer it; `docs/workspace-tools.md` also shows the +name inside an escape sequence and a `PS1` snippet, and those are not read here. """ from __future__ import annotations @@ -38,48 +51,66 @@ REPO_ROOT = Path(__file__).resolve().parent.parent -# 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", +# The answer, and the one copy of it a wrong string makes `cargo test` fail on. +TRUTH = Path("rust") / "devlaunch-core" / "src" / "domain" / "workspace_id.rs" +TRUTH_CLAIM = re.compile( + r'id\(\s*"blooop",\s*"devlaunch",\s*"main",?\s*\)\s*\.label\(\)\s*,\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*)?`([^`]+)`") +# The claim as each page happens to write it, anchored on the example command so a +# differently-titled example cannot fail this test, and across whatever line break +# each happens to wrap at. +NAMES_THE_PANE = re.compile( + r"`dl blooop/devlaunch` names the pane\s+(?://[/!]?\s*)?`([^`]+)`" +) +TERMINAL_TAB_ROW = re.compile(r"\[terminal tab\]\([^)]*\)\s*\|\s*`([^`]+)`") +PROSE = { + Path("README.md"): NAMES_THE_PANE, + Path("docs") / "workspaces.md": TERMINAL_TAB_ROW, + Path("docs") / "workspace-tools.md": NAMES_THE_PANE, + Path("rust") / "devlaunch-core" / "src" / "flows" / "launch.rs": NAMES_THE_PANE, +} -def _claims(relative: Path) -> list[str]: - return CLAIM.findall((REPO_ROOT / relative).read_text(encoding="utf-8")) +def _read(relative: Path) -> str: + path = REPO_ROOT / relative + assert path.is_file(), ( + f"{relative} is gone, so this guard cannot read the copy it diffs. Move the " + "path with the file, or retire the entry along with the copy" + ) + return path.read_text(encoding="utf-8") -def test_every_source_still_states_the_pane_name(): + +def _computed() -> str: + """The pane name `cargo test` pins for the example every prose copy writes.""" + stated = TRUTH_CLAIM.findall(_read(TRUTH)) + assert len(stated) == 1, ( + f"{TRUTH} states {len(stated)} pane names for `blooop/devlaunch@main` and " + f"this guard reads it as the answer, so it has to state exactly one: " + f"{stated}. The line is the `label()` assertion in " + "`a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was`" + ) + return stated[0] + + +def test_every_prose_copy_still_states_the_pane_name(): """A claim that vanished would make the comparison below vacuously true.""" - for relative in SOURCES: - assert _claims(relative), ( + for relative, claim in PROSE.items(): + assert claim.findall(_read(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" + "Either restore the claim or retire this guard's entry along with the " + "copy it diffs" ) -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 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" - ) +def test_the_prose_agrees_with_the_assertion_that_checks_the_name(): + computed = _computed() - 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" + for relative, claim in PROSE.items(): + stated = sorted(set(claim.findall(_read(relative)))) + assert stated == [computed], ( + f"{relative} says `dl blooop/devlaunch` names the pane {stated}, and " + f"{TRUTH} pins '{computed}'. That assertion is checked against " + "`label()` itself, so the prose is what moves unless `label()` changed" ) From 1f448c42f8dfc5e2727aab113ccc5f17691630db Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 08:52:43 +0000 Subject: [PATCH 06/10] style: ruff-format the guard's regex onto one line The formatter's own output on the previous commit. Kept separate rather than amended so the branch history stays append-only. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- test/test_title_claim_agrees.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/test_title_claim_agrees.py b/test/test_title_claim_agrees.py index da1149e6..4b4f9a9e 100644 --- a/test/test_title_claim_agrees.py +++ b/test/test_title_claim_agrees.py @@ -60,9 +60,7 @@ # The claim as each page happens to write it, anchored on the example command so a # differently-titled example cannot fail this test, and across whatever line break # each happens to wrap at. -NAMES_THE_PANE = re.compile( - r"`dl blooop/devlaunch` names the pane\s+(?://[/!]?\s*)?`([^`]+)`" -) +NAMES_THE_PANE = re.compile(r"`dl blooop/devlaunch` names the pane\s+(?://[/!]?\s*)?`([^`]+)`") TERMINAL_TAB_ROW = re.compile(r"\[terminal tab\]\([^)]*\)\s*\|\s*`([^`]+)`") PROSE = { From 823bcd17b8f352d3431b70c7141a974e4a02dc8f Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 08:58:14 +0000 Subject: [PATCH 07/10] fix: docs/cli.md quoted two refusals nothing diffed against the binary The `--force` section added in ee0119a writes both refusals into a code block: dl --force rm -> Unknown command '--force'. dl --force rm -> Unknown workspace '--force'. Those are the only diagnostics quoted verbatim anywhere in `docs/cli.md`, and this diff introduced both. They are a second hand-maintained copy of strings `render.rs` and `lib.rs` own, which this repository allows only with a test beside them that diffs them against the first, and the same rule this branch exists to apply to the terminal title. The section already cites three tests, but citing is not diffing: every one of them pins the *behaviour*, so all three stay green with the message reworded and the page left quoting the old one. Reword `Unknown command` to `Unknown verb` in the binary and nothing fails. `the_force_placement_section_quotes_the_refusals_it_says_it_does` reads the section back and asserts each quoted refusal is a prefix of what the binary actually prints, alongside the exit code and the clone still being there. A prefix because the block drops the `Use '...'` suggestion deliberately: it is quoting the refusal, not transcribing the session. Confirmed red by making that reword in the doc and green with it restored. It reads the page the way `provision::lending_contract` reads `docs/workspace-tools.md`, matching on the heading rather than on any phrase under it, so the prose stays free to be rewritten. A renamed heading panics with the name it looked for instead of yielding an empty section every assertion passes over. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- docs/cli.md | 5 ++- rust/dl/tests/grammar.rs | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index 439a5f19..30db43d8 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -439,7 +439,10 @@ every other global. Note this is about the literal word `--force`: `--devcontain 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`. +`rust/dl/tests/grammar.rs`. The two refusals quoted above are a second copy of +strings `render.rs` and `lib.rs` own, so +`the_force_placement_section_quotes_the_refusals_it_says_it_does` reads this +section back and diffs them against what the binary prints. ## Remote Control: every `aid` session, on your phone too diff --git a/rust/dl/tests/grammar.rs b/rust/dl/tests/grammar.rs index 0d00b762..eb4196ff 100644 --- a/rust/dl/tests/grammar.rs +++ b/rust/dl/tests/grammar.rs @@ -169,6 +169,88 @@ fn force_after_the_verb_still_deletes() { assert!(!world.clone_is_there(), "the clone was deleted"); } +/// The refusals `docs/cli.md` quotes, held to the ones the binary prints. +/// +/// The "Where `--force` may sit" section writes both into a code block, cut at the +/// end of the first sentence. That is a hand-maintained copy of a string owned by +/// `render.rs` and `lib.rs`, and this repository allows a second copy only with a +/// test beside it that diffs it against the first. The section names the tests that +/// pin the *behaviour*, which is not the same promise: those would still pass with +/// the message reworded and the page left quoting the old one. +/// +/// Matched as a prefix, because the block drops the `Use '...'` suggestion that +/// follows on purpose. It is quoting the refusal, not transcribing the session. +#[test] +fn the_force_placement_section_quotes_the_refusals_it_says_it_does() { + let doc = std::fs::read_to_string(repo_root().join("docs/cli.md")).expect("docs/cli.md"); + let section = force_placement_section(&doc); + + for (spelling, args) in [ + ( + "dl --force rm", + ["blooop/devlaunch@main", "--force", "rm"], + ), + ( + "dl --force rm", + ["--force", "blooop/devlaunch@main", "rm"], + ), + ] { + let quoted = quoted_refusal(§ion, spelling); + let world = World::full(); + let run = world.dl(&args); + + assert_eq!(run.code, Some(1), "stderr: {}", run.err); + assert!( + run.err.starts_with("ed), + "docs/cli.md says `{spelling}` prints {quoted:?}; it prints {:?}", + run.err + ); + assert!( + world.clone_is_there(), + "`{spelling}` must not have deleted the clone" + ); + } +} + +/// The text under the `--force` placement heading, up to the next heading. +/// +/// Matched on the heading rather than on a phrase under it, so the prose stays free +/// to be rewritten while this test keeps pointing at one span. A missing heading +/// says so rather than yielding an empty section that every assertion passes over. +fn force_placement_section(document: &str) -> String { + const HEADING: &str = "### Where `--force` may sit"; + let start = document + .find(HEADING) + .unwrap_or_else(|| panic!("docs/cli.md no longer has a '{HEADING}' section")); + let rest = &document[start + HEADING.len()..]; + let end = rest + .find( + " +## ", + ) + .unwrap_or(rest.len()); + rest[..end].to_string() +} + +/// The refusal `docs/cli.md` writes for one spelling: what follows the `->` arrow, +/// up to the run of spaces before the `(exit 1, ...)` annotation. +fn quoted_refusal(section: &str, spelling: &str) -> String { + let line = section + .lines() + .find(|line| line.starts_with(spelling)) + .unwrap_or_else(|| panic!("docs/cli.md no longer shows `{spelling}`")); + let (_, printed) = line + .split_once("->") + .unwrap_or_else(|| panic!("`{spelling}` in docs/cli.md no longer names what it prints")); + printed + .trim_start() + .split(" ") + .next() + .expect("split always yields one part") + .trim_end() + .to_string() +} + #[test] fn a_path_that_names_no_workspace_is_refused() { let world = World::full(); From 018277d424af961ef04e59b6d7474124dce4eefa Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 10:28:03 +0000 Subject: [PATCH 08/10] Single-source the tab's name instead of guarding four copies of it The branch's answer to "the README and the docs page disagree about the terminal title" was a guard that held the copies to each other. Reviewing it turned up the same defect twice: the copy it called authoritative was a doc comment nothing checks, and the count kept being wrong. A guard whose own correctness argument has needed fixing twice is the wrong instrument for four sentences. So there is one place that writes the tab's spelling now: the renderings table in `docs/workspaces.md`, beside the id and the selector row it is meant to be matched against. That table is the reason the fact wants to be written down at all, and it is the only page that publishes all three side by side. Everything else states the rule and links: - `README.md` says the suffix comes off and the dash before the branch is spelled `@`, and links to the table. It loses the worked example, which is the real cost of this shape and the reason it was worth asking about. - `docs/workspace-tools.md` keeps the whole explanation of *why* the spelling is what it is, and points at the table for the value. It still shows the name inside the escape sequence and the `PS1` line, which are illustrations of the mechanism rather than statements of the answer, and the prose now says so. - `flows/launch.rs` names `docs/workspaces.md` and stops writing the string. A doc comment is exactly the copy that goes stale, which is what the deleted guard got wrong about itself. `test_title_claim_agrees.py` is deleted, 114 lines. The standing rule it was answering only bites on a second hand-maintained copy, and there is no longer one. What survives is the assertion the guard was eventually anchored to. `a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` still pins `("blooop", "devlaunch", "main")` to `devlaunch@main`, which is the cell the table publishes, and `docs/workspaces.md` names it. That is one test checking one published string against what `label()` returns, rather than a regex reading three pages and a doc comment. The `--force` guard added in the previous commit stays. A page quoting a program's diagnostics is a copy no amount of single-sourcing removes. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- README.md | 13 +- docs/workspace-tools.md | 13 +- docs/workspaces.md | 11 +- .../devlaunch-core/src/domain/workspace_id.rs | 10 +- rust/devlaunch-core/src/flows/launch.rs | 14 +-- test/test_title_claim_agrees.py | 114 ------------------ 6 files changed, 24 insertions(+), 151 deletions(-) delete mode 100644 test/test_title_claim_agrees.py diff --git a/README.md b/README.md index aae4707c..01d7f409 100644 --- a/README.md +++ b/README.md @@ -354,14 +354,11 @@ anything to its `devcontainer.json`. terminal beside itself in the same container, and you can attach to it from anywhere. `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` in zellij, tmux, or a plain terminal window: the workspace id read for - 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 terminal named after the workspace.** In zellij, tmux, or a plain terminal window + the pane takes the workspace id read for a person: the hashed suffix off and the dash + before the branch spelled `@`, so a tab and the row `dl --ls` prints still match by + eye. A workspace you name by its id keeps the id. + [The three renderings side by side](docs/workspaces.md#workspace-ids). - **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/workspace-tools.md b/docs/workspace-tools.md index 22ec14a2..58a5da9c 100644 --- a/docs/workspace-tools.md +++ b/docs/workspace-tools.md @@ -508,14 +508,11 @@ the same size of decision. That one installs a package into a container and star session; this one writes an escape sequence and two lines into a profile. **It is the [workspace id](workspaces.md#workspace-ids) read for a person: -`@`, 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. `README.md`, `docs/workspaces.md` and -`rust/devlaunch-core/src/flows/launch.rs` name the pane too, and -`test_title_claim_agrees.py` holds the name all four write against the `label()` -assertion in `rust/devlaunch-core/src/domain/workspace_id.rs`, which `cargo test` -checks against what `label()` returns. +`@`, with the hashed suffix off.** The [renderings +table](workspaces.md#workspace-ids) is where the three are written down side by side +and where the spelling is decided; the escape sequence above and the profile line +below show it in place rather than settle it. One string with two characters changed, +so a tab and a listing row still match by eye. 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/docs/workspaces.md b/docs/workspaces.md index d1a816b6..dceac6fe 100644 --- a/docs/workspaces.md +++ b/docs/workspaces.md @@ -60,11 +60,12 @@ The other two are renderings of that same id, cut to what their surface is for: | The [selector](cli.md#the-selector) | `blooop \| devlaunch \| main` | One row at a time with the width of a terminal, so the owner comes back and the branch is spelled in full, out of the clone's `HEAD` | They are renderings and not separate derivations, which is what keeps them -matchable: the tab is the id with the suffix off and one dash spelled `@`, so -`devlaunch@main` and `devlaunch-main-3j1t` are recognisably the same workspace. -The tab's name in that table is one of four copies, and -`test_title_claim_agrees.py` holds them to the `label()` assertion in -`rust/devlaunch-core/src/domain/workspace_id.rs`. +matchable: the tab is the id with the suffix off and one dash spelled `@`, so the +first two rows are recognisably the same workspace. That table is where this +repository writes the three down; everywhere else links to it rather than copying +it, and `a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` in +`rust/devlaunch-core/src/domain/workspace_id.rs` pins the tab's cell against what +`WorkspaceId::label` returns. Which dash the `@` replaces is not readable off the id, since a repo slug holds dashes of its own, so the tab's name travels with the launch that resolved it rather diff --git a/rust/devlaunch-core/src/domain/workspace_id.rs b/rust/devlaunch-core/src/domain/workspace_id.rs index 7c51a845..00b7cac8 100644 --- a/rust/devlaunch-core/src/domain/workspace_id.rs +++ b/rust/devlaunch-core/src/domain/workspace_id.rs @@ -1415,12 +1415,10 @@ mod tests { assert_eq!(parsed.value(), "devlaunch-feature-auth-np10"); assert_eq!(parsed.label(), "devlaunch@feature-auth"); - // The example the README, `docs/workspaces.md`, `docs/workspace-tools.md` - // and `flows::launch`'s doc comment all name. `test_title_claim_agrees.py` - // reads this literal as the answer it holds those four to, so it is here - // rather than there: a separator that moves fails this line first, and the - // prose follows it. Written as its own assertion, and not folded into the - // table below, because that is what the guard's regex anchors on. + // The example `docs/workspaces.md` publishes in its renderings table, which + // is the only page that writes the tab's spelling out. Pinned here because + // nothing else checks that cell, and a table of three strings a reader is + // invited to match by eye is worth exactly as much as its being right. assert_eq!(id("blooop", "devlaunch", "main").label(), "devlaunch@main"); // Stated as a *difference*, not as a reconstruction, because which dash the diff --git a/rust/devlaunch-core/src/flows/launch.rs b/rust/devlaunch-core/src/flows/launch.rs index dc643e3c..b9779759 100644 --- a/rust/devlaunch-core/src/flows/launch.rs +++ b/rust/devlaunch-core/src/flows/launch.rs @@ -2233,16 +2233,10 @@ pub(crate) fn dotfiles_update( /// The name is the placement's ([`Placement::title`]), and it is the workspace id /// with its two unreadable characteristics taken off: the four-character suffix, /// which carries identity and no meaning, and the dash between the repo and the ref, -/// which is spelled `@`. `dl blooop/devlaunch` names the pane `devlaunch@main` where -/// devpod, the hostname and the `WORKSPACE` column of `dl --ls` all say -/// `devlaunch-main-3j1t`. -/// -/// The pane name is written here, in `README.md`, `docs/workspaces.md` and -/// `docs/workspace-tools.md`, and `test_title_claim_agrees.py` holds all four to one -/// answer. The answer is not this comment, which nothing checks: it is the `label()` -/// assertion in `rust/devlaunch-core/src/domain/workspace_id.rs`, compared against -/// what `label()` returns every time the suite runs. A separator that moves fails -/// there first, and these four follow it. +/// which is spelled `@`. `docs/workspaces.md` tabulates what a tab, a listing row and +/// a selector row read for one workspace, and is the only page that writes them out; +/// this comment deliberately does not, because a comment nothing checks is the copy +/// that goes stale. [`WorkspaceId::label`]'s own tests carry the worked examples. /// /// **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 diff --git a/test/test_title_claim_agrees.py b/test/test_title_claim_agrees.py deleted file mode 100644 index 4b4f9a9e..00000000 --- a/test/test_title_claim_agrees.py +++ /dev/null @@ -1,114 +0,0 @@ -"""The terminal title is written in four prose copies, held to one checked answer. - -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/workspaces.md` tabulates it beside the -other two renderings of the id, `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 is four -hand-maintained copies of one fact, 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 -`docs/workspace-tools.md` 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. - -**None of those four can be the answer, `launch.rs` included.** Diffing prose -against prose only pins that it agrees, which the wrong set of edits satisfies as -easily as the right one: repair the drift by editing a page down to the wrong -string and a prose-only guard blesses it. `launch.rs` looks like it escapes that, -being the module that computes the title, but what this guard can read there is a -`///` comment -- prose that sits beside the code rather than prose the compiler -checks. Move the separator in `WorkspaceId::label`, fix the assertion that pins -it, and every prose copy is stale with `cargo test` green. - -So the answer is read from that assertion instead. `TRUTH_CLAIM` matches the one -line in `a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` that -states the label for this example, which is the only copy that cannot be wrong -without a test failing: it is compared against `label()`'s real output every time -the suite runs. A separator change fails there first and reaches the prose here -second. - -The instrument in each prose copy is the sentence or row it already writes about -this one example, so no page carries a marker for this test's benefit. Anchoring -on the example 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 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. - -What this guard does not claim is to have found every copy. It pins one example's -answer in the four places that answer it; `docs/workspace-tools.md` also shows the -name inside an escape sequence and a `PS1` snippet, and those are not read here. -""" - -from __future__ import annotations - -import re -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parent.parent - -# The answer, and the one copy of it a wrong string makes `cargo test` fail on. -TRUTH = Path("rust") / "devlaunch-core" / "src" / "domain" / "workspace_id.rs" -TRUTH_CLAIM = re.compile( - r'id\(\s*"blooop",\s*"devlaunch",\s*"main",?\s*\)\s*\.label\(\)\s*,\s*"([^"]+)"' -) - -# The claim as each page happens to write it, anchored on the example command so a -# differently-titled example cannot fail this test, and across whatever line break -# each happens to wrap at. -NAMES_THE_PANE = re.compile(r"`dl blooop/devlaunch` names the pane\s+(?://[/!]?\s*)?`([^`]+)`") -TERMINAL_TAB_ROW = re.compile(r"\[terminal tab\]\([^)]*\)\s*\|\s*`([^`]+)`") - -PROSE = { - Path("README.md"): NAMES_THE_PANE, - Path("docs") / "workspaces.md": TERMINAL_TAB_ROW, - Path("docs") / "workspace-tools.md": NAMES_THE_PANE, - Path("rust") / "devlaunch-core" / "src" / "flows" / "launch.rs": NAMES_THE_PANE, -} - - -def _read(relative: Path) -> str: - path = REPO_ROOT / relative - assert path.is_file(), ( - f"{relative} is gone, so this guard cannot read the copy it diffs. Move the " - "path with the file, or retire the entry along with the copy" - ) - return path.read_text(encoding="utf-8") - - -def _computed() -> str: - """The pane name `cargo test` pins for the example every prose copy writes.""" - stated = TRUTH_CLAIM.findall(_read(TRUTH)) - assert len(stated) == 1, ( - f"{TRUTH} states {len(stated)} pane names for `blooop/devlaunch@main` and " - f"this guard reads it as the answer, so it has to state exactly one: " - f"{stated}. The line is the `label()` assertion in " - "`a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was`" - ) - return stated[0] - - -def test_every_prose_copy_still_states_the_pane_name(): - """A claim that vanished would make the comparison below vacuously true.""" - for relative, claim in PROSE.items(): - assert claim.findall(_read(relative)), ( - f"{relative} no longer says what `dl blooop/devlaunch` names the pane. " - "Either restore the claim or retire this guard's entry along with the " - "copy it diffs" - ) - - -def test_the_prose_agrees_with_the_assertion_that_checks_the_name(): - computed = _computed() - - for relative, claim in PROSE.items(): - stated = sorted(set(claim.findall(_read(relative)))) - assert stated == [computed], ( - f"{relative} says `dl blooop/devlaunch` names the pane {stated}, and " - f"{TRUTH} pins '{computed}'. That assertion is checked against " - "`label()` itself, so the prose is what moves unless `label()` changed" - ) From 863439446a5122de4d651a06c1f7c06cdd23260e Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 10:41:58 +0000 Subject: [PATCH 09/10] fix: the --force quote guard passed on any message the binary printed `the_force_placement_section_quotes_the_refusals_it_says_it_does`, added two commits ago, compared the doc's quote to the binary's stderr with `run.err.starts_with("ed)`. `quoted` is a `String` built by splitting the doc line at `->`, so "the refusal the page quotes" and "the page quoted nothing" are the same value, and `starts_with("")` is true of every string. Reachable from an ordinary edit. Reflow the code block so the message wraps below the arrow: dl --force rm -> Unknown command '--force'. (exit 1, nothing deleted) `split_once("->")` returns an empty tail, and the assertion carrying the test's whole claim asserts nothing. I applied exactly that to `docs/cli.md` and the test reported 1 passed. The two remaining assertions in the loop cannot catch it: the exit code is 1 for both spellings by construction and neither line deletes a clone, so they hold no matter what the binary says. From that point the message could be reworded freely while `docs/cli.md` went on citing this test by name as what diffs the quote against what the binary prints. Nothing bounded the prefix below, either: quoting `Unknown` and nothing else passed too. Both close the same way. The quote is now compared for equality against the message's first sentence, which is the span the page actually writes, so a quote that is empty or truncated fails on the same assertion as a quote that is stale. Six mutations now fail where two of them passed before: the empty capture, the truncation to `Unknown`, a reworded quote, a reworded binary message, a renamed spelling and a renamed heading. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- rust/dl/tests/grammar.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/rust/dl/tests/grammar.rs b/rust/dl/tests/grammar.rs index eb4196ff..a17d7607 100644 --- a/rust/dl/tests/grammar.rs +++ b/rust/dl/tests/grammar.rs @@ -178,8 +178,12 @@ fn force_after_the_verb_still_deletes() { /// pin the *behaviour*, which is not the same promise: those would still pass with /// the message reworded and the page left quoting the old one. /// -/// Matched as a prefix, because the block drops the `Use '...'` suggestion that -/// follows on purpose. It is quoting the refusal, not transcribing the session. +/// Compared against the message's first sentence, because the block drops the +/// `Use '...'` suggestion that follows on purpose. Not by prefix, which was the +/// first shape of this test and was worth nothing: every truncation of the message +/// satisfies a prefix test, the empty string included, and an empty quote is +/// precisely what a code block reflowed to wrap after the arrow produces. The guard +/// then stood there passing on any message at all. #[test] fn the_force_placement_section_quotes_the_refusals_it_says_it_does() { let doc = std::fs::read_to_string(repo_root().join("docs/cli.md")).expect("docs/cli.md"); @@ -200,8 +204,9 @@ fn the_force_placement_section_quotes_the_refusals_it_says_it_does() { let run = world.dl(&args); assert_eq!(run.code, Some(1), "stderr: {}", run.err); - assert!( - run.err.starts_with("ed), + assert_eq!( + quoted, + first_sentence(&run.err), "docs/cli.md says `{spelling}` prints {quoted:?}; it prints {:?}", run.err ); @@ -251,6 +256,17 @@ fn quoted_refusal(section: &str, spelling: &str) -> String { .to_string() } +/// The first sentence of a diagnostic, which is the span `docs/cli.md` quotes. +/// +/// The rest of the message is the `Use '...'` suggestion, which the page leaves out +/// because it names a workspace the reader did not type. +fn first_sentence(message: &str) -> String { + match message.split_once(". ") { + Some((head, _)) => format!("{head}."), + None => message.trim_end().to_string(), + } +} + #[test] fn a_path_that_names_no_workspace_is_refused() { let world = World::full(); From 986ff289fa8034a427efc715fbfd8d94de00eddd Mon Sep 17 00:00:00 2001 From: Austin Gregg-Smith Date: Tue, 1 Sep 2026 10:43:23 +0000 Subject: [PATCH 10/10] fix: "the only page that writes the tab's spelling" was not true 018277d deleted the title guard on the grounds that the fact now had one copy, and said so in three places. It had four. `docs/workspace-tools.md` went on stating the answer for a named command twice, outside the two exemptions that commit's own prose enumerated: line 549 a workspace opened from the selector is titled `devlaunch@main`, the same as one opened as `dl blooop/devlaunch@main` line 599 `dl blooop/devlaunch@main` installs `devlaunch@main`, a later `dl devlaunch-main-3j1t` installs the id `rust/dl/src/select.rs` carried a third. Neither doc sentence is showing a mechanism the way the escape sequence and the `PS1` line are; each tells the reader what a specific command produces, which is exactly what the README was doing wrong when 1e2a24b opened this branch. Apply the branch's own test: move the separator in `WorkspaceId::label`, fix the one assertion, and all three sentences are wrong with nothing failing. Deleting the guard while they stood left the fact less protected than the commit claimed, on the page that explains the feature. None of the three needed the literal. The selector sentence is about the title being the *same* as a spec launch, so it now says that and nothing else. The profile sentence is drawing the contrast between the readable name and the id, so it names them rather than spelling one. `select.rs` points at the table. The claims are corrected to what is true rather than widened to fit: the table is where the tab's spelling is *decided*, and no other page states it as the answer to a command. What survives elsewhere is inside an example of the mechanism, which the prose now says and a reader can check: the OSC line, the `PS1` line and the contrast that explains why the character is `@`. Found by the Spec and Defects axes independently, which is the second time on this branch that a commit message's claim about its own guarantee has been the defect rather than the code. Claude-Session: https://claude.ai/code/session_01Lxs53nxfvou4SiPeFx4TZv --- docs/workspace-tools.md | 4 ++-- docs/workspaces.md | 9 ++++++--- rust/devlaunch-core/src/domain/workspace_id.rs | 6 +++--- rust/devlaunch-core/src/flows/launch.rs | 7 ++++--- rust/dl/src/select.rs | 6 +++--- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/workspace-tools.md b/docs/workspace-tools.md index 58a5da9c..95338ac6 100644 --- a/docs/workspace-tools.md +++ b/docs/workspace-tools.md @@ -546,7 +546,7 @@ no arguments is how a workspace is reopened. It hands the launch a workspace id any other, but it had the triple a moment earlier: it read the owner and repo out of the cache layout and the branch out of the clone's `HEAD` to draw the row you picked. That travels with the pick, so a workspace opened from the selector is titled -`devlaunch@main`, the same as one opened as `dl blooop/devlaunch@main`. +exactly as one opened as `dl blooop/devlaunch@main` is. It is checked rather than trusted. `HEAD` is the branch checked out *now*, so a `git switch` inside the container leaves a triple that derives some other workspace, @@ -596,7 +596,7 @@ a hash of its own text, so a second, different name for one workspace does not r the first, it sits after it, and the last one wins. Every launch that resolves a branch derives the same label, and the arms that never had one all use the id, so those agree among themselves. What does not agree is one workspace opened **both** -ways: `dl blooop/devlaunch@main` installs `devlaunch@main`, a later +ways: `dl blooop/devlaunch@main` installs the readable name, a later `dl devlaunch-main-3j1t` installs the id, and the tab reads as the id from then on. It costs one extra line in the profile and a less readable tab, in a case most workspaces never reach. diff --git a/docs/workspaces.md b/docs/workspaces.md index dceac6fe..dacaf6e1 100644 --- a/docs/workspaces.md +++ b/docs/workspaces.md @@ -61,9 +61,12 @@ The other two are renderings of that same id, cut to what their surface is for: They are renderings and not separate derivations, which is what keeps them matchable: the tab is the id with the suffix off and one dash spelled `@`, so the -first two rows are recognisably the same workspace. That table is where this -repository writes the three down; everywhere else links to it rather than copying -it, and `a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` in +first two rows are recognisably the same workspace. This table is where the tab's +spelling is decided, and no other page states it as the answer to a command; where +one appears elsewhere it is inside an example of the mechanism, like the escape +sequence and the profile line in +[workspace-tools.md](workspace-tools.md#naming-the-terminal-after-the-workspace). +`a_label_is_the_id_with_the_suffix_off_and_an_at_where_the_dash_was` in `rust/devlaunch-core/src/domain/workspace_id.rs` pins the tab's cell against what `WorkspaceId::label` returns. diff --git a/rust/devlaunch-core/src/domain/workspace_id.rs b/rust/devlaunch-core/src/domain/workspace_id.rs index 00b7cac8..2ceb2640 100644 --- a/rust/devlaunch-core/src/domain/workspace_id.rs +++ b/rust/devlaunch-core/src/domain/workspace_id.rs @@ -1416,9 +1416,9 @@ mod tests { assert_eq!(parsed.label(), "devlaunch@feature-auth"); // The example `docs/workspaces.md` publishes in its renderings table, which - // is the only page that writes the tab's spelling out. Pinned here because - // nothing else checks that cell, and a table of three strings a reader is - // invited to match by eye is worth exactly as much as its being right. + // is where that spelling is decided. Pinned here because nothing else checks + // the cell, and a table of three strings a reader is invited to match by eye + // is worth exactly as much as its being right. assert_eq!(id("blooop", "devlaunch", "main").label(), "devlaunch@main"); // Stated as a *difference*, not as a reconstruction, because which dash the diff --git a/rust/devlaunch-core/src/flows/launch.rs b/rust/devlaunch-core/src/flows/launch.rs index b9779759..745c5a7e 100644 --- a/rust/devlaunch-core/src/flows/launch.rs +++ b/rust/devlaunch-core/src/flows/launch.rs @@ -2234,9 +2234,10 @@ pub(crate) fn dotfiles_update( /// with its two unreadable characteristics taken off: the four-character suffix, /// which carries identity and no meaning, and the dash between the repo and the ref, /// which is spelled `@`. `docs/workspaces.md` tabulates what a tab, a listing row and -/// a selector row read for one workspace, and is the only page that writes them out; -/// this comment deliberately does not, because a comment nothing checks is the copy -/// that goes stale. [`WorkspaceId::label`]'s own tests carry the worked examples. +/// a selector row read for one workspace, and is where that spelling is decided; this +/// comment deliberately does not write it, because a comment nothing checks is the +/// copy that goes stale. [`WorkspaceId::label`]'s own tests carry the worked +/// examples. /// /// **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 diff --git a/rust/dl/src/select.rs b/rust/dl/src/select.rs index 40f198f7..eeb84507 100644 --- a/rust/dl/src/select.rs +++ b/rust/dl/src/select.rs @@ -133,9 +133,9 @@ pub(crate) struct Offer { /// knows its triple: it read the owner and repo out of the cache layout and the /// branch out of the clone's `HEAD` to draw the row. A launch handed a bare id /// cannot recover any of that, so without this the tab of every workspace - /// opened from the picker reads as the id - /// (`devlaunch-main-3j1t`) where the same workspace opened as - /// `dl blooop/devlaunch@main` reads `devlaunch@main`. + /// opened from the picker reads as the id where the same workspace opened as + /// `dl blooop/devlaunch@main` reads the name for a person that + /// `docs/workspaces.md` tabulates. /// /// `None` where the row is not one of dl's clones, and **also** where the /// recovered triple derives some *other* id: `HEAD` is the branch checked out