session_domain.identity
The repository, the checkout, the open episode and the provider session, each with the canonical value this executable computes and each store's own spelling beside it. The stores record the repository sometimes as an absolute .git path and sometimes as a remote URL, the worktree as a path and as a digest, and the session as both an episode id and a provider UUID; this is the surface that says so, so that a reader never compares two spellings of different things and concludes they disagree.
The repository, the checkout, the open episode and the provider session, each with the canonical value this executable computes and each store's own spelling beside it. The stores record the repository sometimes as an absolute .git path and sometimes as a remote URL, the worktree as a path and as a digest, and the session as both an episode id and a provider UUID; this is the surface that says so, so that a reader never compares two spellings of different things and concludes they disagree.
- query
- behaviorally_verified
- module session_domain
- #session
- #identity
- #continuity
Exposure
Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.
| surface | as |
|---|---|
| MCP | tool majordomus_session_identity resource majordomus://session/identity |
| HTTP | GET /api/v1/session/identity operationId session_domain.identity |
| command line | — |
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 · IdentityReport
use majordomus_cli::capability::builtin::IdentityReport;
use majordomus_cli::session::identities;
let dir = tempfile::tempdir().unwrap();
let report = IdentityReport {
checkout: dir.path().display().to_string(),
facets: identities(dir.path()),
findings: Vec::new(),
};
let subjects: Vec<&str> = report.facets.iter().map(|f| f.subject.as_str()).collect();
assert_eq!(subjects, ["repository", "checkout", "episode", "provider_session"]);| property | type | required | description |
|---|---|---|---|
| checkout | string | yes | The checkout this answer is about. An absolute path, because this capability is
served to the worker in front of this checkout and never published — the same
exposure rule continuity.state follows and for the same reason (ADR 0014). |
| facets | array | yes | One entry per subject, in the order a reader meets them: repository, checkout, episode, provider session. |
| findings | array | yes | What a reader should know before comparing any two of these. Empty is not the good case here — the conflation is a property of the stores, not of this checkout — so the standing note is always present. |
JSON Schema
{
"$defs": {
"IdentityFacet": {
"description": "One identity of this checkout, as this executable computes it and as each store spells\nit. What `session.identity` answers, and the direct projection of the audit's table.\n\n```\nuse majordomus_cli::session::{identities, IdentityFacet};\n\nlet dir = tempfile::tempdir().unwrap();\nlet facets: Vec<IdentityFacet> = identities(dir.path());\nlet repository = facets.iter().find(|f| f.subject == \"repository\").expect(\"a facet\");\nassert!(repository.spellings.len() >= 2, \"one word, two writers, two values\");\n```",
"properties": {
"canonical": {
"description": "The canonical value this executable computes, when it can compute one.",
"type": "string"
},
"note": {
"description": "Why the subject needs its own type, in one line, for a reader meeting it in a\nprojection rather than in this module.",
"type": "string"
},
"spellings": {
"description": "How each store spells the same subject, in the order `crate::order` gives.",
"items": {
"$ref": "#/$defs/RecordedSpelling"
},
"type": "array"
},
"subject": {
"description": "What is being identified: `repository`, `checkout`, `episode`, `provider_session`.",
"type": "string"
}
},
"required": [
"subject",
"note"
],
"type": "object"
},
"RecordedSpelling": {
"description": "One store's spelling of one subject.\nWhich half of the layer wrote it, what wrote it, and the value it would have here.\n\n```\nuse majordomus_cli::session::{RecordedSpelling, Spelling};\n\nlet local = RecordedSpelling {\n spelling: Spelling::Local,\n writer: \"mj_git_repo_id (lib/common.sh)\".into(),\n value: \"/src/repo/.git\".into(),\n};\n// knowing which half a string came from is what stops a path being compared with a URL\nassert_eq!(local.spelling.as_str(), \"local\");\nassert!(local.value.starts_with('/'));\n```",
"properties": {
"spelling": {
"$ref": "#/$defs/Spelling",
"description": "Which half of the layer wrote it."
},
"value": {
"description": "The value, as this checkout would have it written. Empty when nothing has written\none here yet, which is a fresh clone and not a fault.",
"type": "string"
},
"writer": {
"description": "The writer, named so a reader can go and look at it.",
"type": "string"
}
},
"required": [
"spelling",
"writer"
],
"type": "object"
},
"Spelling": {
"description": "Which store a recorded identity was read from, kept beside the value.\n\nThe point is not to choose between `mj_git_repo_id` and `mj_repository_id`, or between\n`worktree:` and `worktree_id:`. It is to stop pretending they are one value. A reader\nthat knows a string is the *local* spelling can compare it with other local spellings\nand can refuse to compare it with a shared one, which is precisely the comparison that\nproduced \"these two records are about different repositories\" for records of the same\nrepository written four minutes apart.\n\n```\nuse majordomus_cli::session::Spelling;\n// the two halves are different answers to one question, and are never compared\nassert_ne!(Spelling::Local, Spelling::Shared);\nassert_eq!(Spelling::Canonical.as_str(), \"canonical\");\n```",
"oneOf": [
{
"const": "local",
"description": "The local half's spelling: a path, in `.ai/local/state/`. It names this disk and is\nnever published.",
"type": "string"
},
{
"const": "shared",
"description": "The shared half's spelling: a remote URL or a digest, in `.ai/repo/sessions/`. It\nnames the repository and travels with a clone (ADR 0014).",
"type": "string"
},
{
"const": "canonical",
"description": "This executable's own canonical digest, computed rather than read.",
"type": "string"
}
]
}
},
"description": "The identities of this checkout: one facet per subject, with a standing note about what\na reader must not do with them.\n\n```\nuse majordomus_cli::capability::builtin::IdentityReport;\nuse majordomus_cli::session::identities;\n\nlet dir = tempfile::tempdir().unwrap();\nlet report = IdentityReport {\n checkout: dir.path().display().to_string(),\n facets: identities(dir.path()),\n findings: Vec::new(),\n};\nlet subjects: Vec<&str> = report.facets.iter().map(|f| f.subject.as_str()).collect();\nassert_eq!(subjects, [\"repository\", \"checkout\", \"episode\", \"provider_session\"]);\n```",
"properties": {
"checkout": {
"description": "The checkout this answer is about. An absolute path, because this capability is\nserved to the worker in front of this checkout and never published — the same\nexposure rule `continuity.state` follows and for the same reason (ADR 0014).",
"type": "string"
},
"facets": {
"description": "One entry per subject, in the order a reader meets them: repository, checkout,\nepisode, provider session.",
"items": {
"$ref": "#/$defs/IdentityFacet"
},
"type": "array"
},
"findings": {
"description": "What a reader should know before comparing any two of these. Empty is not the good\ncase here — the conflation is a property of the stores, not of this checkout — so\nthe standing note is always present.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"checkout",
"facets",
"findings"
],
"title": "IdentityReport",
"type": "object"
}Policies
- benchmark
- required — a target on every transport the exposure declares; the cases are the input type's
- cache
- process — up to 2 entries in the process, 2s each, scoped by the registry fingerprint
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.