lifecycle.episodes
Každá otevřená epizoda v úložišti tohoto checkoutu, s relací providera, která ji vlastní, worktree a branchí, na které se otevřela, kde stojí a co kniha naposledy viděla, že dělala. `continuity.state` hlásí tu jednu epizodu, na kterou ukazuje pointer, což je správné pro briefing a slepé ke každému jinému oknu otevřenému na stejném worktree.
Každá otevřená epizoda v úložišti tohoto checkoutu, s relací providera, která ji vlastní, worktree a branchí, na které se otevřela, kde stojí a co kniha naposledy viděla, že dělala. `continuity.state` hlásí tu jednu epizodu, na kterou ukazuje pointer, což je správné pro briefing a slepé ke každému jinému oknu otevřenému na stejném worktree.
- query
- behaviorally_verified
- module lifecycle
- #continuity
- #session
- #episode
Zpřístupnění
Kde je tato jedna definice dosažitelná. Absence je explicitní: povrch, který není uveden, ji nenese.
| povrch | jako |
|---|---|
| MCP | nástroj majordomus_lifecycle_episodes zdroj majordomus://lifecycle/episodes |
| HTTP | GET /api/v1/lifecycle/episodes operationId lifecycle.episodes |
| příkazová řádka | — |
Schémata
Kanonický vstup a výstup z typů Rustu; schéma nástroje MCP a parametry a odpovědi OpenAPI se odvozují z nich.
vstup · Prázdné
Žádné vlastnosti: schopnost nebere žádný vstup.
JSON Schema
{
"additionalProperties": false,
"description": "No input.",
"title": "Empty",
"type": "object"
}output · Episodes
continuity.state dát nemůže, protože čte ukazatel, kdežto toto čte úložiště.
current je ta jedna epizoda, na kterou ukazatel míří, a je jedním řádkem episodes, ne samostatnou věcí: celá oprava, kterou tento typ nese, je, že epizoda, na kterou se ukazuje, je jedním členem množiny, ne množinou. V den, kdy to bylo napsáno, úložiště drželo pět otevřených epizod v jednom checkoutu a každý povrch mohl pojmenovat jen jednu z nich.
present: false je čerstvý klon, což není závada — nikdo zde ještě neotevřel epizodu — a hlásí se jako takový, ne jako chyba.
use majordomus_cli::capability::builtin::lifecycle::Episodes;
use serde_json::json;
// a clone in which no episode has ever been opened: absence, answered as absence
let fresh: Episodes = serde_json::from_value(json!({
"present": false,
"store": ".ai/local/state/sessions-open",
"worktree": "example/a-fresh-clone",
})).unwrap();
assert!(!fresh.present);
assert!(fresh.episodes.is_empty() && fresh.current.is_empty());
assert!(fresh.findings.is_empty(), "nothing to warn about; it is simply new");
// and a store with work in it: `current` names one of the rows, never a sixth thing
let busy: Episodes = serde_json::from_value(json!({
"present": true,
"store": ".ai/local/state/sessions-open",
"worktree": "example/wt",
"current": "s-0002",
"episodes": [
{"session_id": "s-0001", "path": "a.yaml", "standing": "open",
"events": 3, "note": "this worktree, not pointed at"},
{"session_id": "s-0002", "path": "b.yaml", "standing": "current",
"events": 9, "note": "the pointer resolves to it"},
],
})).unwrap();
assert_eq!(busy.episodes.len(), 2, "the pointer is blind to the first of these");
assert!(busy.episodes.iter().any(|e| e.session_id == busy.current));| vlastnost | typ | povinné | popis |
|---|---|---|---|
| aktuální | string | ne | Id epizody, na které se ukazatel vyřeší, když se na nějakou vyřeší. |
| epizody | array | ne | Každá otevřená epizoda, nejnovější první podle zaznamenaného začátku. |
| zjištění | array | ne | Co by měl čtenář vědět, než uvěří výše uvedenému: soubor, který se nenaparsuje, ukazatel, který se nevyřeší na nic. Prázdné je dobrý případ. |
| přítomno | boolean | ano | Zda existuje úložiště otevřených epizod. False v čerstvém klonu, což není chyba. |
| úložiště | string | ano | Úložiště, relativně k repozitáři. |
| worktree | string | ano | Tento worktree, absolutně. Každé postavení výše se rozhoduje vůči němu. |
JSON Schema
{
"$defs": {
"Episode": {
"description": "One open episode, as its file records it and as the ledger has seen it.\n\nEverything here is recorded rather than derived, and almost everything is optional,\nwhich is the point: an episode opened by hand has no provider, a worker that supplied no\nidentity has none, and an episode that opened and did nothing has no ledger line and so\nno `last_activity`. Each of those is ordinary and none is a fault, so each is carried as\nabsence — the empty fields are skipped on the wire — and a reader must handle their\nabsence rather than expect a placeholder.\n\nThe two fields that are always there are `standing` and `note`: where the episode stands,\nand why it stands there in words. A standing a reader cannot act on is the failure this\nsubsystem was corrected for, so `note` is never empty.\n\n```\nuse majordomus_cli::capability::builtin::lifecycle::{Episode, EpisodeStanding};\nuse serde_json::json;\n\n// the smallest true record: an episode opened by hand that has written nothing yet\nlet e: Episode = serde_json::from_value(json!({\n \"session_id\": \"s-0001\",\n \"path\": \".ai/local/state/sessions-open/by-hand.yaml\",\n \"standing\": \"open\",\n \"events\": 0,\n \"note\": \"open in this worktree; the pointer names another episode\",\n})).unwrap();\n\nassert_eq!(e.standing, EpisodeStanding::Open);\nassert!(e.provider.is_empty(), \"no provider session owns a hand-opened episode\");\nassert!(e.last_activity.is_empty(), \"it has written no ledger line\");\nassert!(e.tasks.is_empty(), \"work outside a task is permitted\");\nassert!(!e.note.is_empty(), \"a standing always says why\");\n\n// and absence stays absence: the empty fields are not serialised back as \"\"\nlet wire = serde_json::to_value(&e).unwrap();\nassert!(wire.get(\"provider\").is_none());\nassert_eq!(wire[\"events\"], 0, \"a count of zero is a measurement, so it is kept\");\n```",
"properties": {
"branch": {
"description": "The branch it opened on.",
"type": "string"
},
"events": {
"description": "How many ledger lines carry this episode id. Zero for an episode that opened and did\nnothing, which is ordinary and not a fault.",
"format": "uint",
"minimum": 0,
"type": "integer"
},
"last_activity": {
"description": "The timestamp of the newest ledger line carrying this episode id, as recorded.\nEmpty when it has written none.",
"type": "string"
},
"last_event": {
"description": "That line's event name.",
"type": "string"
},
"note": {
"description": "Why it stands where it does, in words. Never empty: a standing a reader cannot act\non is the failure this subsystem is being corrected for.",
"type": "string"
},
"owner": {
"description": "Who opened it.",
"type": "string"
},
"path": {
"description": "Repository-relative path of the open record. The body is at the path.",
"type": "string"
},
"provider": {
"description": "The provider whose event opened it, when one did. Empty for an episode opened by\nhand, which is the one episode no provider session owns.",
"type": "string"
},
"provider_session": {
"description": "That provider's own session identity: the file's stem, and the string the prompt\narchive stamps on the same worker's records.",
"type": "string"
},
"session_id": {
"description": "The episode id the ledger stamps on every line this worker wrote.",
"type": "string"
},
"standing": {
"$ref": "#/$defs/EpisodeStanding",
"description": "Where it stands."
},
"start_head": {
"description": "The commit it opened at.",
"type": "string"
},
"started_at": {
"description": "When it opened, as recorded.",
"type": "string"
},
"tasks": {
"description": "The tasks this episode's ledger lines name, in the order it first touched each.\n\nA relation, not a container. An episode may touch three tasks or none, and a task may\nspan three episodes; the two are joined here by what the ledger recorded rather than\nby either one owning the other. An episode with no task is ordinary — work outside a\ntask is permitted — and it is emphatically not a reason for the episode's own records\nto go unwritten, which is the premise ADR 0052 removes.",
"items": {
"type": "string"
},
"type": "array"
},
"worker": {
"description": "The worker identity, when one was supplied. Never inferred.",
"type": "string"
},
"worktree": {
"description": "The worktree it opened in, absolute, as recorded.",
"type": "string"
}
},
"required": [
"session_id",
"path",
"standing",
"events",
"note"
],
"type": "object"
},
"EpisodeStanding": {
"description": "Where an open episode stands, as far as the repository can observe it.\n\nIt is worth being precise about what is *not* here. Whether the provider that opened an\nepisode is still attached to its conversation is not a fact of this repository: the\nepisode file records no pid, the peer board records no episode id, and a client that\nexits without firing its end event leaves a file identical to one a live worker is using.\nGuessing would produce exactly the confident-and-wrong answer this subsystem exists to\navoid, so the vocabulary below is the observable one — where the file is, whose worktree\nit names, and whether the pointer follows it — and `note` always says which.\n\nRead as a scale, the four run from \"this checkout's own work\" to \"nobody's work any\nmore\", and only the last is a fault: `current` and `open` are both this worktree's live\nepisodes and differ only in which one the pointer happens to aim at, `foreign` belongs to\nanother worktree and is reported rather than adopted, and `stranded` is the one that\nneeds a person, because nothing can close it.\n\n```\nuse majordomus_cli::capability::builtin::lifecycle::EpisodeStanding;\n\n// the standings that are somebody's live work, against the one that is nobody's\nlet live = [EpisodeStanding::Current, EpisodeStanding::Open, EpisodeStanding::Foreign];\nassert!(live.iter().all(|s| *s != EpisodeStanding::Stranded));\n\n// serialised as the word `as_str` gives, so the two renderings cannot drift apart\nfor s in [EpisodeStanding::Current, EpisodeStanding::Foreign, EpisodeStanding::Stranded] {\n assert_eq!(serde_json::to_value(s).unwrap(), serde_json::json!(s.as_str()));\n}\n```",
"oneOf": [
{
"const": "current",
"description": "This checkout's pointer resolves to it. `continuity.state` is about this one.",
"type": "string"
},
{
"const": "open",
"description": "Open, and it names this worktree, but the pointer names a different episode. Two\nwindows of one provider on one worktree are two workers, and only one is pointed at.",
"type": "string"
},
{
"const": "foreign",
"description": "It names another worktree of this repository. Reported, never treated as this\ncheckout's — a record about somebody else's work quietly becoming yours is the\nfailure the two-tier resolution rule exists to prevent.",
"type": "string"
},
{
"const": "stranded",
"description": "The worktree it names is gone from disk. Nothing can close it: the close path\ncomposes the record from git in the worktree the episode opened in.",
"type": "string"
}
]
}
},
"description": "Every open episode of this checkout's store — the answer `continuity.state` cannot give,\nbecause it reads the pointer and this reads the store.\n\n`current` is the one episode the pointer resolves to, and it is one row of `episodes`\nrather than a separate thing: the whole correction this type carries is that the pointed-at\nepisode is *a* member of the set and not the set. On the day this was written the store\nheld five open episodes in one checkout and every surface could name one of them.\n\n`present: false` is a fresh clone, which is not a fault — nobody has opened an episode\nhere yet — and is reported as itself rather than as an error.\n\n```\nuse majordomus_cli::capability::builtin::lifecycle::Episodes;\nuse serde_json::json;\n\n// a clone in which no episode has ever been opened: absence, answered as absence\nlet fresh: Episodes = serde_json::from_value(json!({\n \"present\": false,\n \"store\": \".ai/local/state/sessions-open\",\n \"worktree\": \"example/a-fresh-clone\",\n})).unwrap();\nassert!(!fresh.present);\nassert!(fresh.episodes.is_empty() && fresh.current.is_empty());\nassert!(fresh.findings.is_empty(), \"nothing to warn about; it is simply new\");\n\n// and a store with work in it: `current` names one of the rows, never a sixth thing\nlet busy: Episodes = serde_json::from_value(json!({\n \"present\": true,\n \"store\": \".ai/local/state/sessions-open\",\n \"worktree\": \"example/wt\",\n \"current\": \"s-0002\",\n \"episodes\": [\n {\"session_id\": \"s-0001\", \"path\": \"a.yaml\", \"standing\": \"open\",\n \"events\": 3, \"note\": \"this worktree, not pointed at\"},\n {\"session_id\": \"s-0002\", \"path\": \"b.yaml\", \"standing\": \"current\",\n \"events\": 9, \"note\": \"the pointer resolves to it\"},\n ],\n})).unwrap();\nassert_eq!(busy.episodes.len(), 2, \"the pointer is blind to the first of these\");\nassert!(busy.episodes.iter().any(|e| e.session_id == busy.current));\n```",
"properties": {
"current": {
"description": "The episode id the pointer resolves to, when it resolves to one.",
"type": "string"
},
"episodes": {
"description": "Every open episode, newest first by recorded start.",
"items": {
"$ref": "#/$defs/Episode"
},
"type": "array"
},
"findings": {
"description": "What a reader should know before trusting the above: a file that does not parse, a\npointer that resolves to nothing. Empty is the good case.",
"items": {
"type": "string"
},
"type": "array"
},
"present": {
"description": "Whether the open-episode store exists. False in a fresh clone, which is not a fault.",
"type": "boolean"
},
"store": {
"description": "The store, repository-relative.",
"type": "string"
},
"worktree": {
"description": "This worktree, absolute. Every standing above is decided against it.",
"type": "string"
}
},
"required": [
"present",
"store",
"worktree"
],
"title": "Episodes",
"type": "object"
}Politiky
- benchmark
- povinné — cíl na každém transportu, který expozice deklaruje; případy jsou ty ze vstupního typu
- cache
- process — až 2 záznamy v procesu, po 2 s každý, vymezené otiskem registru
Cíle benchmarku
Odvozeno z registru pro tento repozitář: jeden požadavek na transport a případy, které poskytuje typ vstupu. Celá matice je na stránce měření výkonu.
| transport | state | případy | targets |
|---|---|---|---|
| direct | pokryto | 1 | výchozí studený+teplý |
| mcp | pokryto | 1 | výchozí studený+teplý |
| http | pokryto | 1 | výchozí studený+teplý |
Tvrzení implementovaná vedle ní
Připojeno souborem, který každé tvrzení implementuje; každé odkazuje na svou stránku s implementací a testem, který je dokazuje.