Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions docs/cli-navigation.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
# tracebloc CLI — navigation map

**The single source of truth for how a user moves through the CLI.** Every command, decision point, and place a user can end up. Diagrams are [Mermaid](https://mermaid.js.org) — they render natively on GitHub and in the docs, and this file is version-controlled, so **edit this file + open a PR to change the map** (that PR is where we discuss flow changes).

> **Basis:** `develop` @ `27c5392`. Two things are marked distinctly:
> - **`resources show`** has shipped to `develop` (#237) → drawn solid; **`resources set`** (#241) is still on a feature branch → drawn **dashed / "not shipped"**.
> - The **status-aware home screen** (greeting + sign-in + environment) is a *proposed* redesign (see `home-screen-spec.md`); today's home screen is **stateless** (same for everyone). Proposed bits are dashed.

**How to read it:** `{diamond}` = decision · `[box]` = step · `([rounded])` = where you end up · green = exit 0 · red = non-zero exit · grey dashed = hidden (installer/back-compat) · orange dashed = proposed / not-yet-shipped.

---

## 1. Top-level map — what's reachable from `tracebloc` / `tb`

```mermaid
flowchart TD
H0["tracebloc / tb — home screen"]

H0 --> ACCT["Account (needs sign-in)"]
H0 --> ENVC["Your secure environment & data (needs a reachable client)"]
H0 --> OFF(["delete — offboard this machine"])

ACCT --> login["login"]
ACCT --> logout["logout"]
ACCT --> authst["auth status"]
ACCT --> clis["client status"]
ACCT -.-> clcreate["client create"]:::hidden
ACCT -.-> cllist["client list"]:::hidden

ENVC --> di["data ingest"]
ENVC --> dl["data list"]
ENVC --> dd["data delete"]
ENVC --> dv["data validate (local only, no cluster)"]
ENVC --> ci["cluster info"]
ENVC --> doc["doctor (a.k.a. cluster doctor today)"]
ENVC --> res["resources show"]
ENVC -.-> resset["resources set"]:::proposed

classDef hidden fill:#eee,stroke:#999,stroke-dasharray:3 3,color:#666;
classDef proposed fill:#fff5e6,stroke:#e0a24a,stroke-dasharray:5 5;
```

`tb` is a convenience alias for `tracebloc` (installer-placed symlink; identical behavior). Aliases kept one deprecation cycle: `data`↔`dataset`, `data ingest`↔`push`, `data delete`↔`rm`. Hidden nodes are fully functional but off the everyday surface (installer / back-compat).

---

## 2. The two gate chains — the key structural fact

Commands split into **two independent families** that authenticate differently. Account commands need you signed in (a user token). Data / environment commands are **auth-free w.r.t. your login** — they reach the cluster via kubeconfig RBAC and mint an in-cluster token. Only `client create` and `delete` cross both.

```mermaid
flowchart TD
subgraph A["Chain A — Account (your user token)"]
a0["login · logout · auth status · client * · delete"] --> a1{"signed in?"}
a1 -->|no| aL(["exit 1 → run: login"]):::fail
a1 -->|yes| a2{"token valid? (WhoAmI)"}
a2 -->|"401 / 403"| aL
a2 -->|"426"| aU(["exit 1 → upgrade the CLI"]):::fail
a2 -->|ok| aOK(["proceed"]):::ok
end

subgraph B["Chain B — Data & environment (kubeconfig + in-cluster token; NO sign-in)"]
b0["data ingest / list / delete · cluster info · resources"] --> b1{"kubeconfig reachable?"}
b1 -->|no| bK(["exit 3 → fix kubeconfig · doctor"]):::fail
b1 -->|yes| b2{"client / release found?"}
b2 -->|no| bR(["exit 4 → run the installer, or --namespace"]):::fail
b2 -->|yes| b3{"shared storage bound? (ingest / delete)"}
b3 -->|no| bR
b3 -->|yes| b4{"in-cluster token mintable?"}
b4 -->|no| bT(["exit 5 → grant RBAC"]):::fail
b4 -->|yes| bOK(["proceed"]):::ok
end

classDef fail fill:#fdecec,stroke:#d9534f;
classDef ok fill:#eafaea,stroke:#5cb85c;
```

---

## 3. `data ingest` — the core flow

The wizard fills only what flags left empty; off a TTY (or `--no-input` / `--output-json`) every gap becomes a hard error instead of a prompt.

```mermaid
flowchart TD
di["data ingest &lt;path&gt;"] --> wiz{"interactive TTY<br/>and not --no-input?"}
wiz -->|yes| W["guided wizard — fills only what's missing:<br/>train/test → name → path → task → label → extras → review → confirm"]
wiz -->|no| FLAGS["flag-only path"]
W -->|"cancel / Ctrl-C"| cwiz(["exit 0 — nothing ingested"]):::ok
W --> LOCAL
FLAGS --> LOCAL

LOCAL{"local checks — path exists · name valid · task supported ·<br/>flags relevant · layout walk · schema · content preflight"}
LOCAL -->|"bad input"| e2(["exit 2"]):::fail
LOCAL -->|"unreadable / parse"| e3(["exit 3"]):::fail
LOCAL -->|ok| CONN["Connecting to your workspace… (Chain B: kubeconfig→3, no client/storage→4)"]

CONN --> DRY{"--dry-run?"}
DRY -->|yes| e0dry(["exit 0 — checks out, nothing created"]):::ok
DRY -->|no| DEST{"destination already exists?"}
DEST -->|"new"| STAGE
DEST -->|"exists · interactive"| REPL{"replace it?"}
REPL -->|no| e0a(["exit 0 — left as-is"]):::ok
REPL -->|yes| TD
DEST -->|"exists · non-interactive · no --overwrite"| e6(["exit 6 — already exists"]):::fail
DEST -->|"--overwrite"| TD["teardown existing (fail → exit 7)"]
TD --> STAGE

STAGE["Step 2/3 — copy into your workspace (stage pod; fail → exit 7)"] --> SUB["Step 3/3 — submit → watch → summary"]
SUB --> OUT{"outcome"}
OUT -->|"token 401/403"| e5(["exit 5"]):::fail
OUT -->|"submit rejected 4xx/5xx"| e8(["exit 8"]):::fail
OUT -->|"Failed / Unknown / watch lost / completed-with-failures"| e9(["exit 9"]):::fail
OUT -->|"--detach / Ctrl-C mid-watch"| e0d(["exit 0 — detached (kubectl logs -f)"]):::ok
OUT -->|"Succeeded, clean"| e0s(["exit 0 — done"]):::ok

classDef fail fill:#fdecec,stroke:#d9534f;
classDef ok fill:#eafaea,stroke:#5cb85c;
```

> Note: exit codes are **not** monotonic in execution order — staging (exit 7) runs *before* the token mint (exit 5). The diagram shows the true order.

---

## 4. `resources` — show & set *(`show` shipped #237, on `develop`; `set` on feature branch #241, not yet on `develop`)*

```mermaid
flowchart TD
R0["resources (bare = show)"] --> Rg{"Chain B gates<br/>kubeconfig→3 · no client→4"}
Rg -->|ok| RS(["show machine capacity + per-run ceiling — exit 0"]):::ok

RSET["resources set [max]"] --> RVsh{"request valid?<br/>(not max+flags; not empty)"}
RVsh -->|no| re2a(["exit 2"]):::fail
RVsh -->|ok| Rmode{"max · flags · or wizard?"}
Rmode -->|"wizard (TTY)"| RW["current vs machine →<br/>'Use as much as possible' (default) / choose / leave"]
Rmode -->|flags| RF["override only the passed dimensions"]
Rmode -->|max| RM["whole machine − overhead"]
RW --> FIT
RF --> FIT
RM --> FIT

FIT{"fits the machine? (+ floors)"}
FIT -->|no| re2b(["exit 2 — too big / too small<br/>(macOS: raise Docker Desktop)"]):::fail
FIT -->|"no change"| re0n(["exit 0 — nothing to change"]):::ok
FIT -->|ok| CONF{"confirm? (--yes skips)"}
CONF -->|"declined / non-TTY, no --yes"| re0c(["exit 0 declined / exit 1 non-TTY"]):::fail
CONF -->|yes| PIN{"chart version pinned?"}
PIN -->|no| re1(["exit 1 — refuse unpinned upgrade"]):::fail
PIN -->|yes| APPLY(["helm upgrade — applies to your next run — exit 0"]):::ok

classDef fail fill:#fdecec,stroke:#d9534f;
classDef ok fill:#eafaea,stroke:#5cb85c;
```

---

## Exit codes

| code | meaning |
|---|---|
| 0 | success (incl. dry-run, detached, "nothing to change", declined-safely) |
| 1 | generic / account-auth (not signed in, token rejected, upgrade required) |
| 2 | bad input / schema violation / doesn't fit |
| 3 | kubeconfig unreachable, or a local file/parse error |
| 4 | cluster reached but no tracebloc client / storage |
| 5 | in-cluster token could not be minted (RBAC) |
| 6 | destination dataset already exists |
| 7 | staging / teardown failed |
| 8 | jobs-manager rejected the submit |
| 9 | ingestion Job failed / partial-failure / watch error |
| 130 | Ctrl-C |

## Cross-links — where a dead-end points

- **not signed in / token 401·403** → `login`
- **426 upgrade-required** → upgrade the CLI
- **kubeconfig (exit 3)** → fix `--kubeconfig`/`--context`, then `doctor`
- **no client / environment (exit 4)** → run the installer (or `--namespace`); triage with `doctor`
- **no token (exit 5)** → grant RBAC; diagnose with `cluster info` / `doctor`
- **destination exists (exit 6)** → `--overwrite`, a different `--name`, or `data delete` first
- **staging partial (exit 7)** → `data delete` then re-ingest
- **ingest failed (exit 9)** → the panel / `kubectl get job` / `kubectl logs -f`
- **no active client** (client status / delete) → `client create` / re-run installer

## Known gaps / decisions (raise in review)

1. **Home screen is stateless today.** The status-aware redesign (greeting + sign-in + environment + compute) in `home-screen-spec.md` is *proposed* — it's the target, not current behavior.
2. **`delete` (offboard) exits 0 even on a *partial/degraded* teardown** — it warns but never returns non-zero, so a script can't detect an incomplete offboard. A dedicated non-zero "partial offboard" code would close this.
3. **`resources set` is unshipped on `develop`.** `show` shipped via #237 (now on `develop`); `set` lands via #241 (still on a feature branch).
4. **`cluster doctor` → `tb doctor`** rename is decided (home-screen spec); the map already shows `doctor`. `cluster info`'s home (stay under `cluster`, or also promote) is open.
5. Terminology in the live copy (client / cluster / `<table>`) is pre-cleanup; the map uses the agreed target words (secure environment, etc.). The rename wave aligns the code later.
Loading