Skip to content
Cockpit0.29.0
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

query
A query. It reads and changes nothing.

This capability takes no input.

GET /api/v1/knowledge/candidates

Projections

InterfaceAsWhere
MCP toolmajordomus_knowledge_candidates-
MCP resourcemajordomus://knowledge-candidates-
HTTPGET /api/v1/knowledge/candidatesSwagger
Command linemajordomus 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
{}