Skip to main content

majordomus_cli/
about.rs

1//! What the executable says about itself, written once. The OpenAPI document's `info`,
2//! the MCP `initialize` instructions, the HTTP index and the generated reference all
3//! read these sentences from here, so that no projection describes the surface in words
4//! of its own and none can drift from the others. Prose only: counts, URLs of a running
5//! server and anything else that is measured are added by the projection that measures
6//! it.
7//!
8//! ```
9//! use majordomus_cli::about;
10//!
11//! // one sentence, and every projection titles itself with it
12//! assert_eq!(about::NAME, "Majordomus");
13//! let text = about::description();
14//! assert!(text.starts_with(about::SUMMARY));
15//! // the description is the summary and every paragraph, so a projection that shows the
16//! // description cannot be missing one a projection that shows the paragraphs has
17//! assert!(about::PARAGRAPHS.iter().all(|p| text.contains(p)));
18//! ```
19
20/// The product name, as every projection titles itself.
21pub const NAME: &str = "Majordomus";
22
23/// The source repository, from the crate manifest.
24pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
25
26/// The licence, from the crate manifest, as an SPDX identifier.
27pub const LICENSE: &str = env!("CARGO_PKG_LICENSE");
28
29/// Where the HTTP projection is explained for a person: the derived API reference on the
30/// documentation site, rendered from the OpenAPI document itself.
31pub const REFERENCE_URL: &str = "https://majordomus.dev/docs/api/";
32
33/// One sentence: what is being served.
34///
35/// Not "read-only": the registry holds commands whose declared effect is a repository
36/// mutation, and a sentence every client reads first must not deny them. Which ones they
37/// are is measured by the projection that serves them ([`writes`]), never written here.
38pub const SUMMARY: &str = "The projection of a repository's AI layer: every rule, prompt, session and project record under .ai/, the capabilities that read them and the declared commands that change them, served over MCP, HTTP and the command line from one registry.";
39
40/// The paragraphs a reader of any projection should have, in order. CommonMark, as
41/// Swagger UI and the site render it; each entry is one paragraph.
42pub const PARAGRAPHS: &[&str] = &[
43    "**One registry, every interface a projection.** Every operation here is a capability of the registry and nothing is declared for HTTP alone. `operationId` is the capability's canonical id; the same capability answers as the MCP tool `x-majordomus-mcp` names and as the command `x-majordomus-cli` names, where its exposure says so, through one executor with one cache. A change to a capability's description or input type reaches this document, the MCP schema, the command line and the generated reference on the next start; nothing is edited by hand.",
44    "**Loopback, unauthenticated, and explicit about what writes.** The first `majordomus mcp` in a repository binds this server beside its stdio session and logs the URL; `majordomus serve` binds it alone. It listens on 127.0.0.1 and asks for no credentials: whoever can reach the socket can read the layer and can run every command it exposes. An operation that changes a tracked file declares the effect `repository_mutation` in `x-majordomus-effect`; it is always a `POST` and never a read-only MCP tool, and every other operation reads. `GET /` is the home page and lists every surface; `/swagger` is Swagger UI over this document; `/docs/` is this repository's own documentation; `/mcp` is MCP over HTTP for a second client.",
45    "**Binding.** `GET` binds every top-level property of the capability's input as a query parameter, coerced by the schema's type (integers, numbers and booleans are parsed, everything else is text); `POST` binds the input as the JSON body. An unknown parameter is an invalid input, not ignored. Every failure is one JSON body, `{ \"error\": { \"code\", \"message\" } }`: 400 `invalid_input`, 403 `forbidden` (a caller beyond loopback asking for anything but a read or a signed mesh message), 404 `not_found`, 422 `refused` (a command's alone), 500 `internal`; 405 `method_not_allowed` names a path that exists under another method.",
46    "**Examples are benchmark cases.** Every example in this document is one of the capability's own benchmark cases, the inputs the benchmarks time and the tests replay against a real socket: an operation without an example is an operation without a case, and the executable does not compile in that state. `x-majordomus-benchmark` and `x-majordomus-cache` carry the policies; `x-majordomus-stability` and `x-majordomus-provenance` say how far a capability is proved and where it was declared.",
47    "**Generated, committed, checked.** This document is rendered from the registry at every request and committed as `docs/generated/openapi.json`; `majordomus generate --check` derives it again and refuses a stale copy, and CI runs that check on every push, so the committed document, the served one and the reference on the site are the same document.",
48];
49
50/// The sentence that says what writes the repository, given the MCP tools whose declared
51/// effect is a repository mutation, as the projection measured them from its registry.
52///
53/// ```
54/// use majordomus_cli::about;
55/// assert_eq!(about::writes(&[]), "Nothing here writes to the repository.");
56/// let text = about::writes(&["majordomus_a", "majordomus_b"]);
57/// assert!(text.starts_with("Only majordomus_a, majordomus_b write to the repository"));
58/// ```
59pub fn writes(tools: &[&str]) -> String {
60    if tools.is_empty() {
61        return "Nothing here writes to the repository.".to_string();
62    }
63    format!(
64        "Only {} write to the repository (declared effect repository_mutation); every other tool and resource reads.",
65        tools.join(", ")
66    )
67}
68
69/// The paragraphs as one CommonMark text.
70///
71/// ```
72/// use majordomus_cli::about;
73/// let text = about::description();
74/// assert!(text.starts_with(about::SUMMARY));
75/// assert!(text.contains("**Binding.**"));
76/// ```
77pub fn description() -> String {
78    let mut text = String::from(SUMMARY);
79    for p in PARAGRAPHS {
80        text.push_str("\n\n");
81        text.push_str(p);
82    }
83    text
84}