entity.kinds
The kinds the layer holds, each with the route of its index and how many objects it carries, and every route collision there is. A collision is the only way an object of the index can fail to have an address of its own; the healthy answer is none.
The kinds the layer holds, each with the route of its index and how many objects it carries, and every route collision there is. A collision is the only way an object of the index can fail to have an address of its own; the healthy answer is none.
- query
- implemented
- module entity
- #objects
- #governance
Exposure
Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.
| surface | as |
|---|---|
| MCP | tool majordomus_kinds |
| HTTP | GET /api/v1/entity/kinds operationId entity.kinds |
| command line | majordomus entity kinds |
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 · Empty
No properties: the capability takes no input.
JSON Schema
{
"additionalProperties": false,
"description": "No input.",
"title": "Empty",
"type": "object"
}output · KindList
The two numbers are what scripts/ci/entity-check compares: the objects the index holds
against the objects that have an address. They are reported together, in one answer, so
that the gate asks one question instead of joining two lists and deciding for itself
what the difference means.
use majordomus_cli::capability::builtin::entity::KindList;
let healthy: KindList = serde_json::from_value(serde_json::json!({
"count": 1,
"kinds": [{ "kind": "adr", "count": 52, "route": "/cockpit/objects/adr" }],
"routable": 52,
}))
.unwrap();
let held: usize = healthy.kinds.iter().map(|k| k.count).sum();
// every object addressable, and no collision: the only state the gate accepts
assert_eq!(held, healthy.routable);
assert!(healthy.collisions.is_empty());| property | type | required | description |
|---|---|---|---|
| collisions | array | no | Two objects of one kind whose identities reduce to one route, which is the only way
an object can fail to be addressable. Empty is the healthy answer and the only one
scripts/ci/entity-check accepts. |
| count | integer | yes | How many kinds. |
| kinds | array | yes | The kinds, in the index's order. |
| routable | integer | yes | How many objects have a route. Every object does, unless it is in collisions. |
JSON Schema
{
"$defs": {
"KindEntry": {
"description": "One kind of the layer, with its address and how many objects it holds.\n\n`example` carries one of the kind's objects so that a catalogue of kinds is somewhere a\nreader can go from rather than a list of words: a kind with a count and no way in is a\ndead row. It is `None` only for a kind the index holds no object of.\n\n```\nuse majordomus_cli::capability::builtin::entity::KindEntry;\n\nlet e: KindEntry = serde_json::from_value(serde_json::json!({\n \"kind\": \"adr\",\n \"count\": 52,\n \"route\": \"/cockpit/objects/adr\",\n}))\n.unwrap();\n\nassert_eq!(e.kind, \"adr\");\n// the route is the kind's, derived — never a per-kind entry in a table of routes\nassert_eq!(e.route, majordomus_cli::entity::kind_route(&e.kind));\nassert!(e.example.is_none());\n```",
"properties": {
"count": {
"description": "How many objects of it the index holds.",
"format": "uint",
"minimum": 0,
"type": "integer"
},
"example": {
"anyOf": [
{
"$ref": "#/$defs/ObjectSummary"
},
{
"type": "null"
}
],
"description": "One of its objects, so a reader has somewhere to go."
},
"kind": {
"description": "The kind.",
"type": "string"
},
"route": {
"description": "Where its index is served.",
"type": "string"
}
},
"required": [
"kind",
"count",
"route"
],
"type": "object"
},
"ObjectSummary": {
"description": "One object, summarised for a listing.",
"properties": {
"description": {
"description": "The one-line description, when the kind holds one.",
"type": [
"string",
"null"
]
},
"id": {
"description": "The capability id, `<kind>.<identity>`.",
"type": "string"
},
"identity": {
"description": "The identity within the kind.",
"type": "string"
},
"kind": {
"description": "The kind the object was read as.",
"type": "string"
},
"path": {
"description": "Repository-relative source path.",
"type": "string"
},
"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",
"path"
],
"type": "object"
},
"RouteCollision": {
"description": "Two objects of one kind whose identities reduce to the same route.\n\nCarries both identities and both files, because the reader who has to fix it needs to\nknow which two things collided and where they are written — a count would tell them\nonly that the tree is refused. `correction` is the sentence `collisions` wrote about\nthis particular pair.\n\n```\n# use majordomus_cli::{Object, Provenance};\n# fn object(kind: &str, identity: &str) -> Object {\n# Object { kind: kind.into(), identity: identity.into(),\n# uri: format!(\"majordomus://{kind}/{identity}\"), title: None, description: None,\n# metadata: serde_json::json!({}), body: String::new(), content: String::new(),\n# media_type: \"text/markdown\",\n# provenance: Provenance { path: format!(\"{identity}.md\"), directory: \"x\".into(),\n# source_class: kind.into(), section: None, bytes: 0, member: None } }\n# }\nuse majordomus_cli::entity::{collisions, Collision};\n\n// `a.b` and `a-b` are two identities and one slug\nlet objects = vec![object(\"rule\", \"a.b\"), object(\"rule\", \"a-b\")];\nlet found: Vec<Collision> = collisions(&objects);\n\nassert_eq!(found.len(), 1);\nassert_eq!(found[0].kind, \"rule\");\nassert_eq!(found[0].slug, \"a-b\");\nassert_eq!(found[0].identities, [\"a.b\", \"a-b\"]);\nassert!(!found[0].correction.is_empty());\n```",
"properties": {
"correction": {
"description": "What to do about it.",
"type": "string"
},
"identities": {
"description": "The identities that collide, in index order.",
"items": {
"type": "string"
},
"type": "array"
},
"kind": {
"description": "The kind they share.",
"type": "string"
},
"paths": {
"description": "The files that declared them.",
"items": {
"type": "string"
},
"type": "array"
},
"slug": {
"description": "The slug they both reduce to; empty when an identity reduces to nothing.",
"type": "string"
}
},
"required": [
"kind",
"slug",
"identities",
"paths",
"correction"
],
"type": "object"
}
},
"description": "Every kind of the layer, each with its route, and every route collision there is.\n\nThe two numbers are what `scripts/ci/entity-check` compares: the objects the index holds\nagainst the objects that have an address. They are reported together, in one answer, so\nthat the gate asks one question instead of joining two lists and deciding for itself\nwhat the difference means.\n\n```\nuse majordomus_cli::capability::builtin::entity::KindList;\n\nlet healthy: KindList = serde_json::from_value(serde_json::json!({\n \"count\": 1,\n \"kinds\": [{ \"kind\": \"adr\", \"count\": 52, \"route\": \"/cockpit/objects/adr\" }],\n \"routable\": 52,\n}))\n.unwrap();\n\nlet held: usize = healthy.kinds.iter().map(|k| k.count).sum();\n// every object addressable, and no collision: the only state the gate accepts\nassert_eq!(held, healthy.routable);\nassert!(healthy.collisions.is_empty());\n```",
"properties": {
"collisions": {
"description": "Two objects of one kind whose identities reduce to one route, which is the only way\nan object can fail to be addressable. Empty is the healthy answer and the only one\n`scripts/ci/entity-check` accepts.",
"items": {
"$ref": "#/$defs/RouteCollision"
},
"type": "array"
},
"count": {
"description": "How many kinds.",
"format": "uint",
"minimum": 0,
"type": "integer"
},
"kinds": {
"description": "The kinds, in the index's order.",
"items": {
"$ref": "#/$defs/KindEntry"
},
"type": "array"
},
"routable": {
"description": "How many objects have a route. Every object does, unless it is in `collisions`.",
"format": "uint",
"minimum": 0,
"type": "integer"
}
},
"required": [
"count",
"kinds",
"routable"
],
"title": "KindList",
"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.