A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live
knowledge_base.candidates
Candidates awaiting review
What it is
Every knowledge record with status candidate under the candidates source class: its class, its assertion, the episode it came from and that episode's branch, and how long it has waited — measured from the derivation that wrote it, then the commit that added it, then its own date — judged against session.freshness; against the policy's cap. A candidate whose episode this checkout does not know is reported with no branch rather than hidden. Ordered by id.
Descriptor
- Canonical id
knowledge_base.candidates- Module
knowledge_base- Kind
- query
- Stability
- behaviorallyverified
- Provenance
- declared
- Source
apps/majordomus-cli/src/capability/builtin/knowledge_base.rs- Cache
disabled- Benchmark
required- Tags
- knowledgecontinuity
Run
queryA query. It reads and changes nothing.
Projections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_knowledge_candidates | - |
| MCP resource | majordomus://knowledge-candidates | - |
| HTTP | GET /api/v1/knowledge/candidates | Swagger |
| Command line | majordomus knowledge candidates | - |
Schemas
Input · Empty
{
"type": "object",
"additionalProperties": false,
"description": "No input.",
"title": "Empty"
}Output · KnowledgeCandidates
{
"type": "object",
"properties": {
"total": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "How many records carry `status: candidate` under the candidates class."
},
"on_this_branch": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "How many of them came from an episode on this checkout's branch."
},
"unattributed": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "How many name an episode this checkout knows nothing about."
},
"branch": {
"type": "string",
"description": "The branch this answer is about, or `DETACHED`."
},
"candidates": {
"type": "array",
"items": {
"$ref": "#/$defs/KnowledgeCandidate"
},
"description": "Every candidate, in canonical order of id."
},
"groups": {
"type": "array",
"items": {
"$ref": "#/$defs/KnowledgeGroup"
},
"description": "The candidates and rejected records derived from observations, grouped by what they\nare about, in order of subject."
},
"cap": {
"type": [
"integer",
"null"
],
"format": "uint",
"minimum": 0,
"description": "`knowledge.candidates_max_files`, when the policy declares it."
},
"over_cap": {
"type": "boolean",
"description": "True when `total` exceeds `cap`. False when no cap is declared: nobody wrote one down."
},
"findings": {
"type": "array",
"items": {
"type": "string"
},
"description": "What a reader should know before trusting the above: a malformed record that was\nskipped, a cap that was exceeded, a policy that declares none. Empty is the good case."
}
},
"required": [
"total",
"on_this_branch",
"unattributed",
"branch",
"over_cap"
],
"description": "The candidates awaiting review, against the policy's cap.\n\n```\nuse majordomus_cli::capability::builtin::knowledge_base::KnowledgeCandidates;\nlet empty: KnowledgeCandidates = serde_json::from_str(\n r#\"{\"total\":0,\"on_this_branch\":0,\"unattributed\":0,\"branch\":\"master\",\"candidates\":[],\"over_cap\":false}\"#,\n).unwrap();\nassert_eq!(empty.total, 0, \"an empty queue is an answer\");\nassert_eq!(empty.cap, None, \"and a policy without the key declares no cap\");\n```",
"title": "KnowledgeCandidates",
"$defs": {
"KnowledgeCandidate": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The record id, which is also its file name."
},
"uri": {
"type": "string",
"description": "`majordomus://knowledge/<id>`."
},
"path": {
"type": "string",
"description": "Repository-relative path."
},
"class": {
"type": "string",
"description": "`fact`, `convention`, `constraint`, `memory` or `lesson`."
},
"status": {
"type": "string",
"description": "Always `candidate` here; carried so that a reader sees the same record shape as `record`."
},
"epistemics": {
"type": "string",
"description": "`observed`, `inferred` or `decided`."
},
"title": {
"type": "string",
"description": "The assertion, in one line."
},
"date": {
"type": "string",
"description": "The day of the evidence, `YYYY-MM-DD`."
},
"derived_from": {
"type": "array",
"items": {
"type": "string"
},
"description": "The typed references the record names as its evidence, as declared."
},
"episode": {
"type": "string",
"description": "The episode the record came from: the `session:` reference, or empty when it names none."
},
"branch": {
"type": [
"string",
"null"
],
"description": "The branch that episode ran on, joined to the tracked session record or, while that\nis not yet written, to the ledger's `session.started` line. `None` when this checkout\nknows nothing about the episode — named rather than hidden, so that a reader can tell\n\"not this branch\" from \"not known here\". Serialised as `null` in that case, never\nomitted.",
"default": null
},
"freshness": {
"$ref": "#/$defs/Freshness",
"description": "How long it has waited, judged against `session.freshness`."
},
"age_minutes": {
"type": [
"integer",
"null"
],
"format": "int64",
"description": "The wait in whole minutes, when it could be measured."
},
"freshness_reason": {
"type": "string",
"description": "Why it was judged that way, in one phrase. Never empty."
},
"age_source": {
"$ref": "#/$defs/AgeSource",
"description": "Where the wait was read from."
},
"about": {
"type": "string",
"description": "What the observation it was derived from is about (ADR 0118); empty for a record\nderived from anything else."
},
"route": {
"type": "string",
"description": "Which owner should look first, derived from `about`'s structure; empty without one."
}
},
"required": [
"id",
"uri",
"path",
"class",
"status",
"epistemics",
"title",
"date",
"freshness",
"freshness_reason",
"age_source"
],
"description": "One candidate record awaiting review: what a reader needs to decide whether to open it.\n\n```\nuse majordomus_cli::capability::builtin::knowledge_base::KnowledgeCandidate;\nlet c: KnowledgeCandidate = serde_json::from_str(r#\"{\n \"id\": \"e1-0123456789ab\", \"uri\": \"majordomus://knowledge/e1-0123456789ab\",\n \"path\": \".ai/repo/knowledge/candidates/e1-0123456789ab.md\", \"class\": \"convention\",\n \"status\": \"candidate\", \"epistemics\": \"decided\", \"title\": \"Tests run in disposable repositories\",\n \"date\": \"2026-09-12\", \"derived_from\": [\"session:e1\"], \"episode\": \"e1\",\n \"freshness\": \"unknown\", \"freshness_reason\": \"no timestamp\", \"age_source\": \"date\"\n}\"#).unwrap();\nassert_eq!(c.episode, \"e1\");\nassert_eq!(c.branch, None, \"an episode this checkout never saw has no branch, and says so\");\n```"
},
"Freshness": {
"oneOf": [
{
"type": "string",
"const": "fresh",
"description": "Inside `session.freshness.fresh_minutes`. Current; act on it."
},
{
"type": "string",
"const": "aging",
"description": "Between the two thresholds. Still current, but its age is worth stating."
},
{
"type": "string",
"const": "stale",
"description": "At or beyond `session.freshness.stale_minutes`. History, never current."
},
{
"type": "string",
"const": "unknown",
"description": "Nothing to judge it by: no timestamp, or no thresholds declared."
},
{
"type": "string",
"const": "invalid",
"description": "A timestamp that does not parse, or one in the future."
}
],
"description": "How old a record is, judged against `session.freshness` in the policy.\n\nThis is the second of two independent judgements every record carries, and it exists\nbecause the first one cannot answer this question. `Divergence` says where a record's\ncommit sits relative to HEAD; `advanced` means that commit is an ancestor of this one,\nwhich sounds like agreement and is identically true on the day the record is written and\na month later. Between 2026-09-05 and 2026-09-11 this repository served a handover that\nwas `advanced` and six days dead, and every surface that read this model — MCP, the HTTP\nAPI, the Cockpit — presented its `Next Action` as the thing to do (ADR 0052).\n\nThe thresholds are policy and are read, never restated here: a constant in this file\nwould be a second source of truth for a number the policy owns, and a repository whose\npolicy predates the key is reported as `Freshness::Unknown` naming the missing key\nrather than judged against a default nobody declared.\n\nThe five words divide into two groups, and the division is the one a caller acts on:\n`fresh` and `aging` are current and may be presented as the instruction, `stale` and\n`invalid` must be shown as history, and `unknown` is neither — a record nobody can date\nis not thereby an old record, and treating it as one would quietly discard a briefing\nthat is perfectly good. `Freshness::history` is that division, and it is the only\nplace it is decided.\n\n```\nuse majordomus_cli::capability::builtin::continuity::Freshness;\n\n// the two renderings are one value: the serialised word is the word `as_str` gives,\n// so a reader who met `stale` from MCP and from the command line met one fact\nlet stale = serde_json::to_value(Freshness::Stale).unwrap();\nassert_eq!(stale, serde_json::json!(\"stale\"));\nassert_eq!(stale.as_str(), Some(Freshness::Stale.as_str()));\n\n// and the division a caller acts on: absence of a judgement is not a bad judgement\nassert!(Freshness::Stale.history() && Freshness::Invalid.history());\nassert!(!Freshness::Unknown.history(), \"undated is not old\");\nassert!(!Freshness::Fresh.history() && !Freshness::Aging.history());\n```"
},
"AgeSource": {
"oneOf": [
{
"type": "string",
"const": "derivation",
"description": "The oldest `knowledge.derived` line in this checkout's ledger whose paths name it."
},
{
"type": "string",
"const": "commit",
"description": "The commit that added the file to this history."
},
{
"type": "string",
"const": "date",
"description": "The record's own `date`, at midnight UTC: the evidence day, not the queue day."
}
],
"description": "Where the age of a candidate in the review queue was read from. A candidate's `date` is\nthe day of the evidence it came from, deliberately, so that the same evidence yields the\nsame bytes; the day it entered the queue is a different fact and is read from the ledger\nfirst, then from git, and only then from the record.\n\n```\nuse majordomus_cli::capability::builtin::knowledge_base::AgeSource;\nlet from_ledger: AgeSource = serde_json::from_str(r#\"\"derivation\"\"#).unwrap();\nassert_eq!(from_ledger, AgeSource::Derivation);\nassert!(AgeSource::Derivation < AgeSource::Date, \"the ledger is asked before the record\");\n```"
},
"KnowledgeGroup": {
"type": "object",
"properties": {
"about": {
"type": "string",
"description": "The subject, as written."
},
"route": {
"type": "string",
"description": "The route of its records."
},
"candidates": {
"type": "array",
"items": {
"type": "string"
},
"description": "The candidates awaiting review, by id."
},
"rejected": {
"type": "array",
"items": {
"type": "string"
},
"description": "The records rejected and kept, by id."
},
"episodes": {
"type": "array",
"items": {
"type": "string"
},
"description": "The episodes behind all of them, each once, in order."
}
},
"required": [
"about",
"route"
],
"description": "The records under one subject: every candidate awaiting review and every rejected one,\nwith the episodes behind them (ADR 0118). Recurrence is read here, never written: each\nrecord stays one episode's own file, and a group is how several of them read as one thing.\n\n```\nuse majordomus_cli::capability::builtin::knowledge_base::KnowledgeGroup;\nlet g: KnowledgeGroup = serde_json::from_str(\n r#\"{\"about\":\"lib/a.sh\",\"route\":\"project\",\"candidates\":[\"e1-a\",\"e2-b\"],\"episodes\":[\"e1\",\"e2\"]}\"#,\n).unwrap();\nassert_eq!(g.episodes.len(), 2, \"two episodes observed it\");\nassert!(g.rejected.is_empty(), \"and nobody rejected it\");\n```"
}
}
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
default
{}