fix(install): source dtach-router at login instead of executing it

Executing dtach-router broke its return-based interactive guard and
errored on /dev/tty in non-interactive login shells (bash -lc, cron,
scp). It is now sourced via a guarded, idempotent ~/.profile block
(case $- in *i*) ... . dtach-router) installed by wire_dtach_profile(),
which migrates the old execute-based block. Also adds cc (create) and
d (re-summon) aliases to bashrc-linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CN1KSmsuLG6TxSeN5m8xvM
This commit is contained in:
Bastien Chanot
2026-06-24 18:00:52 +02:00
co-authored by Claude Opus 4.8
parent c0fddc6f94
commit dafe9ea185
6 changed files with 72 additions and 13 deletions
+11
View File
@@ -40,3 +40,14 @@ deployed via `sudo install -D -m 0644` to `/etc/profile.d/` from `install_disk_w
the apt-get Linux block (see LRN-005). Alt rejected: per-user append to `~/.bashrc` — wanted the warn
for EVERY login account on the box, not just the installing user, so system-wide profile.d won. Known
limit: login-shell scope only (non-login terminals miss it). Status: done.
## BDR-007 — dtach resume menu wired login-scope via guarded SOURCE in ~/.profile
2026-06-24. Wired dtach session-resume into `~/.profile` (login scope = once per SSH) as a guarded SOURCE
`case $- in *i*) [ -x ~/.local/bin/dtach-router ] && . … ;;`, NOT `~/.bashrc` (every interactive shell →
menu pops on each tab/subshell). Matches "à la connexion SSH" intent. install.sh `wire_dtach_profile()`
idempotent: awk strips prior block (marker-delimited managed block `# >>> claude-dtach >>>` + legacy
`DT=$(dt ls)…fi` execute block) then re-appends marker block. cc/d aliases live in bashrc-linux (sourced by
.profile BEFORE the router runs → available). Alts rejected: (a) source from `.bashrc` (router's own header
suggests it) — fires too often for login-only intent; (b) keep execute + string-parse — broke the return-based
guard (LRN-006) + fragile parse. Supersedes the old execute+string-parse block. Status: done in repo; live
~/.profile re-migrated this session.
+9
View File
@@ -32,3 +32,12 @@ Added etc/profile.d/disk-usage-warning.sh (POSIX sh, warns bold red when / or /h
install_disk_warning() in install.sh: sudo install -D -m 0644 → /etc/profile.d, gated in apt block
(Linux-only: df --output=pcent GNU-only + /etc/profile.d Debian convention). shellcheck + sh -n CLEAN,
both code paths runtime-verified. README + CLAUDE.md synced. Not committed (master, user to confirm).
## 2026-06-24 — dtach login wiring fix (source not execute) + cc/d aliases
Old ~/.profile block EXECUTED dtach-router + parsed "Aucune session dtach." → broken: executing breaks
the script's return-based interactive guard → falls through → fzf/`dt at >/dev/tty` errors `/dev/tty: No
such device` in every non-interactive login shell (repro'd live on each Bash init). Replaced with guarded
SOURCE `case $- in *i*) ... . dtach-router` via idempotent wire_dtach_profile() (awk strips legacy +
marker block, re-appends marker block). Added cc (create) / d (re-summon) aliases to bashrc-linux.
shellcheck + bash -n CLEAN; migration simulated on real .profile copy. LRN-006 + BDR-007. README synced.
Not committed; live ~/.profile not yet re-migrated.
+11
View File
@@ -36,3 +36,14 @@ absent on macOS BSD df. Any install step deploying such a snippet system-wide mu
`command -v apt-get` (Linux) block, never the OS-agnostic path. Deploy idempotently with
`sudo install -D -m 0644 src /etc/profile.d/x.sh` (-D makes the dir, overwrite = re-runnable). Caveat:
`/etc/profile.d/*.sh` runs for LOGIN shells only — non-login terminals need `/etc/bash.bashrc` instead.
## LRN-006 — Login-resume scripts must be SOURCED, not executed
2026-06-24. `dtach-router` (any login script that hands control back via `return` + attaches to host TTY)
must be SOURCED, never run as a command. Executed: its guard `case $- in *i*) ;; *) return 0 2>/dev/null ;;`
can't `return` from a non-sourced script → error swallowed by `2>/dev/null` → falls THROUGH the guard →
runs fzf + `dt at … >/dev/tty` → `/dev/tty: No such device or address` in EVERY non-interactive login shell
(`bash -lc`, cron, scp, tool sandbox). Repro'd live (fired on each Bash init). Fix in `~/.profile`:
`case $- in *i*) [ -x router ] && . router ;; esac`. Also: don't re-guard by parsing decorative output
(`[ "$(dt ls)" != "Aucune session dtach." ]`) — fragile (couples to exact string) AND redundant
(`dtach-router` already returns on empty `dt --raw`). Let the script self-guard. Bonus gotcha: `~/.profile`
is NOT read by bash if `~/.bash_profile` or `~/.bash_login` exists.