Spustitelný soubor Majordomus v Rustu. Sestavuje binárku jménem majordomus, která čte AI vrstvu repozitáře nezávislou na poskytovateli pod .ai/, skládá jeden registr schopností z vlastních spustitelných schopností a objektů vrstvy a obsluhuje tento registr přes několik rozhraní, všechna jen pro čtení: MCP přes stdio (mcp), které také spouští jediný sdílený server repozitáře nebo se k němu připojuje (domovská stránka vypisující každý povrch, který obsluhuje, dokumentace tohoto repozitáře, dokument OpenAPI, shell Swagger UI, Cockpit a MCP přes HTTP na loopback rozhraní, tentýž server, který serve provozuje samostatně), introspekci na příkazovém řádku (capabilities) a generované referenční soubory (generate). Každý klient připojený ke sdílenému serveru je peer, kterého ostatní vidí.
Není to druhá implementace shellového nástroje: init, start, check, finish, doctor, update a životní cyklus úlohy žijí v bin/majordomus v kořeni repozitáře a tento spustitelný soubor inzeruje jen příkazy, které implementuje. Architektura je docs/CAPABILITIES.md; povrch MCP, jak ho vidí klient, je docs/MCP.md; rozhodnutí jsou ADR 1, ADR 2 a ADR 3 pod .ai/repo/adrs/.
Co sem patří a co ne
Patří sem: čtení vrstvy tak, jak ji repozitář deklaruje, validace přečteného proti schématům distribuce a repozitáře, skládání registru, jeho promítání a přesné říkání, co se přečíst nepodařilo. Každá schopnost je read-only a každé spuštění nechá repozitář bajt po bajtu totožný.
Nepatří sem: jakákoli mutace repozitáře, životní cyklus úlohy, projekce politiky do souborů poskytovatelů, hooky, daemon, autentizace, databáze, volání modelu ani cokoli z Prismatic. Binárka nezávisí na žádné službě ani jiném repozitáři; za běhu potřebuje adresář share/ distribuce nástroje a git pro výchozí objevování.
Sestavení, spuštění, testy
cargo build --manifest-path apps/majordomus-cli/Cargo.toml
B=apps/majordomus-cli/target/debug/majordomus
$B --help
$B --version
$B mcp --inspect # what would be served, every diagnostic; exit 10 when degraded
$B mcp # MCP on stdio until the client goes; the first one in a repository is the shared server (Swagger UI at the URL it logs), every later one attaches
$B mcp --standalone # this client alone: no port, no lease, no peers
$B serve # the shared server alone: 127.0.0.1:8741, the home page /, /docs/, /swagger, /openapi.json, /mcp, /api/v1/...; exits 0 when one already runs
$B web list # every web surface: id, kind, mount, category, world, source — discovered, never registered
$B web explain docs # one surface and where each of its values came from
$B web validate # the topology's invariants; exit 10 with every finding and its remedy
bin/majordomus-mcp # builds $B when needed, then `mcp`: what .mcp.json, .gemini/settings.json and .codex/config.toml name
just # the recipes a person runs, routed to $B wherever it can serve them
$B capabilities list # every capability with its projections
$B capabilities describe objects.get
$B capabilities schema objects.search --side input
$B capabilities validate # the registry's invariants, every projection and the benchmark coverage; exit 10 with the list
$B generate # docs/generated/ (each document in every encoding it is committed in; artifacts.md indexes them) and share/allow/*.txt
$B generate --check # exit 10 naming every stale file; writes nothing
$B bench coverage # every required benchmark target and whether it is covered; the denominator is the registry's
$B bench --profile quick # time every operation: directly, over MCP (a real child), over HTTP (a real socket); slowest first
$B bench objects.search --transport mcp --profile full --format json
$B bench --check # against this platform's baseline under .ai/repo/benchmarks/rust/, under policy.yaml
$B bench baseline update # record this platform's baseline (a tracked, reviewable file); refuses a dirty tree
Rust 1.85 nebo novější, git na PATH pro výchozí objevování a sdílený adresář (níže). Z apps/majordomus-cli/:
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings # missing_docs is an error here
cargo test # unit, integration, doctests
cargo doc --no-deps # RUSTDOCFLAGS="-D warnings" in CI
cargo bench # criterion, benches/{projections,shared,scaling}.rs
cargo llvm-cov --all-targets --summary-only # coverage; CI enforces the threshold in scripts/rust-check
scripts/rust-check v kořeni repozitáře (just rust-check) spustí vše v pořadí, v jakém to dělá CI; práh pokrytí je jediné celé číslo v scripts/rust-coverage-threshold, čtené CI, skriptem a just coverage. Testy si každý sestaví jednorázový repozitář a tento checkout nikdy nečtou; test/cases/72_rust_mcp.sh, 76_capabilities_projections.sh a 90_mcp_shared_server.sh v kořeni repozitáře spouštějí sestavenou binárku proti repozitáři, který zapsal vlastní init shellového nástroje, což je křížová kontrola, že oba programy čtou stejnou vrstvu, a 77_rust_evidence.sh dokazuje, že výše uvedené kontroly jsou zapojeny stejně ve skriptu, workflow a justfile (pravidlo project.rust-cli-evidence, tvrzení rust-evidence-gates, rust-coverage-floor, rust-hot-path-benchmarks).
Binárka sdílí jméno se shellovým nástrojem záměrně. Dejte na PATH jedno nebo druhé, nebo volejte tento cestou. Klient MCP se konfiguruje jednou, v kořeni repozitáře, a tento repozitář ty soubory dodává: .mcp.json pro Claude Code, .gemini/settings.json pro Gemini CLI a .codex/config.toml pro Codex všechny spouštějí bin/majordomus-mcp, který sestaví tento crate, když program chybí nebo je starší než jeho zdroje, nastaví MAJORDOMUS_SHARE a exec majordomus mcp. Chcete-li program pojmenovat přímo:
{ "mcpServers": { "majordomus": { "command": "/path/to/apps/majordomus-cli/target/debug/majordomus", "args": ["mcp"], "env": { "MAJORDOMUS_SHARE": "/path/to/prismatic-majordomus/share" } } } }Adresář share: druhy a schémata za běhu
Nic o druzích ani klíčích není zkompilováno. Při každém startu spustitelný soubor najde adresář share a načte z něj kinds.yaml a schemas/<name>.schema.json v tomto pořadí:
| zdroj | kdy |
|---|---|
--share <DIR> | vždy vyhrává; adresář bez kinds.yaml je chyba |
MAJORDOMUS_SHARE | stejné |
<repository root>/share | když drží kinds.yaml: tento repozitář dozoruje sám sebe |
<directory of the executable>/../share | instalace rozložená jako bin/ vedle share/ |
Žádný nenalezen: exit 12, každý vyzkoušený adresář pojmenován. Repozitář přidává druhy ve vlastním kinds.yaml pod .ai/repo/knowledge/ a schémata pod .ai/repo/knowledge/schemas/; jméno, které distribuce už deklaruje, je chyba pojmenovávající oba soubory.
Přehled příkazů
Příkazová řádka se deklaruje jednou, v src/cli.rs: clap pro strukturu a typovaná Rustová metadata vedle něj (EXAMPLES) pro příklady, které clap nenese. Každá reference se generuje z této jedné deklarace a nikdy se nepíše ručně — majordomus --help v terminálu, docs/generated/cli.md pro čtenáře na GitHubu, docs/generated/cli.json a jeho sourozenec YAML pro generátor webu a pro čtenáře konfigurace, reference pod /docs/cli/ webu s jednou stránkou na příkaz a stránka příkazové řádky registru. majordomus generate soubory zapisuje a majordomus generate --check v CI zastaralý odmítne. Každý artefakt, který zapíše, deklaruje dokument, který promítá, své kódování, schéma, které jeho obsah splňuje, a svůj zdroj, a strukturovaný dokument se zapisuje v každém kódování, v němž je commitnut, z jedné hodnoty; docs/generated/artifacts.md je generovaný index celé sady a artifacts.list ho čte zpět usmířený s pracovním stromem. Každý příkaz začíná týmiž volbami (--repo, --discovery, --strict, --share); reference je uvádí u každého příkazu, který je přijímá.
Příkaz není hotový, když se naparsuje. cli::validate — spouštěno majordomus capabilities validate, tests/cli_docs.rs a tedy CI — odmítne příkaz bez shrnutí, argument bez nápovědy, výčtovou hodnotu, která nic neříká, a každý příkaz, který člověk může spustit a který nenese příklad. tests/cli_examples.rs pak spustí každý zdokumentovaný příklad proti sestavenému spustitelnému souboru v jednorázovém repozitáři: argumentové vektory, které provádí, se čtou ze stejné deklarace, kterou vykreslují stránky, takže zdokumentovaný příklad, který přestal fungovat, je selhávající test, ne zastaralá stránka. Přidat příkaz tedy znamená: deklarovat ho v clap, deklarovat vedle něj alespoň jeden příklad, implementovat ho, just derive.
Návratové kódy
Stejný kontrakt jako docs/CLI.md:
| kód | význam | příklady |
|---|---|---|
0 | ok | servírováno do EOF; --inspect nebo validate, když není nic špatně |
2 | použití | neznámý příkaz nebo volba (clap) |
10 | kontrakt nesplněn | manifest, sources.yaml, druhy nebo schéma neplatné; registr se nesestaví; --strict s chybami; --inspect s chybami; zastaralé artefakty pod --check |
12 | chybějící artefakt | žádné .ai/manifest.yaml v žádném předkovi; rozvržení před .ai; žádný adresář share; neznámé id schopnosti |
13 | interní | selhání I/O, git nepoužitelné pod --discovery vcs, port, který nelze navázat, selhání transportu |
Tabulka vedlejších efektů
| command | změna souborového systému | mutace gitu | síť | stdout |
|---|---|---|---|---|
--help, --version, capabilities …, mcp --inspect, generate --check, bench coverage | ne | ne | ne | text nebo JSON |
bench | .ai/local/benchmarks/<run>.json (lokální polovina, nesledovaná), pokud není --no-write; baseline update zapisuje .ai/repo/benchmarks/rust/baseline.<platform>.json | ne | loopback socket a vlastní potomek majordomus mcp | text nebo JSON |
mcp | lease pod .ai/local/state/mcp/ (nesledovaný), odstraněný při ukončení; žádný s --standalone | ne | naslouchá na loopbacku (server) nebo se k němu připojuje (most); prohlížeč stahuje prostředky Swagger UI | Protokol MCP |
serve | lease, jak je uvedeno výše | ne | naslouchá na loopbacku; prohlížeč stahuje prostředky Swagger UI | HTTP na socketu, nic na stdout |
generate (bez --check) | zapisuje docs/generated/ a share/allow/ | ne | ne | zapsané cesty |
tests/mcp_stdio.rs::serving_mutates_nothing, tests/http_serve.rs::serving_from_a_nested_directory_finds_the_same_root_and_writes_nothing a test/cases/90_mcp_shared_server.sh porovnávají git status a každý sledovaný blob před relací a po ní; tests/mcp_shared.rs kontroluje, že po serveru lease zmizel.
Architektura
main.rs parse, init stderr logging, run, map the error to an exit code
cli.rs clap declarations; RepoArgs shared by every command
app.rs the one composition point: repository -> share -> kinds and schemas -> index -> registry
commands/ mcp, serve, capabilities, generate, bench: each a function from its arguments to an exit code;
mcp holds the session that is answered locally (this process is the server) or bridged
perf.rs the process-wide counters and phase timings behind perf.counters and the structural tests
bench/ projection.rs (targets from the registry), coverage.rs, runner.rs (direct, HTTP, MCP),
stats.rs, results.rs (the versioned document), baseline.rs (per-platform baseline, the policy), system.rs
synthetic.rs a generated repository of any size, for the property tests and the scaling benchmarks
lease.rs one shared server per repository: the lease file, the election, the stale-lease takeover
shared.rs the shared server: bind, publish the URL, serve until the last peer leaves, release
peers.rs the peer board: sessions named by their clients, announcements, in memory only
repository.rs root discovery (nearest .ai/manifest.yaml), the typed manifest
share.rs locating the distribution's share directory; reading its schemas
discovery/ sources.yaml, the DiscoverySource trait, its two implementations, :(glob) matching
metadata/ kinds.yaml, the schema set (jsonschema), the YAML subset, front matter
index.rs read every discovered file into an Object or a Diagnostic; dedupe; sort
model.rs Object, Provenance, Diagnostic, Severity: the domain of the layer, no I/O
capability/ the canonical model: model.rs (descriptor, kinds, cache and benchmark policy), schema.rs
(canonical schemas and their MCP and OpenAPI projections), handler.rs (typed handlers, Context,
the capability! macro), module.rs (module! and compose_modules!), benchmark.rs (BenchmarkCases),
executor.rs (the one execution path and the cache), registry.rs (the registry, its modules and
invariants, the fingerprint), builtin/<module>.rs (the executables, one file per module;
mod.rs composes the application), declarative.rs
mcp/ surface.rs (the registry as resources and tools), protocol.rs (JSON-RPC and MCP), stdio.rs,
bridge.rs (a stdio session forwarded to another process's shared server; its own HTTP client)
http/ surfaces.rs (the resolved topology narrowed to this process, one handler bound per surface;
a native surface with no handler refuses the router), router.rs (asks the topology who owns a
path, and binds a capability's input from the registry), openapi.rs, swagger.rs,
server.rs (tiny_http, a few worker threads), mcp.rs (MCP over HTTP: sessions, expiry)
web/ the web surfaces as one model: model.rs (Surface, Mount, Topology, and the two worlds a mount
can be claimed in), discover.rs (the executable's own declarations, the site's configuration,
a producer's surface.json), validate.rs, files.rs (serving a static surface safely),
home.rs (the page at /), html.rs, compose.rs, manifest.rs, report/
generate.rs the one generator pipeline and the allow-list derivation
git/ read-only git: toplevel, head, branch, dirty state, ls-files
logging.rs, error.rs tracing to stderr; the typed errors and their exit codes
Závislosti tečou dovnitř: commands zná clap a vše pod sebou; mcp a http znají registr a nic o clap ani souborech; capability zná model indexu; index, discovery, metadata, repository, share a git znají model a chyby; model nezná nic. Žádná knihovna MCP ani framework HTTP: podmnožina MCP jen pro čtení je osm metod v jednom souboru, hrstka loopbackových tras potřebuje pár synchronních vláken workerů nad jedním neměnným registrem, ne asynchronní runtime, a požadavek mostu je třicet řádků nad TcpStream. Jediný trait, DiscoverySource, existuje, protože se dodávají dvě enumerace. Závislosti: clap, serde, serde_json, schemars, jsonschema, tiny_http, thiserror, tracing, tracing-subscriber; dev: tempfile, criterion.
Webový povrch
Vše, co tento spustitelný soubor vystavuje přes HTTP, je povrch a žádný jejich seznam neexistuje. Objevují se — z deklarací capability! a konstant v web::discover::native_all(), z site/config.toml a z surface.json, kterou producent zapíše vedle svého výstupu pod target/web/<id>/. Jedno rozřešení, držené na Context::web a rozřešené jednou za generaci repozitáře (live::Live), krmí router, domovskou stránku na /, schopnost web.surfaces, validátor, publikaci, log startu a docs/generated/web.json.
Měníte-li cokoli pod src/http/, src/web/ nebo src/cockpit/, platí invariant: povrch HTTP je deklarován jednou, tam, kde je jeho producent, a každé další jeho zobrazení je odvozeno. Nepřidávejte cestu na domovskou stránku, do seznamu infrastruktury dokumentu OpenAPI, do inventáře měření, do tabulek Cockpitu ani do dokumentační tabulky — nic z toho se nepíše ručně a vše čte totéž rozlišení. Přidání statického povrchu je surface.json a vůbec žádný Rust; přidání trasy, na kterou tento exekutor odpovídá, je jedno Surface v web::discover::native_all() a jedna větev v http::surfaces::Native::of, což router kontroluje při konstrukci.
/docs je dokumentace tohoto repozitáře a /swagger je Swagger UI. Ani jedno jméno nesmí být použito pro to druhé: říká to project.web-surface-declared-once, vynucuje majordomus web validate v scripts/rust-check a dokazuje test/cases/89_web_surface.sh přes skutečný socket. Celý návrh, metadata, která povrch nese, jak se /docs staví pro svůj mount a co se kde vynucuje, je docs/WEB.md; rozhodnutí je ADR 13.
Objevování repozitáře
Jde se od startovního adresáře nahoru. První předek, který nese .ai/manifest.yaml, je kořen. Manifest, který se nedá parsovat, nese klíč, který nic nečte, nebo deklaruje jiné schéma než ai-repository/v1, zastaví hledání s touto chybou: bližší rozbitá vrstva se nikdy nepřeskočí kvůli vzdálenější funkční. .git není značka, takže obyčejný git repozitář není repozitář Majordomus. .majordomus/ také není značka: s bin/majordomus uvnitř jde o instalaci nástroje a ignoruje se; bez něj jde o rozložení před .ai a zamítne se jménem s odkazem na majordomus migrate.
Datově řízený model
Nic v kódu v Rustu nejmenuje soubor repozitáře kromě dvou bootstrapových konvencí, které vrstva sama dokumentuje, .ai/manifest.yaml a sources.yaml v sekci knowledge manifestu, a jmen souborů distribuce (kinds.yaml, schemas/*.schema.json). Vše ostatní se čte z dat za běhu:
| rozhoduje | čteno z | vlastník |
|---|---|---|
| kde je vrstva | .ai/manifest.yaml existuje | repozitář |
| které sekce existují | sections: v manifestu | repozitář |
| které názvy souborů musí nést kontrakt kontextu | context.documents: v manifestu | repozitář |
| které soubory nesou který druh | .ai/repo/knowledge/sources.yaml | repozitář |
| jak se druh čte | share/kinds.yaml, plus kinds.yaml pod .ai/repo/knowledge/ | distribuce, repozitář |
| jaké klíče a hodnoty smí druh nést | share/schemas/<kind>.schema.json, plus .ai/repo/knowledge/schemas/ | distribuce, repozitář |
Čtečka YAML implementuje zdokumentovanou podmnožinu vrstvy (docs/SCHEMAS.md), nikoli obecné YAML, takže tento spustitelný soubor a shellový nástroj přijímají stejné soubory a stejné odmítají (jedna záměrná odchylka: tato čtečka ořezává koncové mezery před testováním uvozovek; shell uvozovky ponechá a takový prompt odmítne až později). Politika selhání, rozhodnutá jednou v index.rs: manifest, sources.yaml, soubory druhů a schémata jsou chyby, protože bez nich nelze nic objevit; každý jiný soubor, který se nemůže stát objektem, je vyloučen s diagnostickou chybou pojmenovávající jeho cestu a kód, a index je degraded. Degradovaný index stále slouží; --strict ho odmítne.
Diagnostické kódy: malformed_front_matter, missing_front_matter, malformed_yaml, unknown_key, schema_violation, kind_mismatch, unsupported_version, missing_field, missing_context_contract, duplicate_identity, unknown_kind, required_source_empty, claimed_twice (varování), invalid_utf8, oversized, symlink, not_a_file, unreadable.
Bezpečnostní hranice
Obsah repozitáře je nedůvěryhodný vstup: symlinky se nikdy nesledují ani nečtou; soubor nad 4 MiB nebo front matter nad 64 KiB je odmítnut; neplatné UTF-8 je odmítnuto; čtou se jen cesty, které odpovídají deklarovaným pathspecům, a jen z kořene repozitáře; resources/read a objects.get odpovídají z indexu v paměti a z registru (jedno překládání URI, sdílené oběma; majordomus://repository provádí repository.info), nikdy z cesty, kterou uvede klient; server HTTP se váže na loopback, čte nejvýše 1 MiB těla a nevěří žádné hlavičce kromě id relace, které sám vydal, jež pojmenovává relaci a nic nepřiznává (každá relace vidí stejný registr jen pro čtení). Zápůjčce se věří, až když server, který pojmenovává, odpověděl za tento kořen; oznámení peera je text, který čtou jiní peeři, nikdy cesta, kterou server otevře. Nebrání se: nepřátelskému sources.yaml pojmenovávajícímu obrovský strom (průchod je omezen repozitářem, ne rozpočtem) a paměti, protože obsah každého objektu je po dobu relace držen.
Stabilita
| status | |
|---|---|
--help, --version, mcp --help, návratové kódy, nápověda a kódy nových příkazů | behaviorálně ověřeno (tests/cli.rs) |
objevení kořene, vítězí nejbližší, odmítnutí legacy, --repo | behaviorálně ověřeno (tests/repository_discovery.rs) |
| schéma druhu, schémata za běhu, každý diagnostický kód, druhy definované repozitářem | ověřeno chováním (tests/metadata_contract.rs, tests/external_extension.rs) |
| determinismus napříč výčty, původ, klasifikace | behaviorálně ověřeno (tests/index_behavior.rs) |
| MCP: handshake, výpis, čtení, nástroje, chyby, čistý EOF, stdout jen s protokolem, žádná mutace | behaviorálně ověřeno (tests/mcp_stdio.rs) |
| invarianty registru; každá projekce je přítomna a žádná není osiřelá; změna se dostane do každé projekce | ověřeno chováním (tests/registry.rs, tests/projections.rs) |
HTTP přes socket, OpenAPI, Swagger shell, typované chyby, HEAD, stdin /dev/null, parita MCP/HTTP | behaviorálně ověřeno (tests/http_serve.rs) |
jeden sdílený server na repozitář: lease, most, relace /mcp, peeři, záložní port, odkládání serve, --standalone, převzetí, opětovné připojení, odmítnutí | behaviorálně ověřeno (tests/mcp_shared.rs, tests/shared_units.rs, test/cases/90_mcp_shared_server.sh) |
| moduly skládají schopnosti, kořen skládá moduly; invarianty modulů, cache a měření výkonu | ověřeno chováním (tests/registry.rs, test/cases/91_canonical_architecture.sh) |
| jeden executor; ekvivalence cache vypnuto/studená/teplá, žádný handler při zásahu, chyby a příkazy se nikdy necachují, mez, otisk; pořadí klíčů je nepodstatné | behaviorálně ověřeno (tests/executor.rs, tests/properties.rs s proptest) |
| žádný požadavek nepřestavuje kanonický stav; OpenAPI sestaveno jednou; výpisy připraveny jednou | ověřeno chováním (tests/hot_path.rs, test/cases/91_canonical_architecture.sh) |
| každá operace je cílem měření výkonu s generovaným jmenovatelem; šíření vystavení a politiky; přímé, HTTP a MCP runnery nad skutečnými transporty; kontrola základní hodnoty | behaviorálně ověřeno (tests/bench.rs, tests/bench_units.rs, test/cases/91_canonical_architecture.sh) |
| generovaná reference pro každý modul, matice měření výkonu, manifest registru: deterministické, srovnané | behaviorálně ověřeno (tests/projections.rs) |
generate, bajtově shodná regenerace, --check | behaviorálně ověřeno (tests/generate_check.rs) |
vrstva, kterou init zapisuje, poskytovaná se stavem ok; jedna schopnost přes každé rozhraní | ověřeno chováním (test/cases/72_rust_mcp.sh, 76_capabilities_projections.sh) |
názvy CLI, URI, názvy nástrojů, routy, diagnostické kódy, kinds.yaml, soubory schémat | implementováno; před 1.0 se změny dokumentují, nikdy tiše |
--discovery filesystem | implementováno; pohodlí mimo smlouvu vrstvy |
Záměrně odloženo: prompty MCP (aktiva promptů vykreslují {{CONTEXT}} z lokálního stavu, který vlastní shellový nástroj), mutace repozitáře přes jakékoli rozhraní, odběry a notifikace o změně seznamu, proud iniciovaný serverem na /mcp, parametry cesty, /openapi.yaml, aktiva Swagger UI offline, hot reload (sdílený server drží index, který postavil při startu, dokud neodejde jeho poslední klient), polovina .ai/local/ jako servírovaný obsah, hash obsahu na objekt a trvalá koordinace (nástěnka peerů je paměť jednoho procesu; záznam úlohy a rozsah shellového nástroje jsou trvalá forma).