Skip to content

worktree.uncommitted

Every linked worktree whose index or working tree holds changes no commit carries: how many paths (staged, unstaged, untracked, conflicted), when the oldest change was made (the earliest modification time among the changed paths, the index file's standing in for a deleted one), and what holds the work — an active task record, an open episode that is not stranded, or a process whose working directory is inside. Work nothing holds and older than the threshold is `abandoned`, with the command that would put it on a remote `rescue/` ref; it is suggested, never run. The threshold is the policy's `session.stranded_after` unless `older_than` names another. Reads every worktree and writes none: every status runs without optional locks, so no index is refreshed.

Every linked worktree whose index or working tree holds changes no commit carries: how many paths (staged, unstaged, untracked, conflicted), when the oldest change was made (the earliest modification time among the changed paths, the index file's standing in for a deleted one), and what holds the work — an active task record, an open episode that is not stranded, or a process whose working directory is inside. Work nothing holds and older than the threshold is `abandoned`, with the command that would put it on a remote `rescue/` ref; it is suggested, never run. The threshold is the policy's `session.stranded_after` unless `older_than` names another. Reads every worktree and writes none: every status runs without optional locks, so no index is refreshed.

  • query
  • behaviorally_verified
  • module worktree
  • #worktree
  • #git
  • #continuity
  • #introspection

Exposure

Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.

surfaceas
MCPtool majordomus_worktree_uncommitted
HTTPGET /api/v1/worktrees/uncommitted operationId worktree.uncommitted
command linemajordomus worktree uncommitted

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 · UncommittedInput

What to judge uncommitted work against.
propertytyperequireddescription
older_than string | null no The age past which work nothing holds is abandoned, as a person writes it (90s, 30m, 12h, 1d). Default: the policy's session.stranded_after, which is also how long an open episode may be silent and still hold the work.
JSON Schema
{
  "additionalProperties": false,
  "description": "What to judge uncommitted work against.",
  "properties": {
    "older_than": {
      "description": "The age past which work nothing holds is abandoned, as a person writes it (`90s`,\n`30m`, `12h`, `1d`). Default: the policy's `session.stranded_after`, which is also\nhow long an open episode may be silent and still hold the work.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "title": "UncommittedInput",
  "type": "object"
}

output · UncommittedReport

Every linked work tree holding uncommitted work, with what holds it: the answer of worktree.uncommitted on every surface, and what doctor renders.

use majordomus_cli::worktree::uncommitted::UNCOMMITTED_SCHEMA;
use majordomus_cli::worktree::{Threshold, UncommittedReport, WorktreeService};
let dir = tempfile::tempdir().unwrap();
let init = std::process::Command::new("git")
    .args(["init", "-q"])
    .current_dir(dir.path())
    .status()
    .unwrap();
assert!(init.success());
// a repository with no linked work tree, and a policy that declares no threshold
let threshold = Threshold::resolve(None, None).unwrap();
let report: UncommittedReport =
    WorktreeService::open(dir.path()).unwrap().uncommitted(&threshold).unwrap();
assert_eq!(report.schema, UNCOMMITTED_SCHEMA);
assert!(report.worktrees.is_empty());
assert_eq!(report.tallies.measured, 0);
assert_eq!(report.findings.len(), 1, "a missing threshold is said, not assumed");
propertytyperequireddescription
findings array yes What could not be read or judged, in words.
observed_at string yes When the reading was taken, RFC 3339 in UTC.
schema string yes UNCOMMITTED_SCHEMA.
tallies reference yes The counts.
threshold reference yes The threshold it was judged against.
worktrees array yes Every linked work tree with uncommitted work, abandoned first, then oldest first.
JSON Schema
{
  "$defs": {
    "DirtyState": {
      "description": "Uncommitted work in a work tree, counted from `git status --porcelain`. Untracked\ncontent counts: it is exactly what a careless move loses.",
      "properties": {
        "clean": {
          "description": "Nothing above is non-zero.",
          "type": "boolean"
        },
        "conflicted": {
          "description": "Entries with an unresolved merge conflict.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "in_progress": {
          "description": "A merge, rebase, cherry-pick, revert or bisect is in progress here.",
          "type": [
            "string",
            "null"
          ]
        },
        "staged": {
          "description": "Entries with a change in the index.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "unstaged": {
          "description": "Entries with a change in the work tree that is not in the index.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "untracked": {
          "description": "Untracked, not ignored, files.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "staged",
        "unstaged",
        "untracked",
        "conflicted",
        "clean"
      ],
      "type": "object"
    },
    "OpenEpisode": {
      "description": "An open episode in a work tree, as far as holding its work goes: it holds the work only\nwhile `stranded` is `Some(false)`; a stranded or unjudgeable one holds nothing.\n\n```\nuse majordomus_cli::worktree::OpenEpisode;\nlet unjudged = OpenEpisode {\n    id: \"s-20260101000000-0a0a\".into(),\n    last_sign_of_life: None,\n    stranded: None,\n};\nlet wire = serde_json::to_value(&unjudged).unwrap();\nassert!(wire.get(\"stranded\").is_none(), \"what could not be judged is absent, not false\");\nlet back: OpenEpisode = serde_json::from_value(wire).unwrap();\nassert_eq!(back, unjudged);\n```",
      "properties": {
        "id": {
          "description": "The episode.",
          "type": "string"
        },
        "last_sign_of_life": {
          "description": "Its newest sign of life, RFC 3339 in UTC.",
          "type": [
            "string",
            "null"
          ]
        },
        "stranded": {
          "description": "Silent longer than the threshold. Absent when that cannot be judged — no threshold,\nor no readable sign of life — and then it holds nothing.",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "required": [
        "id"
      ],
      "type": "object"
    },
    "Threshold": {
      "description": "The age past which uncommitted work nothing holds is abandoned, and where it came from.\n\nIt is reported with every reading, so a reader can see what the verdicts were judged\nagainst; a threshold nothing declares is carried as absent, not filled in.\n\n```\nuse majordomus_cli::worktree::Threshold;\nlet declared = Threshold::resolve(Some(\"12h\"), None).unwrap();\nlet wire = serde_json::to_value(&declared).unwrap();\nassert_eq!(wire[\"declared\"], \"12h\");\nassert_eq!(wire[\"seconds\"], 43_200);\nlet absent: Threshold =\n    serde_json::from_value(serde_json::json!({\"source\": \"absent\"})).unwrap();\nassert_eq!((absent.declared, absent.seconds), (None, None), \"absent is not a default\");\n```",
      "properties": {
        "declared": {
          "description": "The value as written (`12h`). Absent when nothing declares one.",
          "type": [
            "string",
            "null"
          ]
        },
        "seconds": {
          "description": "The same value, in seconds.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "source": {
          "description": "`THRESHOLD_KEY`, `older_than` when the caller named one, or `absent`.",
          "type": "string"
        }
      },
      "required": [
        "source"
      ],
      "type": "object"
    },
    "UncommittedTallies": {
      "description": "The counts of an `UncommittedReport`: how many linked work trees were read, how many\nhold uncommitted work, and how those split into held, recent and abandoned. The three\nstandings always add up to `uncommitted`; `doctor` reads `abandoned` to decide whether it\nhas anything to warn about.\n\n```\nuse majordomus_cli::worktree::UncommittedTallies;\nlet wire = serde_json::json!(\n    {\"measured\": 4, \"uncommitted\": 3, \"held\": 1, \"recent\": 1, \"abandoned\": 1}\n);\nlet t: UncommittedTallies = serde_json::from_value(wire.clone()).unwrap();\nassert_eq!(t.held + t.recent + t.abandoned, t.uncommitted);\nassert_eq!(serde_json::to_value(&t).unwrap(), wire, \"the shape doctor's jq reads\");\nassert_eq!(UncommittedTallies::default().abandoned, 0, \"nothing read, nothing abandoned\");\n```",
      "properties": {
        "abandoned": {
          "description": "Held by nothing, older than the threshold.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "held": {
          "description": "Held by a task, an episode or a process.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "measured": {
          "description": "Linked work trees whose status was read.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "recent": {
          "description": "Held by nothing, younger than the threshold.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "uncommitted": {
          "description": "Of those, the ones holding uncommitted work: every entry of the listing.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "measured",
        "uncommitted",
        "held",
        "recent",
        "abandoned"
      ],
      "type": "object"
    },
    "UncommittedWorktree": {
      "description": "One linked work tree holding uncommitted work: how much, how old, what holds it, the\nverdict, and the command that would preserve it. A listing is in canonical order —\nabandoned first, then recent, then held, the oldest first inside each.\n\n```\nuse majordomus_cli::worktree::{DirtyState, UncommittedWorktree, WorkStanding};\nlet entry = |path: &str, standing: WorkStanding, age: u64| UncommittedWorktree {\n    path: path.into(),\n    branch: None,\n    label: path.into(),\n    work: DirtyState { staged: 1, ..DirtyState::default() },\n    paths: 1,\n    oldest_change: None,\n    age_seconds: Some(age),\n    age: None,\n    task: None,\n    episodes: Vec::new(),\n    process_inside: None,\n    held_by: Vec::new(),\n    standing,\n    remedy: String::new(),\n};\nlet mut listing = vec![\n    entry(\"/wt/held-old\", WorkStanding::Held, 900_000),\n    entry(\"/wt/young\", WorkStanding::Abandoned, 50_000),\n    entry(\"/wt/older\", WorkStanding::Abandoned, 90_000),\n];\nmajordomus_cli::order::canonical(&mut listing);\nlet paths: Vec<&str> = listing.iter().map(|w| w.path.as_str()).collect();\nassert_eq!(paths, [\"/wt/older\", \"/wt/young\", \"/wt/held-old\"]);\n```",
      "properties": {
        "age": {
          "description": "The same, for a person (`3d`, `5h`, `40m`).",
          "type": [
            "string",
            "null"
          ]
        },
        "age_seconds": {
          "description": "Seconds since then.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "branch": {
          "description": "The branch checked out there. Absent when detached.",
          "type": [
            "string",
            "null"
          ]
        },
        "episodes": {
          "description": "The episodes open there.",
          "items": {
            "$ref": "#/$defs/OpenEpisode"
          },
          "type": "array"
        },
        "held_by": {
          "description": "What holds it, in words; empty when nothing does.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "label": {
          "description": "The branch, or `detached/<short commit>`.",
          "type": "string"
        },
        "oldest_change": {
          "description": "When the oldest change was made: the earliest modification time among the changed\npaths, the index file's standing in for a path that no longer exists. RFC 3339.",
          "type": [
            "string",
            "null"
          ]
        },
        "path": {
          "description": "Absolute, as git holds it.",
          "type": "string"
        },
        "paths": {
          "description": "Every changed path: staged, unstaged, untracked and conflicted together.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "process_inside": {
          "description": "A process has its working directory inside it. Absent when that could not be read.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "remedy": {
          "description": "The command that would put this work on a remote ref. Suggested, never run.",
          "type": "string"
        },
        "standing": {
          "$ref": "#/$defs/WorkStanding",
          "description": "The verdict."
        },
        "task": {
          "description": "The active task recorded there.",
          "type": [
            "string",
            "null"
          ]
        },
        "work": {
          "$ref": "#/$defs/DirtyState",
          "description": "The counts, as `worktree status` gives them."
        }
      },
      "required": [
        "path",
        "label",
        "work",
        "paths",
        "episodes",
        "held_by",
        "standing",
        "remedy"
      ],
      "type": "object"
    },
    "WorkStanding": {
      "description": "What a work tree's uncommitted work is, judged: held by something, recent, or abandoned.\n`judge` is the one place it is decided, and `doctor` warns only on `abandoned`.\n\n```\nuse majordomus_cli::worktree::uncommitted::{judge, WorkStanding};\nlet standing = judge(false, Some(90_000), Some(43_200));\nassert_eq!(standing, WorkStanding::Abandoned);\nassert_eq!(serde_json::to_value(standing).unwrap(), \"abandoned\");\n```",
      "oneOf": [
        {
          "const": "held",
          "description": "A task, an episode or a process holds it.",
          "type": "string"
        },
        {
          "const": "recent",
          "description": "Nothing holds it, and it is younger than the threshold.",
          "type": "string"
        },
        {
          "const": "abandoned",
          "description": "Nothing holds it, and it is older than the threshold — or its age cannot be read.",
          "type": "string"
        }
      ]
    }
  },
  "description": "Every linked work tree holding uncommitted work, with what holds it: the answer of\n`worktree.uncommitted` on every surface, and what `doctor` renders.\n\n```\nuse majordomus_cli::worktree::uncommitted::UNCOMMITTED_SCHEMA;\nuse majordomus_cli::worktree::{Threshold, UncommittedReport, WorktreeService};\nlet dir = tempfile::tempdir().unwrap();\nlet init = std::process::Command::new(\"git\")\n    .args([\"init\", \"-q\"])\n    .current_dir(dir.path())\n    .status()\n    .unwrap();\nassert!(init.success());\n// a repository with no linked work tree, and a policy that declares no threshold\nlet threshold = Threshold::resolve(None, None).unwrap();\nlet report: UncommittedReport =\n    WorktreeService::open(dir.path()).unwrap().uncommitted(&threshold).unwrap();\nassert_eq!(report.schema, UNCOMMITTED_SCHEMA);\nassert!(report.worktrees.is_empty());\nassert_eq!(report.tallies.measured, 0);\nassert_eq!(report.findings.len(), 1, \"a missing threshold is said, not assumed\");\n```",
  "properties": {
    "findings": {
      "description": "What could not be read or judged, in words.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "observed_at": {
      "description": "When the reading was taken, RFC 3339 in UTC.",
      "type": "string"
    },
    "schema": {
      "description": "`UNCOMMITTED_SCHEMA`.",
      "type": "string"
    },
    "tallies": {
      "$ref": "#/$defs/UncommittedTallies",
      "description": "The counts."
    },
    "threshold": {
      "$ref": "#/$defs/Threshold",
      "description": "The threshold it was judged against."
    },
    "worktrees": {
      "description": "Every linked work tree with uncommitted work, abandoned first, then oldest first.",
      "items": {
        "$ref": "#/$defs/UncommittedWorktree"
      },
      "type": "array"
    }
  },
  "required": [
    "schema",
    "observed_at",
    "threshold",
    "worktrees",
    "tallies",
    "findings"
  ],
  "title": "UncommittedReport",
  "type": "object"
}

Policies

benchmark
required — a target on every transport the exposure declares; the cases are the input type's
cache
disabled — every call runs the handler

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.

transportstatecasestargets
directcovered2policy-threshold
one-hour
mcpcovered2policy-threshold
one-hour
httpcovered2policy-threshold
one-hour

Searches the titles and descriptions of every page this site publishes.