Skip to content

entity.show

One object of the layer as an addressable node, by URI or by the address it is served at: identity and route, provenance, front matter and content, every reference it declares and every reference that resolves to it, the surfaces that answer for it, the state of the executable artefacts it names, and its public documentation page as the repository's site/data/publication.toml derives it (null when the repository declares none).

One object of the layer as an addressable node, by URI or by the address it is served at: identity and route, provenance, front matter and content, every reference it declares and every reference that resolves to it, the surfaces that answer for it, the state of the executable artefacts it names, and its public documentation page as the repository's site/data/publication.toml derives it (null when the repository declares none).

Exposure

Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.

surfaceas
MCPtool majordomus_entity
HTTPGET /api/v1/entity operationId entity.show
command linemajordomus entity show

Schemas

The canonical input and output, from the Rust types; the MCP tool schema and the OpenAPI parameters and responses are derived from these.

input · EntityInput

Which entity: by URI, or by the address it is served at.

Two spellings of one question, because two kinds of caller ask it: MCP and the API hold the majordomus:// URI the index is keyed by, and the Cockpit's router has a kind and a slug out of a path. Nothing converts one form into the other at the edge — the capability resolves either, so neither caller has to know the other's spelling.

use majordomus_cli::capability::builtin::entity::EntityInput;

let by_route: EntityInput =
    serde_json::from_value(serde_json::json!({ "kind": "adr", "slug": "adr-0056" }))
        .unwrap();
assert_eq!(by_route.kind.as_deref(), Some("adr"));
assert_eq!(by_route.slug.as_deref(), Some("adr-0056"));
assert_eq!(by_route.uri, None);

// absent is a shape, not an error: which fields are required is the handler's question
assert_eq!(EntityInput::default().kind, None);
propertytyperequireddescription
kind string | null no The kind, when addressing by route.
slug string | null no The last segment of the entity's route, when addressing by route.
uri string | null no majordomus://<kind>/<identity>. Either this, or kind and slug together.
JSON Schema
{
  "additionalProperties": false,
  "description": "Which entity: by URI, or by the address it is served at.\n\nTwo spellings of one question, because two kinds of caller ask it: MCP and the API hold\nthe `majordomus://` URI the index is keyed by, and the Cockpit's router has a `kind` and\na `slug` out of a path. Nothing converts one form into the other at the edge — the\ncapability resolves either, so neither caller has to know the other's spelling.\n\n```\nuse majordomus_cli::capability::builtin::entity::EntityInput;\n\nlet by_route: EntityInput =\n    serde_json::from_value(serde_json::json!({ \"kind\": \"adr\", \"slug\": \"adr-0056\" }))\n        .unwrap();\nassert_eq!(by_route.kind.as_deref(), Some(\"adr\"));\nassert_eq!(by_route.slug.as_deref(), Some(\"adr-0056\"));\nassert_eq!(by_route.uri, None);\n\n// absent is a shape, not an error: which fields are required is the handler's question\nassert_eq!(EntityInput::default().kind, None);\n```",
  "properties": {
    "kind": {
      "description": "The kind, when addressing by route.",
      "type": [
        "string",
        "null"
      ]
    },
    "slug": {
      "description": "The last segment of the entity's route, when addressing by route.",
      "type": [
        "string",
        "null"
      ]
    },
    "uri": {
      "description": "`majordomus://<kind>/<identity>`. Either this, or `kind` and `slug` together.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "title": "EntityInput",
  "type": "object"
}

output · EntityView

One object of the layer as an addressable node: what it is, where it is served, what it is joined to, and what can be said about its enforcement.

The whole answer in one value, because it is one question: every surface that shows an entity — the Cockpit page, GET /api/v1/entity, majordomus_entity, majordomus entity show — renders this and composes nothing of its own. A field a reader sees on one surface and not another would be a surface holding an opinion, which is what ADR 0004 forecloses.

use majordomus_cli::capability::builtin::entity::EntityView;

// it round-trips through the wire shape every projection reads it as
let view: EntityView = serde_json::from_value(serde_json::json!({
    "uri": "majordomus://adr/adr-0056",
    "id": "adr.adr-0056",
    "kind": "adr",
    "identity": "adr-0056",
    "slug": "adr-0056",
    "route": "/cockpit/objects/adr/adr-0056",
    "kind_route": "/cockpit/objects/adr",
    "title": "Every object of the layer has a derived address",
    "provenance": { "path": "x.md", "directory": "x", "source_class": "adr", "bytes": 0 },
    "metadata": {},
    "media_type": "text/markdown",
    "content": "",
    "relations": [],
    "surfaces": [],
    "evidence": { "state": "unclaimed", "meaning": "nothing named" },
    "documentation": {
        "projection": "entity",
        "route": "/adrs/adr-0056/",
        "url": "https://majordomus.dev/adrs/adr-0056/",
        "declared_in": "site/data/publication.toml",
    },
}))
.unwrap();

assert_eq!(view.route, "/cockpit/objects/adr/adr-0056");
// the Cockpit route is the object's; the public page is the site's, and both are named
let public = view.documentation.as_ref().and_then(|d| d.url.as_deref());
assert_eq!(public, Some("https://majordomus.dev/adrs/adr-0056/"));
// the kind's index is the entity's route with the last segment removed
assert!(view.route.starts_with(&view.kind_route));
propertytyperequireddescription
content string yes The file as read.
description string | null no The one-line description, when the kind holds one.
documentation one of 2 no Its public documentation page, derived from the repository's publication declaration (site/data/publication.toml) and the site's base_url by the same function the site generator writes the page with. Null when the repository declares no publication or does not declare this kind; a kind declared as not published carries the reason. default null
evidence reference yes What can be said about its enforcement without running anything.
id string yes The capability id, <kind>.<identity>.
identity string yes The identity within the kind.
kind string yes The kind.
kind_route string yes The route of its kind's index.
media_type string yes IANA media type of content.
metadata object yes The parsed front matter or YAML, keys in the file's order.
provenance reference yes Where it came from.
relations array yes The references it declares, and the references that resolve to it.
route string yes Its route in the Cockpit.
slug string yes The last segment of its route, derived from the identity.
surfaces array yes Every surface that answers for it.
title string | null no The title, when the kind's title rule found one.
uri string yes majordomus://<kind>/<identity>.
JSON Schema
{
  "$defs": {
    "ClaimedArtifact": {
      "description": "One executable artefact an object names, and whether the tree holds it.\n\n`field` is kept beside the path because the same object can name artefacts under several\nkeys, and a reader fixing a dangling one needs to know which line of the front matter to\nlook at rather than which file is missing.\n\n```\nuse majordomus_cli::capability::builtin::entity::ClaimedArtifact;\n\nlet a = ClaimedArtifact {\n    field: \"x-majordomus.tests\".into(),\n    path: \"test/cases/277_entities_are_routable.sh\".into(),\n    present: true,\n};\nlet wire = serde_json::to_value(&a).unwrap();\nassert_eq!(wire[\"field\"], serde_json::json!(\"x-majordomus.tests\"));\nassert_eq!(wire[\"present\"], serde_json::json!(true));\n```",
      "properties": {
        "field": {
          "description": "The front matter key it was named under (`x-majordomus.tests`, `test`, ...).",
          "type": "string"
        },
        "path": {
          "description": "The repository-relative path as named.",
          "type": "string"
        },
        "present": {
          "description": "Whether the tree holds it.",
          "type": "boolean"
        }
      },
      "required": [
        "field",
        "path",
        "present"
      ],
      "type": "object"
    },
    "EntityDocumentation": {
      "description": "The public documentation page of one entity, as its kind's publication decision derives\nit. Every surface that shows an entity carries this value and renders it; none of them\nderives a public address of its own.\n\n```\nuse majordomus_cli::entity::Documentation;\n\nlet d = Documentation {\n    projection: \"entity\".into(),\n    route: Some(\"/adrs/adr-0056/\".into()),\n    url: Some(\"https://majordomus.dev/adrs/adr-0056/\".into()),\n    reason: None,\n    declared_in: \"site/data/publication.toml\".into(),\n};\nlet wire = serde_json::to_value(&d).unwrap();\nassert_eq!(wire[\"url\"], serde_json::json!(\"https://majordomus.dev/adrs/adr-0056/\"));\n// a field that does not apply is absent, not an empty string\nassert!(wire.get(\"reason\").is_none());\n```",
      "properties": {
        "declared_in": {
          "description": "The file the decision is read from.",
          "type": "string"
        },
        "projection": {
          "description": "The kind's projection: `entity`, `section` or `none`.",
          "type": "string"
        },
        "reason": {
          "description": "Why the kind is not published, for `none`.",
          "type": [
            "string",
            "null"
          ]
        },
        "route": {
          "description": "The public route of the page, site-relative: the entity's own page for an `entity`\nkind, the kind's section for a `section` kind, absent for `none`.",
          "type": [
            "string",
            "null"
          ]
        },
        "url": {
          "description": "The route under the site's `base_url`, when the site configuration names one.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "projection",
        "declared_in"
      ],
      "type": "object"
    },
    "EntityEdge": {
      "description": "One edge, as an entity page reads it.\n\nEverything a reader needs about the other end is here — what to show, what kind of thing\nit is, and the address to send them to — so the page renders a relation without going\nback to the index for it. `route` is `None` exactly when `external` is true: a `file`, a\n`test` or a `command` is named by the layer and is not an object of it, so there is no\nentity page to link to and the template shows plain text instead of a dead link.\n\n```\nuse majordomus_cli::entity::{Direction, Edge};\nuse majordomus_cli::order::Ordered;\n\nlet edge = Edge {\n    direction: Direction::Incoming,\n    edge: \"depends_on\".into(),\n    field: \"depends_on\".into(),\n    uri: Some(\"majordomus://rule/project.x@1\".into()),\n    kind: \"rule\".into(),\n    label: \"project.x@1\".into(),\n    title: Some(\"A rule\".into()),\n    route: Some(\"/cockpit/objects/rule/project-x-1\".into()),\n    external: false,\n};\n\n// the relation is the group and the direction ranks inside it; see the `Ordered` impl\nlet key = edge.order_key();\nassert_eq!(key.group, Some(\"depends_on\"));\nassert_eq!(key.rank, Direction::Incoming as i64);\nassert_eq!(key.identity, \"majordomus://rule/project.x@1\");\n```",
      "properties": {
        "direction": {
          "$ref": "#/$defs/RelationDirection",
          "description": "Which way it runs from the entity that was asked for."
        },
        "edge": {
          "description": "The relationship, in the graph's vocabulary (`depends_on`, `put_in_force`, ...).",
          "type": "string"
        },
        "external": {
          "description": "True when the other end is not an object of this index.",
          "type": "boolean"
        },
        "field": {
          "description": "The front matter key the reference was declared under.",
          "type": "string"
        },
        "kind": {
          "description": "Its kind — of the layer, or the external vocabulary (`file`, `test`, `command`).",
          "type": "string"
        },
        "label": {
          "description": "What to show: the identity of an object, or the name of something outside.",
          "type": "string"
        },
        "route": {
          "description": "Its entity route, when it has one; absent for something outside the layer.",
          "type": [
            "string",
            "null"
          ]
        },
        "title": {
          "description": "Its title, when it is an object that has one.",
          "type": [
            "string",
            "null"
          ]
        },
        "uri": {
          "description": "The object at the other end: its URI when the layer holds it.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "direction",
        "edge",
        "field",
        "kind",
        "label",
        "external"
      ],
      "type": "object"
    },
    "EntityEvidence": {
      "description": "What can be said about an object's enforcement without running anything.\n\n`meaning` travels with `state` so that every surface shows the same sentence about it:\na badge alone invites each renderer to write its own gloss, and the gloss is where a\n\"resolved\" quietly becomes a \"passing\". A kind that names no artefact is `Unclaimed`\nwith an empty `artifacts`, which is an answer and not an omission.\n\n```\nuse majordomus_cli::capability::builtin::entity::{Evidence, EvidenceState};\n\nlet e = Evidence {\n    state: EvidenceState::Unclaimed,\n    meaning: \"This object names no executable artefact.\".into(),\n    artifacts: Vec::new(),\n    proof: None,\n};\n\n// the empty collections are omitted from the wire, so absence reads as absence\nlet wire = serde_json::to_value(&e).unwrap();\nassert_eq!(wire[\"state\"], serde_json::json!(\"unclaimed\"));\nassert!(wire.get(\"artifacts\").is_none());\nassert!(wire.get(\"proof\").is_none());\n```",
      "properties": {
        "artifacts": {
          "description": "Every artefact named, with whether the tree holds it.",
          "items": {
            "$ref": "#/$defs/ClaimedArtifact"
          },
          "type": "array"
        },
        "meaning": {
          "description": "That state in one sentence, for a reader.",
          "type": "string"
        },
        "proof": {
          "anyOf": [
            {
              "$ref": "#/$defs/EntityProofRef"
            },
            {
              "type": "null"
            }
          ],
          "description": "The capability that can say more, when this kind has one."
        },
        "state": {
          "$ref": "#/$defs/EntityEvidenceState",
          "description": "The state, which is never rounded up."
        }
      },
      "required": [
        "state",
        "meaning"
      ],
      "type": "object"
    },
    "EntityEvidenceState": {
      "description": "What this repository can say about the executable artefacts an object names, and only\nthat. The word is chosen so that no reader can mistake it for a run.\n\nThe three states are ordered by how much they permit a reader to conclude, and the\nstrongest of them still concludes nothing about a run: `Resolved` means the files an\nobject points at exist. `proof` on `Evidence` is where \"did it ever pass\" is asked.\n\n```\nuse majordomus_cli::capability::builtin::entity::EvidenceState;\n\n// the wire spellings, which the Cockpit badge and the site template both read\nfor (state, word) in [\n    (EvidenceState::Unclaimed, \"unclaimed\"),\n    (EvidenceState::Dangling, \"dangling\"),\n    (EvidenceState::Resolved, \"resolved\"),\n] {\n    assert_eq!(serde_json::to_value(state).unwrap(), serde_json::json!(word));\n}\n```",
      "oneOf": [
        {
          "const": "unclaimed",
          "description": "The object names no executable artefact. Most kinds name none, and that is not a\ndefect: a prompt is not enforced by anything.",
          "type": "string"
        },
        {
          "const": "dangling",
          "description": "It names artefacts and at least one of them is not in the tree. A claim that points\nat nothing is worse than no claim, because it reads as enforcement.",
          "type": "string"
        },
        {
          "const": "resolved",
          "description": "Every artefact it names is in the tree. This says the claim resolves — never that it\npassed. Whether it ever ran is a different question, and `proof` is where it is\nasked.",
          "type": "string"
        }
      ]
    },
    "EntityProofRef": {
      "description": "Where a fuller answer about this entity's enforcement lives.\n\nA pointer, never an answer. This module states what it can see in the tree and then\nnames the capability that can say more, so that an entity page does not grow a second,\nweaker implementation of proof beside the one that already exists.\n\n```\nuse majordomus_cli::capability::builtin::entity::ProofRef;\n\nlet p = ProofRef {\n    capability: \"rule.proves\".into(),\n    title: \"What proves a rule\".into(),\n    address: \"majordomus rule proves project.entities-are-routable@1\".into(),\n};\nassert_eq!(\n    serde_json::to_value(&p).unwrap()[\"capability\"],\n    serde_json::json!(\"rule.proves\")\n);\n```",
      "properties": {
        "address": {
          "description": "How to ask it here.",
          "type": "string"
        },
        "capability": {
          "description": "The capability that answers it.",
          "type": "string"
        },
        "title": {
          "description": "Its title.",
          "type": "string"
        }
      },
      "required": [
        "capability",
        "title",
        "address"
      ],
      "type": "object"
    },
    "EntitySurface": {
      "description": "One place this entity can be reached, and how.\n\nA row of the \"where else this is answered\" table on an entity page. `surface` is the\ninterface's own name, so a reader who works on the command line and a reader who works\nover MCP each find their own line rather than a route they have to translate.\n\n```\nuse majordomus_cli::entity::Surface;\n\nlet s = Surface {\n    surface: \"mcp\".into(),\n    address: \"majordomus://rule/project.x@1\".into(),\n    detail: \"the file as read\".into(),\n};\nassert_eq!(\n    serde_json::to_value(&s).unwrap()[\"surface\"],\n    serde_json::json!(\"mcp\")\n);\n```",
      "properties": {
        "address": {
          "description": "The address on it: a URI, a route, a command line.",
          "type": "string"
        },
        "detail": {
          "description": "What a reader does with it.",
          "type": "string"
        },
        "surface": {
          "description": "Which surface: `mcp`, `http`, `cli`, `cockpit`, `site`.",
          "type": "string"
        }
      },
      "required": [
        "surface",
        "address",
        "detail"
      ],
      "type": "object"
    },
    "ObjectProvenance": {
      "description": "Where an object came from. Every field is computed from the repository, never authored.",
      "properties": {
        "bytes": {
          "description": "Size of the file in bytes.",
          "format": "uint64",
          "minimum": 0,
          "type": "integer"
        },
        "directory": {
          "description": "The directory the path sits in, repository-relative; `.` for the root. This is the\nhierarchy position a client orders by; no merge semantics are implied.",
          "type": "string"
        },
        "member": {
          "description": "For an object that is one member of a collection file: the member's key path in\nthe file (`claims.3`).",
          "type": [
            "string",
            "null"
          ]
        },
        "path": {
          "description": "Repository-relative path, forward slashes, as the version-control index names it.",
          "type": "string"
        },
        "section": {
          "description": "The manifest section the path falls under (`rules`, `prompts`, ...), when it falls\nunder one; a root `README.md` falls under none.",
          "type": [
            "string",
            "null"
          ]
        },
        "source_class": {
          "description": "The `sources.yaml` class that discovered the file (`rule`, `readme`, ...).",
          "type": "string"
        }
      },
      "required": [
        "path",
        "directory",
        "source_class",
        "bytes"
      ],
      "type": "object"
    },
    "RelationDirection": {
      "description": "Which way a relation runs from the entity being read.\n\nThe direction is a property of the *reading*, not of the relation: one edge in\n`graph::RELATIONS` is outgoing on the page of the object that declared it and incoming\non the page of the object it names. Nothing in the layer writes `Incoming` down.\n\n```\nuse majordomus_cli::entity::Direction;\n\n// the wire spelling, which every surface shows and the site template reads\nassert_eq!(\n    serde_json::to_string(&Direction::Outgoing).unwrap(),\n    \"\\\"outgoing\\\"\"\n);\nassert_eq!(\n    serde_json::to_string(&Direction::Incoming).unwrap(),\n    \"\\\"incoming\\\"\"\n);\n// declared before derived, which is the order an entity page renders the two sections\nassert!((Direction::Outgoing as u8) < (Direction::Incoming as u8));\n```",
      "oneOf": [
        {
          "const": "outgoing",
          "description": "This entity declared the reference.",
          "type": "string"
        },
        {
          "const": "incoming",
          "description": "Another entity named this one. Never declared: derived by reading every other\nobject's references and keeping the ones that resolve here.",
          "type": "string"
        }
      ]
    }
  },
  "description": "One object of the layer as an addressable node: what it is, where it is served, what it\nis joined to, and what can be said about its enforcement.\n\nThe whole answer in one value, because it is one question: every surface that shows an\nentity — the Cockpit page, `GET /api/v1/entity`, `majordomus_entity`, `majordomus entity\nshow` — renders this and composes nothing of its own. A field a reader sees on one\nsurface and not another would be a surface holding an opinion, which is what ADR 0004\nforecloses.\n\n```\nuse majordomus_cli::capability::builtin::entity::EntityView;\n\n// it round-trips through the wire shape every projection reads it as\nlet view: EntityView = serde_json::from_value(serde_json::json!({\n    \"uri\": \"majordomus://adr/adr-0056\",\n    \"id\": \"adr.adr-0056\",\n    \"kind\": \"adr\",\n    \"identity\": \"adr-0056\",\n    \"slug\": \"adr-0056\",\n    \"route\": \"/cockpit/objects/adr/adr-0056\",\n    \"kind_route\": \"/cockpit/objects/adr\",\n    \"title\": \"Every object of the layer has a derived address\",\n    \"provenance\": { \"path\": \"x.md\", \"directory\": \"x\", \"source_class\": \"adr\", \"bytes\": 0 },\n    \"metadata\": {},\n    \"media_type\": \"text/markdown\",\n    \"content\": \"\",\n    \"relations\": [],\n    \"surfaces\": [],\n    \"evidence\": { \"state\": \"unclaimed\", \"meaning\": \"nothing named\" },\n    \"documentation\": {\n        \"projection\": \"entity\",\n        \"route\": \"/adrs/adr-0056/\",\n        \"url\": \"https://majordomus.dev/adrs/adr-0056/\",\n        \"declared_in\": \"site/data/publication.toml\",\n    },\n}))\n.unwrap();\n\nassert_eq!(view.route, \"/cockpit/objects/adr/adr-0056\");\n// the Cockpit route is the object's; the public page is the site's, and both are named\nlet public = view.documentation.as_ref().and_then(|d| d.url.as_deref());\nassert_eq!(public, Some(\"https://majordomus.dev/adrs/adr-0056/\"));\n// the kind's index is the entity's route with the last segment removed\nassert!(view.route.starts_with(&view.kind_route));\n```",
  "properties": {
    "content": {
      "description": "The file as read.",
      "type": "string"
    },
    "description": {
      "description": "The one-line description, when the kind holds one.",
      "type": [
        "string",
        "null"
      ]
    },
    "documentation": {
      "anyOf": [
        {
          "$ref": "#/$defs/EntityDocumentation"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Its public documentation page, derived from the repository's publication declaration\n(`site/data/publication.toml`) and the site's `base_url` by the same function the site\ngenerator writes the page with. Null when the repository declares no publication or\ndoes not declare this kind; a kind declared as not published carries the reason."
    },
    "evidence": {
      "$ref": "#/$defs/EntityEvidence",
      "description": "What can be said about its enforcement without running anything."
    },
    "id": {
      "description": "The capability id, `<kind>.<identity>`.",
      "type": "string"
    },
    "identity": {
      "description": "The identity within the kind.",
      "type": "string"
    },
    "kind": {
      "description": "The kind.",
      "type": "string"
    },
    "kind_route": {
      "description": "The route of its kind's index.",
      "type": "string"
    },
    "media_type": {
      "description": "IANA media type of `content`.",
      "type": "string"
    },
    "metadata": {
      "description": "The parsed front matter or YAML, keys in the file's order."
    },
    "provenance": {
      "$ref": "#/$defs/ObjectProvenance",
      "description": "Where it came from."
    },
    "relations": {
      "description": "The references it declares, and the references that resolve to it.",
      "items": {
        "$ref": "#/$defs/EntityEdge"
      },
      "type": "array"
    },
    "route": {
      "description": "Its route in the Cockpit.",
      "type": "string"
    },
    "slug": {
      "description": "The last segment of its route, derived from the identity.",
      "type": "string"
    },
    "surfaces": {
      "description": "Every surface that answers for it.",
      "items": {
        "$ref": "#/$defs/EntitySurface"
      },
      "type": "array"
    },
    "title": {
      "description": "The title, when the kind's title rule found one.",
      "type": [
        "string",
        "null"
      ]
    },
    "uri": {
      "description": "`majordomus://<kind>/<identity>`.",
      "type": "string"
    }
  },
  "required": [
    "uri",
    "id",
    "kind",
    "identity",
    "slug",
    "route",
    "kind_route",
    "provenance",
    "metadata",
    "media_type",
    "content",
    "relations",
    "surfaces",
    "evidence"
  ],
  "title": "EntityView",
  "type": "object"
}

Policies

benchmark
required — a target on every transport the exposure declares; the cases are the input type's
cache
disabled — every call runs the handler

Benchmark targets

Derived from the registry for this repository: one requirement per transport, and the cases the input type provides. The whole matrix is on the benchmarks page.

transportstatecasestargets
directcovered1first-object
mcpcovered1first-object
httpcovered1first-object

Searches the titles and descriptions of every page this site publishes.