claude_runner 1.4.1

CLI for executing Claude Code via builder pattern; YAML schema constants for command registration
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# Test: `ps`

Integration test planning for the `ps` command. See [command/06_ps.md](../../../../docs/cli/command/06_ps.md) for specification.

`ps` lists running Claude Code processes and queued `clr` waiters in two plain-style
tables. The ≥1-session tests spawn a fake `claude` background process using the
`fake_claude_binary_dir` helper and prepend it to PATH. Gate file tests create a
temp dir as `CLR_GATE_DIR` and write a fake JSON state file to exercise the
queued-sessions table path without a live `gate.rs` waiter. 0-session tests pass
an empty temp dir as `CLR_PROC_DIR` so `find_claude_processes()` sees no entries
regardless of live Claude sessions on the host (see `claude_core::process`).

## Test Case Index

| ID | Test Name | Category |
|----|-----------|----------|
| IT-1 | `clr ps` with 0 sessions → exit 0, no-sessions message | No-sessions |
| IT-2 | `clr ps` with ≥1 session → exit 0, plain table (no `` border) | Sessions present |
| IT-3 | `clr --help` lists `ps` | Help listing |
| IT-4 | `clr p` (typo) → exit 1, stderr: Did you mean | Typo guard |
| IT-5 | `clr ps` table contains `PID`, `Elapsed`, `Absolute Path`, `Task` headers | Column presence |
| IT-6 | `clr pss` (typo) → exit 1, stderr: Did you mean | Typo guard |
| IT-7 | `clr ps` PID not in own output | Self-exclusion |
| IT-8 | `clr ps --unknown` → exit 1 | Error handling |
| IT-9 | `$PRO` prefix replaced by `"$PRO"` in Absolute Path column | Path shortening |
| IT-10 | Gate file present → queued table with `PID`, `CWD`, `Waiting` headers | Queued present |
| IT-11 | No gate files → no queued table in output | Queued absent |
| IT-12 | Active table caption contains `Active Sessions` and `running` | Caption presence |
| IT-13 | Orphaned gate file (dead PID) filtered out of queued table | BUG-293 repro |
| IT-14 | `clr ps --help` → exit 0, stdout contains help text | Help flag |
| IT-15 | `clr ps -h` → exit 0, stdout contains help text | Help flag |
| IT-16 | Task column extracts Form A JSONL content for known session | BUG-295/296/297 repro |
| IT-17 | Task column selects last Form A entry, skips Form B tool_result lines | BUG-297 repro |
| IT-18 | `clr ps help` (positional) → exit 0, stdout contains help text | Help flag |
| IT-19 | Task column extracts Form A content for CWD with no underscores (regression) | BUG-295 regression |
| IT-20 | Active sessions ordered by start time (oldest first) | Sort order |
| IT-21 | `clr ps --mode print` shows only print-mode sessions | Mode filter |
| IT-22 | `clr ps --mode interactive` shows only interactive sessions | Mode filter |
| IT-23 | `clr ps --mode bogus` exits 1 with error | Mode validation |
| IT-24 | `clr ps --columns pid,path,task` shows custom columns in order | Column select |
| IT-25 | `clr ps --columns bogus` exits 1 with error listing valid keys | Column validation |
| IT-26 | `clr ps --wide` shows all 11 columns including Mode, Command, Binary | Wide output |
| IT-27 | `clr ps --wide --columns pid,task``--columns` wins | Precedence |
| IT-28 | `CLR_PS_MODE=print` env var fallback filters print sessions | Env var |
| IT-29 | `CLR_PS_COLUMNS=pid,elapsed` env var fallback selects columns | Env var |
| IT-30 | `Flags` column absent when no session has any flag | Flags — column absent |
| IT-31 | 🐳 flag for session cwd outside `$HOME``Flags` column present | Flags — container |
| IT-32 | 🕰 flag when elapsed exceeds `CLR_PS_ANCIENT_SECS` threshold | Flags — ancient |
| IT-33 | 🐘 flag when RAM exceeds `CLR_PS_HIGH_RAM_MB` threshold | Flags — high RAM |
| IT-34 | ⚠ flag for TOCTOU-dead session (started_at is None) | Flags — dead metrics |
| IT-35 | 🖨 flag for print-mode session | Flags — print mode |
| IT-36 | Legend printed below active table when ≥1 flag present | Flags — legend |
| IT-37 | Legend absent when no flags present | Flags — legend absent |
| IT-38 | `CLR_PS_ANCIENT_SECS` / `CLR_PS_HIGH_RAM_MB` override thresholds | Flags — env vars |
| IT-39 | Sleeping process → no ⚡ flag (CPU delta = 0) | Flags — active absent |
| IT-40 | Busy-loop process → ⚡ flag present (CPU delta ≫ 3) | Flags — active present |

## Test Coverage Summary

- No-sessions: 1 test (IT-1)
- Sessions present: 1 test (IT-2)
- Help listing: 1 test (IT-3)
- Typo guard: 2 tests (IT-4, IT-6)
- Column presence: 1 test (IT-5)
- Self-exclusion: 1 test (IT-7)
- Error handling: 1 test (IT-8)
- Path shortening: 1 test (IT-9)
- Queued present: 1 test (IT-10)
- Queued absent: 1 test (IT-11)
- Caption presence: 1 test (IT-12)
- BUG-293 repro: 1 test (IT-13)
- Help flag: 3 tests (IT-14, IT-15, IT-18)
- BUG-295/296/297 repro: 1 test (IT-16)
- BUG-297 repro: 1 test (IT-17)
- BUG-295 regression: 1 test (IT-19)
- Sort order: 1 test (IT-20)
- Mode filter: 2 tests (IT-21, IT-22)
- Mode validation: 1 test (IT-23)
- Column select: 1 test (IT-24)
- Column validation: 1 test (IT-25)
- Wide output: 1 test (IT-26)
- Precedence: 1 test (IT-27)
- Env var: 2 tests (IT-28, IT-29)
- Flags — column absent: 1 test (IT-30)
- Flags — container: 1 test (IT-31)
- Flags — ancient: 1 test (IT-32)
- Flags — high RAM: 1 test (IT-33)
- Flags — dead metrics: 1 test (IT-34)
- Flags — print mode: 1 test (IT-35)
- Flags — legend: 1 test (IT-36)
- Flags — legend absent: 1 test (IT-37)
- Flags — env vars: 1 test (IT-38)
- Flags — active absent: 1 test (IT-39)
- Flags — active present: 1 test (IT-40)

**Total:** 40 tests

---

### IT-1: 0 sessions → no-sessions message

- **Setup:** empty temp dir passed as `CLR_PROC_DIR` so `find_claude_processes()` returns no results regardless of host sessions
- **Command:** `clr ps` (with `CLR_PROC_DIR=<empty_dir>` in env)
- **Expected behavior:** stdout = `No active Claude Code sessions.`; exit 0
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-2: ≥1 sessions → plain-style table

- **Setup:** fake `claude` binary placed in temp dir; PATH prepended; binary sleeps 30s to stay alive during test
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout contains `PID` header; stdout does NOT contain `` (plain style, no borders)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-3: `clr --help` lists `ps`

- **Command:** `clr --help`
- **Expected behavior:** stdout contains `ps`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-4: Typo `clr p` triggers guard

- **Command:** `clr p`
- **Expected behavior:** stderr contains `Did you mean`; exit 1
- **Exit:** 1
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-5: Table headers present

- **Setup:** ≥1 fake `claude` process running
- **Command:** `clr ps`
- **Expected behavior:** stdout contains `PID`, `Elapsed`, `Absolute Path`, and `Task`; exit 0
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-6: Typo `clr pss` triggers guard

- **Command:** `clr pss`
- **Expected behavior:** stderr contains `Did you mean`; exit 1
- **Exit:** 1
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-7: `clr ps` not listed in own output

- **Setup:** ≥1 fake `claude` process running; capture PID of `clr ps`
- **Command:** `clr ps`
- **Expected behavior:** The `clr ps` process PID is not present in stdout; exit 0
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** `find_claude_processes()` excludes the caller's PID; this test verifies end-to-end exclusion

---

### IT-8: Unknown flag → exit 1

- **Command:** `clr ps --unknown`
- **Expected behavior:** stderr contains an error message; exit 1
- **Exit:** 1
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-9: `$PRO` prefix shortened in Absolute Path column

- **Setup:** temp dir created as fake `$PRO`; subdirectory `my/project` created within it; fake `claude` ELF spawned with `current_dir = $PRO/my/project`; `PRO` set to temp dir path when running `clr ps`
- **Command:** `clr ps` (with `PRO=<temp_dir>` in env)
- **Expected behavior:** stdout contains `"$PRO"` (literal); stdout does NOT contain the full temp dir path; exit 0
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-10: Gate file present → queued table with headers

- **Setup:** temp dir used as `CLR_GATE_DIR`; write `{test_process_pid}.json` with valid gate JSON (`cwd`, `since`, `attempt`, `message`). Uses the test process's own PID so the `/proc/{pid}` liveness filter passes (gate files with dead PIDs are filtered out per BUG-293)
- **Command:** `clr ps` (with `CLR_GATE_DIR=<temp_dir>` in env)
- **Expected behavior:** Exit 0; stdout contains `PID`, `CWD`, and `Waiting` column headers (queued table)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** No live `clr` waiter needed — the test writes the state file directly to exercise the read path. The PID must be alive for the liveness filter

---

### IT-11: No gate files → no queued table

- **Setup:** temp dir used as `CLR_GATE_DIR`; directory is empty (no JSON files); separate empty temp dir passed as `CLR_PROC_DIR` for process isolation
- **Command:** `clr ps` (with `CLR_GATE_DIR=<empty_gate_dir>` and `CLR_PROC_DIR=<empty_proc_dir>` in env)
- **Expected behavior:** Exit 0; stdout = `No active Claude Code sessions.`; stdout does NOT contain `Waiting` or `Attempt` headers
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-12: Active table caption contains `Active Sessions` and `running`

- **Setup:** fake `claude` binary placed in temp dir; PATH prepended; binary sleeps 30s
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout contains `Active Sessions` (caption title) and `running` (count suffix)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** The caption rule line (e.g., `─── Active Sessions · 1 running ──────`) appears above the column header row in the rendered table

---

### IT-13: Orphaned gate file filtered out (BUG-293)

- **Setup:** temp dir used as `CLR_GATE_DIR`; write `99999999.json` with valid gate JSON (PID 99999999 guaranteed dead — far above `pid_max`)
- **Command:** `clr ps` (with `CLR_GATE_DIR=<temp_dir>` in env)
- **Expected behavior:** Exit 0; stdout does NOT contain `Queued` (no queued table rendered); stdout does NOT contain `99999999`; the orphan `.json` file is deleted by self-healing cleanup
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-293). Validates both liveness filtering AND self-healing file deletion

---

### IT-14: `clr ps --help` prints help

- **Command:** `clr ps --help`
- **Expected behavior:** Exit 0; stdout contains help text for the `ps` subcommand (e.g. column descriptions or `Active Sessions`)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-294). Mirrors BUG-222 regression pattern for kill/isolated/ask

---

### IT-15: `clr ps -h` prints help

- **Command:** `clr ps -h`
- **Expected behavior:** Exit 0; stdout contains help text for the `ps` subcommand
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-294)

---

### IT-16: Task column extracts Form A JSONL content

- **Setup:** (1) fake `claude` binary placed in temp dir; PATH prepended; binary runs in a known CWD containing underscores (e.g. `$temp/wip_core/proj`). (2) Create synthetic `~/.claude/projects/` under a temp HOME dir: compute encoded path for the fake CWD by replacing both `/` and `_` with `-`; create that directory; write a JSONL file with one Form A user entry: `{"type":"user","message":{"role":"user","content":"fix the auth module"}}`. (3) Run `clr ps` with `HOME=<temp_home>`.
- **Command:** `clr ps` (with `HOME=<temp_home>` in env)
- **Expected behavior:** Exit 0; stdout Task column contains `fix the auth module` (truncated to 35 chars)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-295, BUG-296, BUG-297). Validates path encoding fix (BUG-295), content field fix (BUG-296), and Form A selection (BUG-297) end-to-end

---

### IT-17: Task column selects last Form A over Form B

- **Setup:** Same as IT-16 but the JSONL contains both a Form A entry and one or more Form B tool_result entries appearing after it: Form B line: `{"type":"user","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"tu_abc","content":[{"type":"text","text":"claude command::some_skill"}]}]}}`. The Form A entry contains `"content":"the actual task"`. Form A appears before Form B in the file (simulating normal session order).
- **Command:** `clr ps` (with `HOME=<temp_home>` in env)
- **Expected behavior:** Exit 0; stdout Task column contains `the actual task`; stdout does NOT contain `claude command::some_skill`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-297). Validates that Form B tool_result lines are skipped in favour of the last Form A human-typed entry. Use `std::process::Command` directly with `.env("HOME", &temp_home)``run_clr_ps()` does not accept HOME parameter.

---

### IT-18: `clr ps help` (positional) prints help

- **Command:** `clr ps help`
- **Expected behavior:** Exit 0; stdout contains help text for the `ps` subcommand
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-294). Positional `help` token (no dashes). Mirrors the `dispatch_kill` / `dispatch_ask` pattern.

---

### IT-19: Task column extracts Form A for underscore-free CWD (regression)

- **Setup:** Same as IT-16 but CWD contains no underscores (e.g. `$temp/work/proj`). Encode path with only `/``-` (no underscore replacement needed). Write synthetic Form A JSONL under the correctly-encoded path in temp HOME.
- **Command:** `clr ps` (with `HOME=<temp_home>` in env)
- **Expected behavior:** Exit 0; stdout Task column shows Form A content value
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Regression test — verifies BUG-295 fix (`replace('_', "-")`) does not break paths that originally encoded correctly with only slash replacement. Use `std::process::Command` directly with `.env("HOME", &temp_home)`.

---

### IT-20: Active sessions ordered by start time (oldest first)

- **Setup:** Spawn two fake `claude` binaries sequentially: process A first (older, lower PID), then process B (newer, higher PID). Both use `fake_claude_binary_dir()` helper + `.arg("30")` to stay alive. A brief `sleep(1)` between spawns ensures different `started_at` values in `/proc/{pid}/stat` field 22.
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; row `#1` has a longer `Elapsed` value than row `#2`. Parse the two `Elapsed` column values and assert that the first row's elapsed duration is ≥ the second row's.
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** test_kind: bug_reproducer(BUG-301). In typical Linux PID allocation, older processes have lower PIDs, so this test validates ordering is applied but cannot distinguish age-sort from PID-sort without `/proc` mocking. The behavioral guarantee is: oldest session appears first.

---

### IT-21: `--mode print` shows only print-mode sessions

- **Setup:** Spawn 2 fake `claude` processes: one with `--print` arg, one without. Both use `fake_claude_binary_dir()` + `.arg("30")`
- **Command:** `clr ps --mode print`
- **Expected behavior:** Exit 0; output contains the print-mode process PID; output does NOT contain the interactive process PID
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Mode detection reads NUL-delimited `/proc/{pid}/cmdline``--print` or `-p` → print, else interactive

---

### IT-22: `--mode interactive` shows only interactive sessions

- **Setup:** Same as IT-21
- **Command:** `clr ps --mode interactive`
- **Expected behavior:** Exit 0; output contains the interactive process PID; output does NOT contain the print-mode process PID
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-23: `--mode bogus` → exit 1

- **Command:** `clr ps --mode bogus`
- **Expected behavior:** Exit 1; stderr contains error message listing valid values (`all`, `interactive`, `print`)
- **Exit:** 1
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-24: `--columns pid,path,task` shows custom columns

- **Setup:** ≥1 fake `claude` process running
- **Command:** `clr ps --columns pid,path,task`
- **Expected behavior:** Exit 0; stdout contains `PID`, `Absolute Path`, `Task`; does NOT contain `CPU%`, `RAM`, `State`, `Elapsed`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-25: `--columns bogus` → exit 1

- **Command:** `clr ps --columns bogus`
- **Expected behavior:** Exit 1; stderr contains error listing valid column keys
- **Exit:** 1
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-26: `--wide` shows all 11 columns

- **Setup:** ≥1 fake `claude` process running
- **Command:** `clr ps --wide`
- **Expected behavior:** Exit 0; stdout contains `Command` and `Binary` headers (plus the 9 default headers including `Mode`)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-27: `--columns` overrides `--wide`

- **Setup:** ≥1 fake `claude` process running
- **Command:** `clr ps --wide --columns pid,task`
- **Expected behavior:** Exit 0; stdout contains `PID` and `Task`; does NOT contain `Mode`, `Command`, `Binary`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-28: `CLR_PS_MODE=print` env var fallback

- **Setup:** Spawn 2 fake `claude` processes: one print-mode, one interactive
- **Command:** `clr ps` with `CLR_PS_MODE=print` in env
- **Expected behavior:** Exit 0; output contains only print-mode session PID
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-29: `CLR_PS_COLUMNS=pid,elapsed` env var fallback

- **Setup:** ≥1 fake `claude` process running
- **Command:** `clr ps` with `CLR_PS_COLUMNS=pid,elapsed` in env
- **Expected behavior:** Exit 0; stdout contains `PID` and `Elapsed`; does NOT contain `CPU%`, `RAM`, `Task`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-30: `Flags` column absent when no session has any flag

- **Setup:** Fake `claude` ELF spawned in a subdirectory of `$HOME` (inside HOME, not ancient, not high-RAM, state not R, interactive mode, metrics readable); `CLR_PS_ANCIENT_SECS=999999` (prevent ancient false positive); `CLR_PS_HIGH_RAM_MB=999999` (prevent high-RAM false positive)
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout does NOT contain `Flags` header; all rows have no flag symbols
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-31: 🐳 flag for session cwd outside `$HOME`

- **Setup:** Fake `claude` ELF spawned with `current_dir` set to `/tmp/container_work` (outside `$HOME`); PATH prepended; session stays alive 30s
- **Command:** `clr ps` with `HOME=<temp_home>` (ensure /tmp does not start with temp_home)
- **Expected behavior:** Exit 0; stdout contains `Flags` header; stdout contains `🐳`; legend below active table lists `🐳`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-32: 🕰 flag when elapsed exceeds `CLR_PS_ANCIENT_SECS` threshold

- **Setup:** Fake `claude` ELF running (any cwd is fine)
- **Command:** `clr ps` with `CLR_PS_ANCIENT_SECS=0` (every session is "ancient" — 0 threshold fires immediately)
- **Expected behavior:** Exit 0; stdout contains `🕰`; legend lists `🕰`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Setting threshold to 0 ensures elapsed_secs > 0 triggers the flag without needing to wait 8 h in tests

---

### IT-33: 🐘 flag when RAM exceeds `CLR_PS_HIGH_RAM_MB` threshold

- **Setup:** Fake `claude` ELF running; `CLR_PS_HIGH_RAM_MB=0` (zero threshold — any non-zero RSS triggers the flag)
- **Command:** `clr ps` with `CLR_PS_HIGH_RAM_MB=0` in env
- **Expected behavior:** Exit 0; stdout contains `🐘`; legend lists `🐘`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Setting threshold to 0 ensures any running process's RSS (always > 0 MB) triggers the flag

---

### IT-34: ⚠ flag for TOCTOU-dead session (started_at is None)

- **Setup:** Spawn a fake `claude` process that exits immediately after being found by `find_claude_processes()`; craft a test using `CLR_PROC_DIR` to inject a fake proc entry pointing to a dead PID — OR use a temp `/proc`-like dir with an entry missing `stat` file
- **Command:** `clr ps` (instrumented so find returns a PID with no readable `/proc/{pid}/stat`)
- **Expected behavior:** Exit 0; stdout contains ``; legend lists ``
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** This is the same TOCTOU scenario documented in the `06_ps.md` Notes section. Implementation may use `CLR_PROC_DIR` to inject a proc dir with cmdline present but stat absent

---

### IT-35: 🖨 flag for print-mode session

- **Setup:** Spawn fake `claude` ELF with `--print` arg; `CLR_PS_ANCIENT_SECS=999999`; `CLR_PS_HIGH_RAM_MB=999999`; cwd inside $HOME
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; the print-mode session row contains `🖨`; legend lists `🖨`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-36: Legend printed below active table when ≥1 flag present

- **Setup:** Fake `claude` ELF spawned in `/tmp/no_home_dir` (triggers 🐳); `HOME=<temp_home>` so `/tmp` is outside HOME
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout contains `Active Sessions` (active table); stdout contains a single legend line AFTER the active table; legend line contains `🐳` and `Container`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-37: Legend absent when no flags present

- **Setup:** Same as IT-30 (no flags firing); `CLR_PS_ANCIENT_SECS=999999`; `CLR_PS_HIGH_RAM_MB=999999`
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout does NOT contain a legend line (no `👈`, `🖨`, ``, `🕰`, `🐘`, ``, `🐳` symbols in output)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md

---

### IT-38: `CLR_PS_ANCIENT_SECS` / `CLR_PS_HIGH_RAM_MB` override thresholds

- **Setup:** Fake `claude` ELF running; set `CLR_PS_ANCIENT_SECS=999999` and `CLR_PS_HIGH_RAM_MB=999999` (impossibly high — no session can trigger these flags)
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout does NOT contain `🕰` or `🐘`; if no other flags fire, `Flags` column absent
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Separate sub-cases: (a) `CLR_PS_ANCIENT_SECS=0` → 🕰 fires (IT-32); (b) `CLR_PS_HIGH_RAM_MB=0` → 🐘 fires (IT-33); (c) both set to 999999 → neither fires (this test)

---

### IT-39: Sleeping process → no ⚡ flag (CPU delta = 0)

- **Setup:** Fake `claude` process spawned via `sleep 300` (zero CPU consumption); `CLR_PS_ANCIENT_SECS=999999`; `CLR_PS_HIGH_RAM_MB=999999`; cwd inside `$HOME`
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout does NOT contain ``; `Flags` column absent (no other flags fire)
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Validates the negative path: sleeping process accumulates 0 CPU ticks in the 1-second sample window → delta = 0 < 3 → no ⚡. **Host caveat:** on the host with any CPU-active live sessions, their ⚡ makes `!stdout.contains("⚡")` fail falsely; this test is reliable only in container (0 live sessions).

---

### IT-40: Busy-loop process → ⚡ flag present (CPU delta ≫ 3)

- **Setup:** Fake `claude` process spawned via `/bin/sh -c 'while :; do :; done'` (100% CPU); `CLR_PS_ANCIENT_SECS=999999`; `CLR_PS_HIGH_RAM_MB=999999`
- **Command:** `clr ps`
- **Expected behavior:** Exit 0; stdout contains ``; legend contains `Active`
- **Exit:** 0
- **Source:** [command/06_ps.md]../../../../docs/cli/command/06_ps.md
- **Note:** Busy-loop consumes ~100 ticks/s → delta ≈ 100 in 1 s window, well above threshold of 3. Also validates the renamed legend text ("Active" not "On CPU")