lifecycle.providers
Per provider: the lifecycle events its adapter declares in the provider's own vocabulary, whether it can archive prompts, and which of this repository's enforcement entries name its hook. Declared in share/providers.yaml and in the policy; never inferred from whether a shim happens to be on disk.
Per provider: the lifecycle events its adapter declares in the provider's own vocabulary, whether it can archive prompts, and which of this repository's enforcement entries name its hook. Declared in share/providers.yaml and in the policy; never inferred from whether a shim happens to be on disk.
- query
- behaviorally_verified
- module lifecycle
- #continuity
- #session
- #provider
Exposure
Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.
| surface | as |
|---|---|
| MCP | tool majordomus_lifecycle_providers |
| HTTP | GET /api/v1/lifecycle/providers operationId lifecycle.providers |
| 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 · ProviderLifecycles
with_lifecycle is a count over providers rather than a second list, so the two
cannot disagree; it is there because "how many of the providers we support actually get
the automation" is the question this capability exists to answer, and a reader should not
have to fold the list to find out.
use majordomus_cli::capability::builtin::lifecycle::ProviderLifecycles;
use serde_json::json;
let p: ProviderLifecycles = serde_json::from_value(json!({
"providers": [
{"id": "claude", "title": "Claude Code", "lifecycle": ["SessionStart"],
"prompt_capture": true, "note": "adapted"},
{"id": "plain", "title": "Plain", "prompt_capture": false, "note": "not adapted"},
],
"with_lifecycle": 1,
})).unwrap();
// the count is over the list, so it is checkable against it
let counted = p.providers.iter().filter(|x| !x.lifecycle.is_empty()).count();
assert_eq!(p.with_lifecycle, counted);
assert_eq!(p.providers.len(), 2, "a provider with no adapter is still reported");
assert!(p.findings.is_empty());| property | type | required | description |
|---|---|---|---|
| findings | array | no | What a reader should know: a provider this repository wires but the distribution declares no lifecycle for, and the like. |
| providers | array | yes | The providers, in declaration order. |
| with_lifecycle | integer | yes | How many of them declare a lifecycle adapter. |
JSON Schema
{
"$defs": {
"ProviderLifecycle": {
"description": "One provider's lifecycle surface: what its adapter declares it can do, and what this\nrepository has wired to it.\n\nThe two halves come from different places and must not be confused. `lifecycle` and\n`prompt_capture` are the *distribution's* declaration — what the shipped adapter can do\nfor this provider at all — while `wired` is *this repository's* policy naming that\nprovider's hook in `wired_by`. Either can be empty with the other full, and that pairing\nis the interesting finding: a provider this repository wires but the tool ships no\nlifecycle for is a hook that will never fire.\n\nNeither half is inferred from a file on disk. Whether a shim is actually installed is\n`majordomus capture status`'s question, and this type deliberately does not answer it.\n\n```\nuse majordomus_cli::capability::builtin::lifecycle::ProviderLifecycle;\nuse serde_json::json;\n\n// a provider the tool adapts and this repository wires: both halves present\nlet full: ProviderLifecycle = serde_json::from_value(json!({\n \"id\": \"claude\",\n \"title\": \"Claude Code\",\n \"lifecycle\": [\"SessionStart\", \"SessionEnd\", \"PreCompact\"],\n \"prompt_capture\": true,\n \"client_config\": \".mcp.json\",\n \"wired\": [\"session.start\", \"session.close\"],\n \"note\": \"adapted and wired\",\n})).unwrap();\nassert!(!full.lifecycle.is_empty() && !full.wired.is_empty());\nassert!(full.prompt_capture);\n\n// and one the tool ships no adapter for: it loses the automation and none of the model,\n// because every command remains the same\nlet unadapted: ProviderLifecycle = serde_json::from_value(json!({\n \"id\": \"some-editor\",\n \"title\": \"Some Editor\",\n \"prompt_capture\": false,\n \"note\": \"no lifecycle adapter ships for it\",\n})).unwrap();\nassert!(unadapted.lifecycle.is_empty(), \"nothing will fire for it\");\nassert!(unadapted.client_config.is_empty(), \"and it reads no project-scoped config\");\n```",
"properties": {
"client_config": {
"description": "The file it reads a project-scoped MCP client configuration from, when it has one.",
"type": "string"
},
"id": {
"description": "The provider id: the bootstrap template's file stem.",
"type": "string"
},
"lifecycle": {
"description": "The lifecycle events its adapter declares, in the provider's own vocabulary\n(`SessionStart`, `SessionEnd`, `PreCompact`). Empty means the tool ships no\nlifecycle adapter for it: such a provider loses the automation and none of the\nmodel, because every command remains the same.",
"items": {
"type": "string"
},
"type": "array"
},
"note": {
"description": "What the two together mean.",
"type": "string"
},
"prompt_capture": {
"description": "Whether its adapter can archive the worker's prompts.",
"type": "boolean"
},
"title": {
"description": "The name a person knows it by.",
"type": "string"
},
"wired": {
"description": "The enforcement entries of *this repository's* policy that name this provider's hook\nin `wired_by`. Declared wiring, not a file on disk: whether a shim is installed is a\nquestion for `majordomus capture status`, which is the command that owns it.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"id",
"title",
"prompt_capture",
"note"
],
"type": "object"
}
},
"description": "Every provider the distribution ships a bootstrap for, with the lifecycle half of each.\n\n`with_lifecycle` is a count over `providers` rather than a second list, so the two\ncannot disagree; it is there because \"how many of the providers we support actually get\nthe automation\" is the question this capability exists to answer, and a reader should not\nhave to fold the list to find out.\n\n```\nuse majordomus_cli::capability::builtin::lifecycle::ProviderLifecycles;\nuse serde_json::json;\n\nlet p: ProviderLifecycles = serde_json::from_value(json!({\n \"providers\": [\n {\"id\": \"claude\", \"title\": \"Claude Code\", \"lifecycle\": [\"SessionStart\"],\n \"prompt_capture\": true, \"note\": \"adapted\"},\n {\"id\": \"plain\", \"title\": \"Plain\", \"prompt_capture\": false, \"note\": \"not adapted\"},\n ],\n \"with_lifecycle\": 1,\n})).unwrap();\n\n// the count is over the list, so it is checkable against it\nlet counted = p.providers.iter().filter(|x| !x.lifecycle.is_empty()).count();\nassert_eq!(p.with_lifecycle, counted);\nassert_eq!(p.providers.len(), 2, \"a provider with no adapter is still reported\");\nassert!(p.findings.is_empty());\n```",
"properties": {
"findings": {
"description": "What a reader should know: a provider this repository wires but the distribution\ndeclares no lifecycle for, and the like.",
"items": {
"type": "string"
},
"type": "array"
},
"providers": {
"description": "The providers, in declaration order.",
"items": {
"$ref": "#/$defs/ProviderLifecycle"
},
"type": "array"
},
"with_lifecycle": {
"description": "How many of them declare a lifecycle adapter.",
"format": "uint",
"minimum": 0,
"type": "integer"
}
},
"required": [
"providers",
"with_lifecycle"
],
"title": "ProviderLifecycles",
"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, 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.
| transport | state | cases | targets |
|---|---|---|---|
| direct | covered | 1 | default cold+warm |
| mcp | covered | 1 | default cold+warm |
| http | covered | 1 | default cold+warm |
Claims implemented beside it
Attached by the file that implements each claim; every one links to its page with the implementation and the test that proves it.