Skip to content

Commit ac2ebdb

Browse files
committed
Add CLI improvement planning docs
1 parent ab5aff2 commit ac2ebdb

2 files changed

Lines changed: 490 additions & 0 deletions

File tree

plan.md

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
# CBRAIN CLI Improvement Backlog
2+
3+
This is the full technical backlog. It is intentionally broader than one summer project.
4+
5+
For the student-facing 3-month scope, use `summer-student-scope.md`. That document explains which parts of this backlog are required, which are optional, and which are out of scope.
6+
7+
## Priority Model
8+
9+
- **Phase 1:** user-visible correctness bugs.
10+
- **Phase 2:** low-hanging guardrails that make future work safer.
11+
- **Phase 3:** output and UX consistency.
12+
- **Phase 4:** incremental architecture cleanup.
13+
- **Phase 5:** documentation and compatibility.
14+
15+
# Phase 1: Correctness Fixes
16+
17+
## 1. Fix `task list bourreau-id <id>` filtering
18+
19+
**Problem:** The parser accepts `bourreau-id`, but the data code checks `bourreau_id`, so the filter is ignored.
20+
21+
**Do:**
22+
- Keep the public CLI spelling as `bourreau-id`.
23+
- Use argparse `dest=` or normalization so internal code sees `bourreau_id`.
24+
- Add coverage proving the generated request includes `bourreau_id=...`.
25+
26+
**Verify:** `cbrain task list bourreau-id 3` sends the expected filter.
27+
28+
## 2. Stop execution after invalid pagination
29+
30+
**Problem:** `pagination()` returns `None` for invalid input, but callers continue. Some crash; others make the wrong request.
31+
32+
**Do:**
33+
- Give pagination a clear contract: raise a validation error or return a handled sentinel.
34+
- Apply the same behavior to all paginated list commands.
35+
- Ensure invalid pagination makes no network request.
36+
37+
**Verify:** `--per-page 1` and `--page 0` return a clear error and non-zero exit.
38+
39+
## 3. Fix `tool show <id>` lookup
40+
41+
**Problem:** `tool show` fetches only the first `/tools` page and filters client-side, so valid tools outside page 1 can appear missing.
42+
43+
**Do:**
44+
- Prefer a direct `/tools/{id}` request if available.
45+
- Otherwise fetch deliberately and document the limitation.
46+
- Split `tool list` and `tool show` data functions.
47+
48+
**Verify:** A tool outside the first default page can be shown correctly.
49+
50+
## 4. Allow `logout` to clean up invalid credentials
51+
52+
**Problem:** `main()` blocks `logout` behind `is_authenticated()`, making cleanup unreachable for malformed credentials.
53+
54+
**Do:**
55+
- Dispatch `logout` without requiring authentication.
56+
- Make local credential removal robust when the file is missing or malformed.
57+
58+
**Verify:** A malformed credentials file is removed by `cbrain logout`.
59+
60+
## 5. Remove import-time config directory creation
61+
62+
**Problem:** `config.py` creates `~/.config/cbrain` at import time, so read-only commands can fail before parsing.
63+
64+
**Do:**
65+
- Keep credential paths as constants.
66+
- Create directories only when writing credentials.
67+
- Ensure reads tolerate missing files/directories.
68+
69+
**Verify:** `cbrain --help` and `cbrain version` do not write to `HOME`.
70+
71+
## 6. Print empty list results consistently
72+
73+
**Problem:** Handlers using `if result:` suppress valid empty lists.
74+
75+
**Do:**
76+
- Use `is not None` for list command results.
77+
- Let formatters handle empty-list messages.
78+
- Ensure JSON mode emits `[]`.
79+
80+
**Verify:** Empty API list responses produce useful normal output and valid JSON/JSONL behavior.
81+
82+
## 7. Implement or remove `project unswitch`
83+
84+
**Problem:** `project unswitch` is advertised but only prints a not-implemented message.
85+
86+
**Do:**
87+
- Prefer implementing it by clearing `current_group_id` and `current_group_name`.
88+
- Make it idempotent when no project is set.
89+
- Respect `--json` and `--jsonl`.
90+
91+
**Verify:** `project switch`, `project unswitch`, and `project show` behave coherently.
92+
93+
# Phase 2: Low-Hanging Guardrails
94+
95+
## 8. Enforce Ruff linting and formatting
96+
97+
**Problem:** Ruff is configured but not clearly enforced as part of routine development.
98+
99+
**Do:**
100+
- Standardize `ruff check .` and `ruff format .`.
101+
- Add pre-commit and/or CI enforcement.
102+
- Document the commands.
103+
104+
**Verify:** CI or local checks fail on lint/format drift.
105+
106+
## 9. Add unit tests beside capture tests
107+
108+
**Problem:** Capture tests protect output but do not isolate parsing, validation, request construction, or handler contracts.
109+
110+
**Do:**
111+
- Add a minimal unit test harness, preferably `pytest`.
112+
- Cover Phase 1 regressions first.
113+
- Mock `urllib.request.urlopen` for request tests.
114+
115+
**Verify:** `python -m pytest` runs without a live CBRAIN server.
116+
117+
## 10. Add regression tests for falsey valid results
118+
119+
**Problem:** Empty lists and possibly empty dictionaries can be valid responses.
120+
121+
**Do:**
122+
- Test empty list handling for each list handler.
123+
- Decide explicitly whether empty dictionaries are valid for show commands.
124+
125+
**Verify:** Empty successful responses do not disappear.
126+
127+
## 11. Add HTTP timeouts
128+
129+
**Problem:** `urlopen()` calls have no timeout and can hang indefinitely.
130+
131+
**Do:**
132+
- Add a default timeout, for example 30 seconds.
133+
- Make timeout errors use shared error handling.
134+
- Optionally allow environment or config override later.
135+
136+
**Verify:** Mocked timeout errors return clear messages and non-zero status.
137+
138+
## 12. Protect credentials file permissions
139+
140+
**Problem:** API tokens are stored as plain JSON without explicit permission handling.
141+
142+
**Do:**
143+
- Create credential files with user-private permissions where supported.
144+
- Preserve restrictive permissions when updating.
145+
- Handle non-POSIX platforms gracefully.
146+
147+
**Verify:** On POSIX, credentials are written with private permissions.
148+
149+
## 13. Use one source of truth for versioning
150+
151+
**Problem:** `version_info()` hardcodes `1.0`, while repository tags may differ.
152+
153+
**Do:**
154+
- Put version metadata in one place.
155+
- Make `cbrain version` read package metadata when installed.
156+
- Provide a source-tree fallback.
157+
158+
**Verify:** Reported version matches package metadata and release tags.
159+
160+
## 14. Add a contributor checklist
161+
162+
**Problem:** Review expectations are implicit.
163+
164+
**Do:**
165+
- Add a short checklist to `README.md` or `CONTRIBUTING.md`.
166+
- Include Ruff, tests, capture tests, output modes, data-layer printing, and credential safety.
167+
168+
**Verify:** Contributors can find the checklist before opening PRs.
169+
170+
## 15. Treat linting as a baseline
171+
172+
**Problem:** A clean Ruff run does not prove command behavior, output modes, or architecture are correct.
173+
174+
**Do:**
175+
- Document that linting complements tests and review.
176+
- Keep architecture-sensitive checks in the review checklist.
177+
178+
**Verify:** CI includes both lint/format checks and tests.
179+
180+
# Phase 3: Output and UX Consistency
181+
182+
## 16. Audit global `--json` and `--jsonl` behavior
183+
184+
**Problem:** Several commands ignore global output flags or always print JSON/plain text.
185+
186+
**Known examples:** `version`, `tool-config boutiques-descriptor`, `dataprovider is-alive`, `dataprovider delete-unregistered-files`, `project switch all`, file operations, and tag operations.
187+
188+
**Do:**
189+
- Create a command/output matrix.
190+
- Convert operation commands to structured result dictionaries.
191+
- Route machine-readable output through `json_printer()` and `jsonl_printer()`.
192+
193+
**Verify:** Representative commands work in normal, JSON, and JSONL modes.
194+
195+
## 17. Make not-implemented behavior structured
196+
197+
**Problem:** Not-implemented paths print prose and return inconsistently.
198+
199+
**Known examples:** `project switch all`, `project unswitch`, `task operation`.
200+
201+
**Do:**
202+
- Decide whether each command should exist.
203+
- If it remains, return non-zero and emit structured JSON/JSONL errors.
204+
205+
**Verify:** Not-implemented commands behave consistently in all output modes.
206+
207+
## 18. Stabilize command vocabulary
208+
209+
**Problem:** Public terms and option names are inconsistent: `dataprovider`, `remote-resource`, `bourreau-id`, `dp-id`, `data-provider`, etc.
210+
211+
**Do:**
212+
- Decide canonical public terms.
213+
- Keep backward-compatible aliases where practical.
214+
- Improve help text for CBRAIN-specific terms.
215+
216+
**Verify:** `cbrain --help` and subcommand help read consistently.
217+
218+
## 19. Clarify destructive command safety
219+
220+
**Problem:** Delete and cleanup commands need clear confirmation, force, partial success, and script behavior.
221+
222+
**Do:**
223+
- Decide which destructive commands require confirmation.
224+
- Add `--yes` or `--force` where appropriate.
225+
- Return structured operation results for scripts.
226+
227+
**Verify:** Destructive commands behave predictably in interactive and non-interactive contexts.
228+
229+
## 20. Add coherent verbose/debug mode
230+
231+
**Problem:** Debug behavior is ad hoc and mostly tied to `whoami --version`.
232+
233+
**Do:**
234+
- Add a global `--verbose` or `--debug`.
235+
- Print sanitized method/path/status diagnostics.
236+
- Never print raw tokens or passwords.
237+
238+
**Verify:** Debug output is useful and credential-safe.
239+
240+
# Phase 4: Incremental Architecture Cleanup
241+
242+
## 21. Define command return contracts
243+
244+
**Problem:** Functions return mixed values: `None`, `1`, lists, dicts, and tuples.
245+
246+
**Do:**
247+
- Data functions return domain data or raise typed errors.
248+
- Handlers orchestrate formatting and return exit codes.
249+
- Formatters only print.
250+
- Apply to one command family first.
251+
252+
**Verify:** Handler tests cover success, empty data, validation errors, and API errors.
253+
254+
## 22. Add typed CLI exceptions
255+
256+
**Problem:** Broad exception handling hides intent and makes failures inconsistent.
257+
258+
**Do:**
259+
- Add exceptions such as `CliValidationError`, `CliApiError`, and `CliResponseError`.
260+
- Update shared error handling to treat known errors clearly.
261+
262+
**Verify:** Expected failures get stable messages; unexpected failures still return non-zero.
263+
264+
## 23. Move printing out of data modules
265+
266+
**Problem:** Data modules mix API work, validation, and presentation by printing directly.
267+
268+
**Do:**
269+
- Replace direct data-layer `print()` calls with typed errors or structured results.
270+
- Keep user-facing output in handlers/formatters.
271+
272+
**Verify:** `cbrain_cli/data` has no new direct user-facing output.
273+
274+
## 24. Remove ad hoc background activity error handling
275+
276+
**Problem:** `list_background_activities()` catches all exceptions and bypasses shared error handling.
277+
278+
**Do:**
279+
- Remove broad `except Exception`.
280+
- Let shared handling process HTTP, URL, JSON, and unexpected errors.
281+
282+
**Verify:** Background activity failures match other command error styles.
283+
284+
## 25. Normalize CLI argument names before data-layer use
285+
286+
**Problem:** Raw public CLI spellings leak into data modules, causing bugs like `bourreau-id` vs `bourreau_id`.
287+
288+
**Do:**
289+
- Use argparse `dest=` for normalized snake_case names.
290+
- Translate API-specific field names at the API boundary.
291+
292+
**Verify:** Parsed args and generated API keys are both tested.
293+
294+
## 26. Split parser construction from execution
295+
296+
**Problem:** `main()` builds the parser and executes commands, making parser behavior harder to test.
297+
298+
**Do:**
299+
- Extract `build_parser()`.
300+
- Keep `main(argv=None)` for parse/auth/dispatch.
301+
302+
**Verify:** Parser tests can run without API calls.
303+
304+
## 27. Introduce a small CBRAIN API client
305+
306+
**Problem:** Data modules duplicate request creation, auth headers, JSON decoding, and error handling.
307+
308+
**Do:**
309+
- Add a lightweight standard-library `CbrainClient`.
310+
- Store base URL, token, user ID, and timeout on the client.
311+
- Migrate one command family first.
312+
313+
**Verify:** Client tests mock `urlopen`; migrated commands still pass capture tests.
314+
315+
## 28. Centralize HTTP request construction
316+
317+
**Problem:** Request construction is duplicated and inconsistent.
318+
319+
**Do:**
320+
- Centralize `get/post/put/delete`, URL joining, auth headers, JSON parsing, and HTTP error wrapping.
321+
- If `CbrainClient` exists, put this logic there.
322+
323+
**Verify:** URL generation and JSON handling are unit tested.
324+
325+
## 29. Load authentication state at command execution time
326+
327+
**Problem:** Credentials are read into module globals at import time and can become stale.
328+
329+
**Do:**
330+
- Add `load_credentials()`.
331+
- Stop importing static `api_token`, `cbrain_url`, and `user_id` into data modules.
332+
- Prefer passing credentials through the API client.
333+
334+
**Verify:** Login/logout/whoami behavior uses current credential state in one process.
335+
336+
# Phase 5: Documentation and Compatibility
337+
338+
## 30. Define supported CBRAIN API compatibility
339+
340+
**Problem:** Endpoint and response assumptions are spread across the codebase.
341+
342+
**Do:**
343+
- Document the supported/tested CBRAIN API or server version.
344+
- Centralize endpoint paths where practical.
345+
- Add tests for representative response shapes.
346+
347+
**Verify:** README states the compatibility target.
348+
349+
## 31. Keep capture tests, but add faster isolated tests
350+
351+
**Problem:** Capture tests are valuable but fragile as the only safety net.
352+
353+
**Do:**
354+
- Keep capture tests for end-to-end output.
355+
- Use unit tests for parsing, validation, request construction, and formatter behavior.
356+
- Run unit tests on every CI job.
357+
358+
**Verify:** Request bugs fail unit tests; output regressions fail capture tests.
359+
360+
## 32. Document internal boundaries
361+
362+
**Problem:** The intended layering is implicit.
363+
364+
**Do:**
365+
- Add a short architecture section:
366+
- `main.py`: parser and dispatch.
367+
- `handlers.py`: orchestration and exit codes.
368+
- `data/`: API calls and domain data.
369+
- `formatter/`: user-facing output.
370+
- `cli_utils.py` or `api.py`: shared helpers and errors.
371+
- State that data modules should not print.
372+
373+
**Verify:** Contributor docs match the architecture used by migrated command families.

0 commit comments

Comments
 (0)