From 98d5e2b3be758cd2a2e652058c0bdf53bb6a8fe0 Mon Sep 17 00:00:00 2001 From: Nathan Herald Date: Wed, 29 Jul 2026 23:23:43 +0200 Subject: [PATCH 1/2] docs(vrs): add thin Doctor contract --- docs/vrs/02-doctor/requirements.md | 38 +++++++++++++++++++++ docs/vrs/02-doctor/spec.md | 55 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 docs/vrs/02-doctor/requirements.md create mode 100644 docs/vrs/02-doctor/spec.md diff --git a/docs/vrs/02-doctor/requirements.md b/docs/vrs/02-doctor/requirements.md new file mode 100644 index 0000000..77b4b55 --- /dev/null +++ b/docs/vrs/02-doctor/requirements.md @@ -0,0 +1,38 @@ +# Doctor — Requirements + +Doctor is the on-demand diagnostic gate for one catalog as seen from one host. +It inherits the root [vision](../vision.md) and refines host placement, +supervision, and observability in +[R03](../requirements.md#L38-L39), [R04](../requirements.md#L43-L46), and +[R08](../requirements.md#L72-L78). +It does not define a second health model for the fleet. + +## Requirements + +- **DOCTOR-R01 One diagnostic subject:** A run evaluates one selected catalog + and one selected host. Its result says whether that catalog is healthy from + that host's perspective; it does not claim that remote hosts are available. +- **DOCTOR-R02 Useful as a gate:** A healthy result exits successfully and any + diagnosed problem exits unsuccessfully. Every problem in the human-readable + report names the declaration, task, runtime, catalog, or supervision subject + that failed. +- **DOCTOR-R03 Read-only diagnosis:** A run must not change catalog, presence, + or runtime state. It reports failures but never reconciles, launches, stops, + reaps, repairs, or materializes work. +- **DOCTOR-R04 Safe non-interactive operation:** Doctor and every external probe + it starts must not consume the caller's standard input. Diagnostic work must + be bounded so a wedged dependency becomes a failed check instead of a hung + caller. +- **DOCTOR-R05 Host-local live health:** For active declarations assigned to the + selected host, doctor diagnoses whether declared work is alive and whether + agent presence remains maintained. Declarations assigned to other hosts are + outside the run's availability claim. +- **DOCTOR-R06 Retirement is absence:** A retired declaration is healthy only + after all of its declared task records are absent, whether a remaining record + is live or dead. Retired declarations do not require presence and are not + evaluated by the active-declaration checks. + +The exact retirement distinction is pinned by the +[Retirement health invariant](../../../INVARIANTS.md#L19) and its executable +proofs. Mechanism, check categories, and known gaps belong in +[spec.md](spec.md), not in these guarantees. diff --git a/docs/vrs/02-doctor/spec.md b/docs/vrs/02-doctor/spec.md new file mode 100644 index 0000000..00c12a2 --- /dev/null +++ b/docs/vrs/02-doctor/spec.md @@ -0,0 +1,55 @@ +# Doctor — Spec + +This is a concise map from the [Doctor requirements](requirements.md) to the +current CLI, implementation, and tests. It does not contract check order, +private filenames, exact report wording, or fallback quirks. + +## Scope and inputs + +The [`doctor` CLI](../../../src/main.rs#L236-L249) selects a catalog through the +normal shared catalog rules and a host explicitly or from local detection. +`--require-supervisor` lets a caller distinguish a resident deployment from +intentional manual or one-shot operation. The resolved pair is reported so a +captured diagnosis identifies its subject. + +## Check categories + +The implementation is authoritative in +[`doctor_cmd`](../../../src/main.rs#L946-L1087). Its checks fall into these +categories; their internal order is not part of this sub-VRS. + +| Category | Diagnostic question | Authority | +| --- | --- | --- | +| Environment | Are required runtime tools available? | [`doctor_cmd`](../../../src/main.rs#L955-L961) | +| Supervision | Is the selected host's resident/manual mode consistent with the caller's request? | [`doctor_cmd`](../../../src/main.rs#L963-L986); [mode tests](../../../tests/doctor.rs#L40-L92) | +| Catalog | Can the selected catalog be structurally understood? | [`doctor_cmd`](../../../src/main.rs#L988-L998) | +| Runtime | Can task state be read safely, and is each active local task alive? | [unified runtime view](../../../src/run.rs#L414-L449); [bounded PTY probe](../../../src/run.rs#L38-L84) | +| Presence | Does each active local declaration have maintained presence? | [`doctor_cmd`](../../../src/main.rs#L1021-L1079) | +| Retirement | Are all declared task records for a retired local declaration absent? | [retirement tests](../../../tests/doctor.rs#L191-L297) | + +## Output contract + +Doctor emits a human-readable report for the resolved catalog and host. Each +failure carries a subject label and available detail. It succeeds only when no +problem was diagnosed; otherwise it exits non-zero. The formatter and exit +decision are directly visible in +[`report_check`](../../../src/main.rs#L1082-L1107), while the clean-path test +proves the CLI remains usable without a predecessor transport +([evidence](../../../tests/native_only.rs#L243-L365)). + +## Open questions + +These are implementation gaps or unsettled product choices, not requirements: + +- **Malformed presence:** unreadable or invalid fresh presence currently becomes + `offline`; should doctor distinguish corruption? + ([source](../../../src/status.rs#L70-L95)) +- **Exec enumeration bound:** terminal-free record enumeration is synchronous + filesystem work outside the PTY probe deadline; what bound should the whole + diagnostic promise? ([source](../../../src/exec_backend.rs#L125-L160)) +- **Tool dependency:** `pty` is required even when the selected host declares + only terminal-free work; should required tooling follow the selected tasks? + ([source](../../../src/main.rs#L955-L961)) +- **Machine output:** the command has no stable machine-readable report; should + callers receive structured findings in addition to the exit status? + ([CLI surface](../../../src/main.rs#L236-L249)) From 208577a1b8015e82117e74fa1755af5326cadd31 Mon Sep 17 00:00:00 2001 From: Nathan Herald Date: Fri, 31 Jul 2026 16:15:34 +0200 Subject: [PATCH 2/2] docs(vrs): simplify Doctor contract --- docs/vrs/02-doctor/requirements.md | 56 ++++++++----------- docs/vrs/02-doctor/spec.md | 89 ++++++++++++++++-------------- 2 files changed, 71 insertions(+), 74 deletions(-) diff --git a/docs/vrs/02-doctor/requirements.md b/docs/vrs/02-doctor/requirements.md index 77b4b55..e25665d 100644 --- a/docs/vrs/02-doctor/requirements.md +++ b/docs/vrs/02-doctor/requirements.md @@ -1,38 +1,30 @@ -# Doctor — Requirements +# Doctor requirements -Doctor is the on-demand diagnostic gate for one catalog as seen from one host. -It inherits the root [vision](../vision.md) and refines host placement, -supervision, and observability in -[R03](../requirements.md#L38-L39), [R04](../requirements.md#L43-L46), and -[R08](../requirements.md#L72-L78). -It does not define a second health model for the fleet. +Doctor checks one catalog from one host. It follows the root +[vision](../vision.md). It refines [R03](../requirements.md#L46-L47), +[R04](../requirements.md#L51-L54), and [R08](../requirements.md#L92-L95). +It does not define fleet health. ## Requirements -- **DOCTOR-R01 One diagnostic subject:** A run evaluates one selected catalog - and one selected host. Its result says whether that catalog is healthy from - that host's perspective; it does not claim that remote hosts are available. -- **DOCTOR-R02 Useful as a gate:** A healthy result exits successfully and any - diagnosed problem exits unsuccessfully. Every problem in the human-readable - report names the declaration, task, runtime, catalog, or supervision subject - that failed. +- **DOCTOR-R01 Diagnostic subject:** One run checks one selected catalog and one + selected host. The result does not claim that a remote host is available. +- **DOCTOR-R02 Gate result:** A healthy result returns zero. Any diagnosed + problem returns non-zero. Each problem names the affected declaration, task, + runtime, catalog, or supervision subject. - **DOCTOR-R03 Read-only diagnosis:** A run must not change catalog, presence, - or runtime state. It reports failures but never reconciles, launches, stops, - reaps, repairs, or materializes work. -- **DOCTOR-R04 Safe non-interactive operation:** Doctor and every external probe - it starts must not consume the caller's standard input. Diagnostic work must - be bounded so a wedged dependency becomes a failed check instead of a hung - caller. -- **DOCTOR-R05 Host-local live health:** For active declarations assigned to the - selected host, doctor diagnoses whether declared work is alive and whether - agent presence remains maintained. Declarations assigned to other hosts are - outside the run's availability claim. -- **DOCTOR-R06 Retirement is absence:** A retired declaration is healthy only - after all of its declared task records are absent, whether a remaining record - is live or dead. Retired declarations do not require presence and are not - evaluated by the active-declaration checks. + or runtime state. It must not reconcile, launch, stop, reap, repair, or + materialize work. +- **DOCTOR-R04 Bounded non-interactive operation:** Doctor and its external + probes must not read the caller's standard input. A probe must stop within a + fixed time. A timed-out probe is a failed check, not a hung caller. +- **DOCTOR-R05 Active local health:** Doctor checks each active declaration on + the selected host. It checks that declared work is alive and that presence is + maintained. It does not check remote availability. +- **DOCTOR-R06 Retired absence:** A retired declaration is healthy only when all + declared task records are absent. A live or dead record is unhealthy. A + retired declaration does not require presence or active-declaration checks. -The exact retirement distinction is pinned by the -[Retirement health invariant](../../../INVARIANTS.md#L19) and its executable -proofs. Mechanism, check categories, and known gaps belong in -[spec.md](spec.md), not in these guarantees. +The [Retirement health invariant](../../../INVARIANTS.md#L20) and its tests prove +the retirement rule. The [specification](spec.md) owns the mechanism, check +groups, and known gaps. diff --git a/docs/vrs/02-doctor/spec.md b/docs/vrs/02-doctor/spec.md index 00c12a2..471056f 100644 --- a/docs/vrs/02-doctor/spec.md +++ b/docs/vrs/02-doctor/spec.md @@ -1,55 +1,60 @@ -# Doctor — Spec +# Doctor specification -This is a concise map from the [Doctor requirements](requirements.md) to the -current CLI, implementation, and tests. It does not contract check order, -private filenames, exact report wording, or fallback quirks. +This document maps the [Doctor requirements](requirements.md) to the current +CLI, implementation, and tests. It does not define check order, private +filenames, exact report text, or fallback behavior. ## Scope and inputs -The [`doctor` CLI](../../../src/main.rs#L236-L249) selects a catalog through the -normal shared catalog rules and a host explicitly or from local detection. -`--require-supervisor` lets a caller distinguish a resident deployment from -intentional manual or one-shot operation. The resolved pair is reported so a -captured diagnosis identifies its subject. - -## Check categories - -The implementation is authoritative in -[`doctor_cmd`](../../../src/main.rs#L946-L1087). Its checks fall into these -categories; their internal order is not part of this sub-VRS. - -| Category | Diagnostic question | Authority | -| --- | --- | --- | -| Environment | Are required runtime tools available? | [`doctor_cmd`](../../../src/main.rs#L955-L961) | -| Supervision | Is the selected host's resident/manual mode consistent with the caller's request? | [`doctor_cmd`](../../../src/main.rs#L963-L986); [mode tests](../../../tests/doctor.rs#L40-L92) | -| Catalog | Can the selected catalog be structurally understood? | [`doctor_cmd`](../../../src/main.rs#L988-L998) | -| Runtime | Can task state be read safely, and is each active local task alive? | [unified runtime view](../../../src/run.rs#L414-L449); [bounded PTY probe](../../../src/run.rs#L38-L84) | -| Presence | Does each active local declaration have maintained presence? | [`doctor_cmd`](../../../src/main.rs#L1021-L1079) | -| Retirement | Are all declared task records for a retired local declaration absent? | [retirement tests](../../../tests/doctor.rs#L191-L297) | +The [`doctor` CLI](../../../src/main.rs#L240-L252) selects a catalog and a host. +Shared catalog rules resolve the catalog. The caller can select a host, or st2 +can detect the local host. The `--require-supervisor` flag requires a resident +`st2 up` process. Without this flag, manual and one-shot operation are valid. +The report names the resolved catalog and host. + +## Checks + +[`doctor_cmd`](../../../src/main.rs#L988-L1130) is the implementation authority. +It performs these check groups: + +- **Environment:** The required runtime tools are available + ([source](../../../src/main.rs#L997-L1003)). +- **Supervision:** The host mode matches the caller's request + ([source](../../../src/main.rs#L1005-L1028); [tests](../../../tests/doctor.rs#L40-L92)). +- **Catalog:** The selected catalog has no discovery errors + ([source](../../../src/main.rs#L1030-L1040)). +- **Runtime:** Runtime state is readable, and each active local task is alive + ([Doctor source](../../../src/main.rs#L1041-L1102); [unified runtime view](../../../src/run.rs#L687-L700)). + The PTY probe has a fixed deadline and closed standard input + ([probe source](../../../src/run.rs#L443-L458); [tests](../../../tests/doctor.rs#L95-L194)). +- **Presence:** Each active local declaration has maintained presence + ([source](../../../src/main.rs#L1103-L1121)). +- **Retirement:** Every task record for a retired local declaration is absent + ([source](../../../src/main.rs#L1068-L1089); [tests](../../../tests/doctor.rs#L196-L302)). ## Output contract -Doctor emits a human-readable report for the resolved catalog and host. Each -failure carries a subject label and available detail. It succeeds only when no -problem was diagnosed; otherwise it exits non-zero. The formatter and exit -decision are directly visible in -[`report_check`](../../../src/main.rs#L1082-L1107), while the clean-path test -proves the CLI remains usable without a predecessor transport -([evidence](../../../tests/native_only.rs#L243-L365)). +Doctor writes a human-readable report for the resolved catalog and host. Each +failure has a subject label and available detail. Doctor returns zero only when +it diagnoses no problem. The formatter and result are in +[`report_check`](../../../src/main.rs#L1165-L1175) and +[`doctor_cmd`](../../../src/main.rs#L1124-L1129). The +[clean-path test](../../../tests/native_only.rs#L243-L369) proves that the CLI +does not need a predecessor transport. ## Open questions These are implementation gaps or unsettled product choices, not requirements: -- **Malformed presence:** unreadable or invalid fresh presence currently becomes - `offline`; should doctor distinguish corruption? +- **Malformed presence:** Unreadable or invalid fresh presence becomes + `offline`. Should Doctor report corruption separately? ([source](../../../src/status.rs#L70-L95)) -- **Exec enumeration bound:** terminal-free record enumeration is synchronous - filesystem work outside the PTY probe deadline; what bound should the whole - diagnostic promise? ([source](../../../src/exec_backend.rs#L125-L160)) -- **Tool dependency:** `pty` is required even when the selected host declares - only terminal-free work; should required tooling follow the selected tasks? - ([source](../../../src/main.rs#L955-L961)) -- **Machine output:** the command has no stable machine-readable report; should - callers receive structured findings in addition to the exit status? - ([CLI surface](../../../src/main.rs#L236-L249)) +- **Exec enumeration bound:** Exec record enumeration is synchronous file-system + work. It is outside the PTY deadline. What deadline should cover the full + diagnosis? ([source](../../../src/exec_backend.rs#L190-L217)) +- **Tool dependency:** Doctor requires `pty` when a host has only exec tasks. + Should the declared tasks select the required tools? + ([source](../../../src/main.rs#L997-L1003)) +- **Machine output:** Doctor has no stable machine-readable report. Should it + provide structured findings in addition to the exit status? + ([CLI surface](../../../src/main.rs#L240-L252))