From a67234266b6cbb6736580d7c4b142ad0d21330f3 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sun, 23 Aug 2026 05:06:39 +0800 Subject: [PATCH] docs(cli): name doctor's config section, and pin the shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tty7 doctor --json` has emitted a fourth top-level section, `config`, since it started answering for a config file that parses but carries keys tty7 does not read. The reference went on describing three. Checked the documented shape of every `--json` verb against a live daemon; this was the one that disagreed. It matters more here than elsewhere. `doctor` is one of the three verbs whose JSON is printed even when the verb fails — an unreachable server is the finding, and `tty7 doctor || alert` needs the rows as well as the code — so something is always parsing this. The page now names `config`, says what `ok` and `state` mean, and says which of the table's rows are prose rather than fields, so the next reader is not left looking for `config keys` in the JSON. The guard compares doctor's top-level sections against the ones the reference line names, both directions. Top level only: the fields inside vary with what a server could be asked — `context` gains `workspace_gone` and `pane_gone` only when one answered — and the page explains that in prose it would be wrong to pin to a fixed list. Verified by dropping the section from the page, and by dropping it from doctor. --- crates/tty7-cli/src/commands.rs | 57 +++++++++++++++++++++++++++++++++ docs/cli/reference.mdx | 7 ++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/crates/tty7-cli/src/commands.rs b/crates/tty7-cli/src/commands.rs index 3f6331b3..f8366f6a 100644 --- a/crates/tty7-cli/src/commands.rs +++ b/crates/tty7-cli/src/commands.rs @@ -4631,6 +4631,63 @@ mod tests { assert!(out.contains("set (/cfg/tty7)"), "{out}"); } + /// `doctor --json` carries the sections the reference says it does. + /// + /// This is one of the three verbs whose JSON is printed even when it + /// fails, so something is always parsing it — `tty7 doctor || alert` needs + /// the rows as well as the code. A section added here and not written down + /// is a shape change under a documented contract, and that is how the + /// `config` section arrived: added to answer for a config the file parsed + /// but tty7 does not read, and the page went on describing three sections. + /// + /// Top level only. The fields inside vary with what a server could be + /// asked — `context` gains `workspace_gone` and `pane_gone` only when one + /// answered — and the page explains that in prose it would be wrong to + /// pin to a fixed list. + #[test] + fn the_doctor_json_sections_are_the_ones_the_reference_names() { + const DOC: &str = include_str!("../../../docs/cli/reference.mdx"); + + let lead = "JSON: `{\"context\":"; + let at = DOC.find(lead).expect("the reference documents doctor's JSON"); + let line = &DOC[at..at + DOC[at..].find('\n').expect("the line ends")]; + let documented: Vec<&str> = line + .split('"') + .filter(|t| t.chars().all(|c| c.is_ascii_lowercase() || c == '_') && !t.is_empty()) + .collect(); + + let out = run_cli( + &["tty7", "doctor", "--json"], + &Context::default(), + &mut doctor_backend(), + ); + let j = json_of(out); + let sections: Vec = j + .as_object() + .expect("doctor answers an object") + .keys() + .cloned() + .collect(); + assert!( + sections.len() >= 3, + "doctor answered almost nothing: {sections:?}" + ); + for section in §ions { + assert!( + documented.contains(§ion.as_str()), + "`doctor --json` emits a `{section}` section that \ + docs/cli/reference.mdx never names" + ); + } + for want in ["context", "server", "hooks", "config"] { + assert!( + sections.iter().any(|s| s == want), + "the reference promises a `{want}` section and doctor did not \ + emit one: {sections:?}" + ); + } + } + #[test] fn doctor_says_when_the_inherited_context_names_nothing_here() { // A shell outlives the workspace it was opened in, and one opened diff --git a/docs/cli/reference.mdx b/docs/cli/reference.mdx index c449a9b4..5c59620b 100644 --- a/docs/cli/reference.mdx +++ b/docs/cli/reference.mdx @@ -320,11 +320,14 @@ nothing, so `tty7 agents` shows it standing still and `tty7 wait` sits there until it times out. Outdated hooks fail the same quiet way. Hooks are a local install, so under `-m` the row reads `unknown`. -JSON: `{"context":{"config_dir","workspace","pane"},"server":{"reachable","dialect_ok","build","status","routes"},"hooks":{"installed","outdated","not_installed"}}` +JSON: `{"context":{"config_dir","workspace","pane"},"server":{"reachable","dialect_ok","build","status","routes"},"config":{"ok","state"},"hooks":{"installed","outdated","not_installed"}}` — the context fields are booleans, not values, and each `hooks` field is a list of agent slugs. `context` also carries `workspace_gone` and `pane_gone` when a server answered and could be asked; both are absent when none did, so that "it -names nothing here" stays distinct from "nobody could check". +names nothing here" stays distinct from "nobody could check". `config.ok` is +false only when the file failed to parse and tty7 is running on defaults; +`config.state` is the same sentence the table prints. The extra rows the table +can show — unread keys, unusable `custom_shells` — are prose, not JSON fields. ## `ws` — workspaces