intents.propose
Propose an intent: what must become true
What it is
Write a new intent record under `.ai/repo/project/intents/` from a statement of what must become true, with an optional title (the statement's first line when absent) and id (the title's slug when absent). The record names no milestone, criterion or invariant: its stage derives as `declared`, `intent validate` reports the two absences as warnings rather than failures, and no work can serve it until it is planned (ADR 0144). Nothing about its standing is stored. An existing record is refused, never overwritten, and the file appears whole or not at all. The answer names the identity and the file; `intents.record` answers the derived stage once the index holds it.
Descriptor
- Canonical id
intents.propose- Module
intents- Kind
- command
- Stability
- experimental
- Provenance
- declared
- Source
apps/majordomus-cli/src/capability/builtin/intents.rs- Cache
disabled- Benchmark
waived: destructive- Tags
- intentproject
Run
commandProjections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_intent_propose | - |
| MCP resource | not exposed | - |
| HTTP | POST /api/v1/intents/propose | Swagger |
| Command line | majordomus intent propose | - |
Schemas
Input · IntentProposeInput
{
"type": "object",
"additionalProperties": false,
"properties": {
"statement": {
"type": "string",
"description": "What must become true, in the proposer's words; kept verbatim."
},
"title": {
"type": [
"string",
"null"
],
"description": "One line naming it; the statement's first line when absent."
},
"id": {
"type": [
"string",
"null"
],
"description": "The identity and file name; the title's slug when absent."
}
},
"required": [
"statement"
],
"description": "The input of `intents.propose`: what must become true, and optionally its title and id.\n\nNo milestone, criterion or invariant: a proposal says what is wanted, and planning it is a\nseparate act (ADR 0144). No timestamp either: the record is dated when it is written.\n\n```\nuse majordomus_cli::capability::builtin::intents::IntentProposeInput;\nlet i: IntentProposeInput =\n serde_json::from_str(r#\"{\"statement\": \"The UI answers within 100 ms warm\"}\"#).unwrap();\nassert_eq!(i.title, None);\n// a field the input does not have is refused, never ignored\nassert!(serde_json::from_str::<IntentProposeInput>(r#\"{\"statement\": \"s\", \"stage\": \"x\"}\"#).is_err());\n```",
"title": "IntentProposeInput"
}Output · Proposed
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The identity, which is also the file name."
},
"path": {
"type": "string",
"description": "Repository-relative path of the record written."
},
"title": {
"type": "string",
"description": "One line."
},
"statement": {
"type": "string",
"description": "What must become true, as it was given."
}
},
"required": [
"id",
"path",
"title",
"statement"
],
"description": "What `propose` wrote: the identity, the file, and the record as it now reads.\n\n```\nuse majordomus_cli::intent::Proposed;\nlet p: Proposed = serde_json::from_str(\n r#\"{\"id\": \"fast-ui\", \"path\": \".ai/repo/project/intents/fast-ui.yaml\",\n \"title\": \"Fast UI\", \"statement\": \"The UI answers at once\"}\"#,\n)\n.unwrap();\n// the path is the identity under the intents directory: one answer to where it lives\nassert_eq!(p.path, format!(\".ai/repo/project/intents/{}.yaml\", p.id));\n```",
"title": "Proposed"
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
a sentence
{
"statement": "Every page of the UI answers within 100 ms when warm"
}