Majordomus 发布 AI 编码智能体监督控制层
korczis/prismatic-majordomus
Majordomus 发布一个面向 AI 辅助工作的轻量监督控制层,为仓库维护一份统一的智能体操作策略,并据此生成各工具读取的指令文件。它把任务、可触碰路径和交接记录保存在仓库而非对话中,在契约未满足时拒绝将工作标记为完成,且完全本地运行、不调用任何模型。
A lightweight supervisory control layer for AI-assisted work.
Website · The challenge: watch it refuse an agent · CLI reference · File schemas · What is guaranteed, advisory, planned, or rejected · Why it exists
AI coding agents do the work. Majordomus decides whether it is done. It holds one canonical policy for how AI workers operate in a repository and generates the instruction file each tool reads from it; it keeps the task, the paths it may touch and its handover in the repository instead of a conversation; it reports when two workers are about to collide; and it refuses to call work finished until a contract is met. It runs entirely locally, in portable shell, and never invokes a model.
A recorded run from the challenge, redacted and otherwise untouched: the scope is clean now, but the repository's own test still fails, so a worker's claim that it is done is refused.
$ majordomus finish --outcome completed --verify-command 'sh test/parse.sh'
OK blockers t-<id> — none open
OK note t-<id> — <time>--main--<head>--<hash>.md
OK scope t-<id> — 1 touched file(s), all within scope
OK state t-<id> — exact (head <head>)
FAIL verification t-<id> — sh test/parse.sh — exit 1, <s>s [reproduce: sh test/parse.sh]
finish: refused, 1 unmet
curl -fsSL https://majordomus.dev/install.sh | shThe problem
Most AI-assisted work fails for boring reasons. Too much context. The wrong rules for the tool that happens to be open. Reasoning at maximum by default. Sessions that never end. Two agents editing the same file. No definition of done, so "done" means the worker said so.
These are operational-control problems, and the environments this tool was distilled from had them at scale:
- instruction files for two AI tools in the same repository sharing 0–9 % of their content: not duplicates, disjoint rule sets
- an always-loaded operating contract oscillating between 0 and about 1,100 lines across roughly 200 hand edits, until a hard budget with a failing check pinned it
- seventeen independent cases of a check documented as blocking, present on disk, executable, and invoked by nothing
- full worktree isolation still producing about 3,200 concurrently modified files and 67 duplicated patches across forty worktrees, with nine ever merged
- 10 GB of session notes standing in for a database, recovered by a hand-written runbook
Better prompts do not fix any of this.
What it does
| Layer | one portable .ai/ directory, readable without the tool: .ai/repo/ is tracked and canonical, .ai/local/ is this checkout's own and ignored; doctor proves the manifest resolves, every section it names exists, and nothing under local/ is tracked |
| Policy | one provider-neutral policy.yaml; unknown keys are errors |
| Projection | update generates CLAUDE.md, AGENTS.md, GEMINI.md, or any target you name, deterministically, and stamps each with the policy hash and the hash of its own content; a hand edit is detected and never silently overwritten |
| State | one active task per checkout in current.yaml, with branch, head, and working_tree computed from git, never authored |
| Scope | start takes the paths a task may touch; check and finish fail on files outside them; other worktrees' overlapping claims are reported |
| Profiles | one bundle per task class, each setting capability class, reasoning effort, verbosity, presentation, context toggles, verification, and checkpoint interval independently |
| Handover | append-only records with computed front matter and required sections, created atomically, never staged; --resolve finds the right one for this worktree and branch and labels how far git has moved since |
| Finish | a typed outcome and a contract evaluated line by line; nothing written when any line fails |
| Doctor | proves the installation is real: policy parses, every declared enforcement is actually invoked by the hook it names without a swallowed exit code, every projection matches its own stamp, the always-loaded file is under budget |
| Watch | reports drift between policy, projections, state, scope, and git, each finding with the command that reproduces it |
| Plan | milestones are outcome specifications and issues are execution contracts; status, execution waves and the next ready issue are derived from the dependency graph, never stored |
Every row above is backed by a case in test/cases/. The README is not
allowed to say more than the tests do. The layer is the repository's, not the tool's:
Majordomus is the reference implementation that validates, projects and enforces it.
Scoped context. Context is a tree, not one file. Every README.md under .ai/ is a
context document: front matter gives it an identity that survives a move, a scope (its
directory, its subtree, or listed paths), the providers it addresses, and how it composes
with its ancestors. majordomus context resolve <path> prints the documents that apply to
a path, least specific first; the nearest one adds to the chain and never silently
replaces it, and sibling directories never see each other's. context validate refuses a
tree with a missing contract, a duplicate id, a broken or cyclic override, or an override
of a document marked final; context affected names the documents a change set touches,
including the ones that track the code that changed; context check-sync is the one
command a hook runs. The model is in docs/CONTEXT.md.
Quick start
curl -fsSL https://majordomus.dev/install.sh | shThen, in the repository you want supervised:
majordomus init # .ai/: policy, four profiles, prompts, the vendored rule baseline, workflows majordomus update # CLAUDE.md, AGENTS.md, GEMINI.md generated from the policy majordomus doctor # names the two hook lines you still need to add
The installer needs no git, no Rust, no Node, no Python and no root; it downloads one
archive for your machine, verifies its SHA-256 against the release metadata before
unpacking anything, and installs atomically. Pinning a version, choosing where it goes,
upgrading, uninstalling, the supported platforms and the security model are in
docs/INSTALL.md; building from a checkout is for contributors and is
in CONTRIBUTING.md.
Per task
Add the hook lines, run doctor again, commit .ai/repo/ and the generated files.
.ai/local/ is this checkout's own state and is ignored; it never travels through git.
Then, per task:
majordomus start "fix OAuth callback" --scope lib/auth --profile debugging # ... the AI worker reads the generated instructions and works ... majordomus check majordomus finish --outcome completed --verify-command "make test"
Requirements: bash 3.2 or newer, git, and sha256sum or openssl. Nothing else.
Nothing is installed into your project except .ai/ and the files the policy names. A
repository set up before the .ai/ layer, with its data under .majordomus/, is moved
by majordomus migrate (preview with --dry-run); every other command refuses that
layout and names it.
Mental model
Human / Organisation
|
v
MAJORDOMUS
policy · state · verification
|
+-------------+-------------+
v v v
Worker A Worker B Worker C
(Claude Code) (Codex) (Gemini, Cursor, local ...)
| | |
+-------------+-------------+
an orchestrator (bb) runs any of them; the agent stays the worker
|
v
Verified, accepted outcomes
Two principles run through everything. Git is the authority; everything else is an
aid. Identity fields on any record are computed from git, and a body that tries to
set them is rejected. Numbers in prose are computed or forbidden. doctor fails on
a hardcoded count in the always-loaded file.
Philosophy
Simple things should be trivial. Complex things should be possible. Weird things should be hackable. Majordomus is opinionated by default, programmable by design, and hackable at the edges: complexity is optional, not ambient.
- Defaults work out of the box:
init,update,doctor,start,check,finish, and nothing about schemas. - The repository's policy, profiles, rules, prompts and workflows are inspectable and
customisable, declaratively, under
.ai/repo/. - The internals stay open: validators, provider templates, projections and the rule package are there to read and to extend.
- Majordomus is not a universal agent framework; its extensibility lives inside its domain.
The rule is .ai/repo/rules/project/optional-complexity.v1.md.
Core principles
- Sessions are workers, not memory.
- Load minimum sufficient context.
- Externalise decisions and durable state.
- One worker, one clear scope.
- Escalate capability and reasoning only when justified, and record it.
- Execution depth is not output verbosity.
- Define done before executing.
- Verify outcomes, not activity.
- Parallelism requires isolation.
- Handovers transfer state, not transcripts.
Each is a rule object in the vendored baseline under .ai/repo/rules/vendor/majordomus/,
one file each, and every enforced rule names the principle it rests on; every worker
reads the same ten sentences from the same place, whichever tool it is.
Doctrine
A principle is a sentence a worker reads. A doctrine is the part of a principle a
machine can decide, and it names the code that decides it. Every rule Majordomus
enforces is a portable rule object — one Markdown file with YAML front matter — in the
package the tool ships under share/standard/majordomus/
and vendors into every repository under .ai/repo/rules/vendor/majordomus/; the ten
principles are rule objects too, and every enforced rule names the principle it rests on
in depends_on. No command selects checks by hand: check, finish, doctor and
watch each ask the resolved rule set what applies to them, so a rule added to the
package is enforced from the moment it is vendored, and a repository's own rules under
.ai/repo/rules/project/ join the same set.
A doctrine is either blocking (a violation stops the command with a non-zero exit)
or advisory (reported, and the command still succeeds). There is no third class, no
severity ladder, no baseline, and no override. The class is read at dispatch time and is
what routes the finding, so changing one word in a rule file changes whether
majordomus check exits 0 — and a test asserts exactly that.
The point of declaring rules is being able to check the declaration. majordomus doctor
walks the chain for each one, reading the source rather than the rule's description
of itself:
declared → validator exists → the commands it names dispatch → a blocking rule can
exit non-zero → a test proves it → CI runs that test without swallowing it
and in the other direction, a validator that no doctrine declares is enforcement running
under no rule, and fails. That check is itself mutation-tested: 18_doctrine_wiring.sh
breaks each link in a throwaway copy and fails unless doctor goes red, because a
verifier that survives broken wiring proves nothing.
majordomus rules list # the effective set, resolved: vendored baseline plus project rules majordomus doctrine status # declared, blocking, advisory, missing validators — all derived majordomus doctrine list # id, class, validator, the commands that enforce it majordomus doctrine show <id> # one rule, with its claims, its tests and the file it lives in majordomus check --rule <id> # run one rule
Full model, and what was deliberately left out: docs/DOCTRINE.md.
Execution profiles
| profile | capability | effort | verbosity | context | verification | checkpoint |
|---|---|---|---|---|---|---|
routine |
fast | low | terse | task, state | verify command if files changed | 30m |
implementation |
standard | medium | concise | + decisions, files | verify command | 15m |
debugging |
strong | high → xhigh | concise | + failing output, history | verify command, regression test | 15m |
deep-work |
strongest | high → xhigh | detailed | + architecture | verify command, decision record | 30m |
Capability classes are not vendor model names; the projection tells the worker to pick
the closest its environment offers. Each axis is a separate field. Add a profile by
adding a file. Full schema: docs/SCHEMAS.md.
Example workflow
$ majordomus start "fix OAuth callback" --scope lib/auth --profile debugging
started t-20260903193012-a4f1 profile=debugging scope=lib/auth
INFO overlap wt-bob — claims lib/auth/oauth — contained by your lib/auth [reproduce: majordomus check --overlap]
next: worker reads the projected instructions; checkpoint every 15m; majordomus check
$ majordomus check
OK state t-20260903193012-a4f1 — advanced (head 3f2a9c1)
FAIL scope config/secrets.example — outside claimed scope (lib/auth) [reproduce: git status --porcelain; git diff --name-only 3f2a9c1 HEAD]
OK checkpoint t-20260903193012-a4f1 — 7m ago, interval 15m
OK blockers t-20260903193012-a4f1 — none open
check: 4 finding(s), 1 failing
$ printf '# Objective\n…\n# Current State\n…\n# Next Action\n…\n' | majordomus handover
.ai/local/state/handovers/20260903T201455Z--main--9b1e2d4--c0ffee1234567890.md
$ majordomus handover --resolve # next session, same branch
Handover: .ai/local/state/handovers/20260903T201455Z--main--9b1e2d4--c0ffee1234567890.md
Match: same_worktree_same_branch
Git state: advanced
---
# Objective
…
A walk-through with real output is in examples/minimal/.
Provider adapters
.ai/repo/policy.yaml + profiles/ .ai/repo/rules/ (the rules themselves)
| ^
| majordomus update | read by the worker, never copied
v |
CLAUDE.md AGENTS.md GEMINI.md <any target named in the policy> --+
Adapters are thin bootstraps. Each generated file tells the worker where the
repository's context lives — README.md, then .ai/README.md and its discovery
protocol — and carries no rule of its own, so a rule that exists for one provider and
not another cannot happen. doctor fails a generated file that carries a rule corpus of its own — a profile
table, rule bullets, a rules or lifecycle or finish-contract heading — and a
README.md that does not name AGENTS.md. The templates ship with the tool under
share/providers/; a repository that needs a different adapter puts its own at
.ai/repo/providers/<provider>.tmpl, and the rules stay where they are.
Commands
| command | answers | writes | exit |
|---|---|---|---|
init |
create the .ai/ layer here |
yes; refuses to overwrite, --extend adds what is missing |
0 / 15 |
migrate |
move pre-.ai project data from .majordomus/ into .ai/ |
git moves, a verified backup, the index | 0 / 12 / 15 |
rules |
the effective rule set: vendored baseline plus project rules, resolved | only vendor update |
0 / 10 / 11 / 12 / 15 |
doctor |
is Majordomus itself real and wired here? | no | 0 / 10 / 12 |
start <task> |
begin a scoped task under a profile | task record, ledger | 0 / 15 |
check |
is the task consistent with policy, scope, state? | no (--checkpoint updates one timestamp) |
0 / 10 |
watch |
what has drifted? | no | 0 / 11 |
doctrine |
what rules are enforced, by what, and are they wired? | no | 0 / 10 / 12 |
update |
regenerate projections from policy | projections | 0 / 10 / 15 |
handover |
write a continuation record; --resolve finds one |
one new file | 0 / 10 / 12 |
finish |
evaluate the finish contract | task record, ledger | 0 / 10 / 15 |
skills |
the repository's skills: list, show one, check every one against its contract | no | 0 / 10 / 12 / 13 |
bench |
how long does every public command take here, cold and warm, against the baseline? | local evidence under .ai/local/benchmarks/; the baseline only with --write-baseline |
0 / 10 / 12 / 15 |
archive |
a snapshot of the tracked tree that can be read, or checked, somewhere else | an archive under tmp/archives/, or wherever --out says |
0 / 2 / 10 / 12 / 13 / 15 |
Exit codes are a contract: 0 ok, 2 usage, 10 contract unmet, 11 drift found,
12 missing artifact, 13 internal error, 15 refused. There is no "warn and
continue". Details: docs/CLI.md.
Every command reports where its time went with MJ_TIMING=1, and bench holds the
accepted state in a tracked baseline; how that works, and the rules behind it, is
docs/PERFORMANCE.md.
Mesh and models
Sessions on several machines can work one repository without colliding. Each machine holds
one Ed25519 node identity; each checkout's server is a runtime of the mesh. Runtimes find
each other over UDP multicast, a broadcast fallback, rendezvous endpoints or declared
seeds, and discovery grants nothing: a link is admitted only by a signed handshake
that checks the peer's key is trusted, its repository is the same one (by root commits or
a declared identity — never a path) and its protocol is compatible. Linked runtimes
replicate one journal of signed events — sessions, claims, handovers, reviews — every
heartbeat and fold it into the same state, so an exclusive claim on one machine refuses an
overlapping claim on another, a handover published on one is consumed on another, and a
runtime that crashes expires everywhere on its own. The whole subsystem is off until the
repository commits an enabled mesh declaration with the keys to trust, so the default
posture — nothing leaves the machine — holds until a person decides otherwise.
majordomus mesh doctor # every prerequisite on this machine, with impact and remedy majordomus mesh identity # this machine's node id and public key (for allowlists) majordomus mesh peers # machines → runtimes → sessions → claims, and each link's state majordomus mesh claim apps --session s1 --issue '#184' # exits 10 when a claim elsewhere holds it majordomus mesh verify # a live round with every peer, and whether the state converged
The guarantees and their tests — separate processes over TCP, and a container lab that
partitions and kills nodes on a real network — are in docs/MESH.md.
Beside it, share/models.yaml declares the AI models this tool's world can name —
vendors, canonical ids, capabilities, context windows, provenance stated and pricing
deliberately absent — and majordomus models route answers which model a stated need
selects, with the reason, the fallback chain and the first failing check for every
excluded candidate. Both are ordinary capability modules: the CLI, /api/v1/mesh* and
/api/v1/models*, the MCP tools and the Cockpit's Mesh and Models pages render the
same state. docs/MESH.md (threat model included) and docs/MODELS.md are the deep
ends; ADR 0050, ADR 0067 and ADR 0049 are the decisions.
Worktrees
Several sessions on one repository need several checkouts, and git has no opinion about where they go — so every caller has one, and the answers accumulate. Majordomus derives the answer instead, from git's own identity and the branch name, and every surface derives it the same way:
~/dev/prismatic-majordomus the primary checkout — hosts the trunk, never moves
~/dev/prismatic-majordomus-wt/ the container: the checkout's sibling, name + "-wt"
~/dev/prismatic-majordomus-wt/feature/x the one worktree of branch feature/x, hierarchy kept
majordomus worktree create feature/improve-cli # you choose the branch, not the path cd "$(majordomus worktree path feature/improve-cli)" majordomus worktree # where am I, and is that where I belong majordomus worktree migrate --plan # what is out of place, and what would move
Nothing registers a path: git is the registry, and adding a branch edits nothing. A
worktree somewhere else is a typed diagnostic on every surface — the command line, MCP,
/api/v1/worktrees, the Cockpit — and worktree migrate brings it home with its
uncommitted work, fingerprinted before and after. The pre-commit hook refuses a feature
branch committed from anywhere but its canonical worktree. Details:
docs/WORKTREES.md; the decision: ADR 21.
Customisation
- Rules workers read: rule objects under
.ai/repo/rules/project/, one Markdown file each; the vendored baseline changes only throughrules vendor update. - Which files are generated: the
projectionslist in the policy. - What "done" means:
verification.finish_requiresin the policy, plus per-profileverification.*fields. - Budget for the always-loaded file:
context.always_loaded_budget_lines. - What must be wired: the
enforcementlist;doctorreconciles it. - A new task class: a new file in
.ai/repo/profiles/.
Unknown keys anywhere are errors, so a typo fails loudly.
What this is not
- not a model, and it never invokes one
- not an agent framework, orchestrator, or runtime
- not a prompt library or a memory system
- not a daemon, database, queue, or hosted service; the Rust executable's
mcpandserveare read-only processes a client or a person starts and owns, one shared server per repository on the loopback interface that ends when its last client leaves (docs/MCP.md) - not a slice of any other platform; there is no shared code
Limitations
- Majordomus is invoked by a person, a git hook, a worker following its instructions, or the worker's own session hooks. Those hooks run only at session and prompt boundaries, to record the session and hand it its briefing, and always let the worker continue: nothing inspects or refuses a tool call. Only Claude Code's hooks are wired; Codex and Gemini have hooks this tool has no adapter for yet.
- It measures tokens only in matched runs with and without it, and states a saving only
once its publication rule is met. It measures no money: cost is the harness's own
projection, not a bill.
docs/ECONOMICS.mdsays what else is not measured. - Scope overlap between worktrees is reported, never blocked. Where a repository opts into the mesh, an exclusive claim refuses an overlapping one on every linked machine; that is a lease, not a lock, and during a network split both sides can claim.
- Where a profile requires a regression test,
finishchecks only whether a test-shaped path was touched (atestorspecdirectory, a_test.or.spec.file name), not what the test proves. - The YAML subset is deliberately small: maps, lists, lists of maps, inline lists, quotes, comments. Tabs and odd indentation are refused; the Rust reader also refuses anchors, aliases and block scalars, which the shell reader takes as text. A flow map is not parsed as a map.
Roadmap
The roadmap is not written here. Milestones are canonical records under
.ai/repo/project/milestones/, ordered by the dependency graph between them, and every
view of them is derived from that graph:
majordomus plan roadmap # the sequence, with what is current and what is next
majordomus plan rgraph # the same graph, as a diagram
Rendered at the roadmap, with the
current state as a document in docs/PLAN_STATUS.md.
Each step is gated by the previous one being real, and that is an invariant rather than a
promise: a milestone whose dependencies are not accepted is blocked, and finishing every
issue inside it does not release it. docs/ROADMAP.md explains how the
ordering, the gate and the claim linkage are derived.
Interfaces
The Rust executable under apps/majordomus-cli/ exposes the same
.ai/ layer to programs, read-only, through several interfaces that are all derived from
one capability registry: a capability is defined once, in a typed descriptor or in a
declarative file with its JSON Schema, and MCP, HTTP, OpenAPI, Swagger UI, the Cockpit, the
command line and the generated reference are projections of it, so nothing is maintained
twice.
Every generated file in the repository comes out of the same executable: majordomus generate writes docs/generated/, share/allow/, the provider bootstraps AGENTS.md
and CLAUDE.md (from the policy and the templates), and the site's registry and product
datasets under site/data/registry/; majordomus generate --check says which of them is
stale, and CI refuses a merge or a deploy from a stale one. The website's own generator
consumes two of those files and nothing else of the crate, so just derive regenerates
every derived file of the repository in dependency order and just derive-check names
every stale one, whichever generator owns it. The owners of every truth and the direction
of generation are in
ADR 5
and the graph is drawn in docs/GITHUB_PAGES_ARCHITECTURE.md;
the site's Executable section —
the registry, every module and capability, the command line, the MCP surface, the HTTP API
and the benchmarks — is rendered from that dataset and from nothing typed by hand.
The website's front door is the same kind of projection. What the product does is stated
once, as one file per feature under .ai/repo/features/, holding the references that name
what the feature is made of and the editorial decisions nothing can infer; the interfaces
it is exposed through, the counts behind it, the operational moments it answers and its
route are derived from those references and refused as keys in the file. The homepage, the
feature pages and the
capability matrix name
no feature, module, command, provider or count of their own, and a stale product dataset
fails the build before it can be deployed. The contract is
docs/PRODUCT.md and the decision is
ADR 23.
just build # cargo build of apps/majordomus-cli (or: cargo build --manifest-path apps/majordomus-cli/Cargo.toml) just mcp # MCP on stdio for the client that spawned it; the first one in a repository is the shared server just serve # the shared server alone: the home page at /, the docs at /docs/, the Cockpit at /cockpit, Swagger UI at /swagger, /openapi.json, /mcp just home # open the running server's home page: every surface it serves just cockpit # open the running server's Cockpit in a browser just site-docs # build the documentation for the running server's /docs/ mount just cockpit-assets # build the Cockpit's stylesheet and vendor its pinned libraries (needs npm ci) just capabilities # every capability and its projections just derive # every derived file of the repository, in dependency order just derive-check # every committed derived file is current; exit 10 naming the stale ones just bench-coverage # every operation is a benchmark target; the denominator is the registry's just bench-run # time every operation: directly, over MCP (a real child), over HTTP (a real socket)
Declare once, derive everything. A capability is one capability! block in its
module's file; modules compose capabilities with module!, the root composes modules
with compose_modules!, and MCP, HTTP, OpenAPI, Swagger UI, the Cockpit, the command line,
the benchmark targets, the cache behaviour and the generated reference are derived from the
registry those blocks build. Adding a capability is: define the typed input and output
(with the input's benchmark cases), write the handler, add the block to its module, run
just derive and just validate. There is no step that edits an MCP registry, an
HTTP router, an OpenAPI document, Swagger, a benchmark inventory, a documentation table or
a page of the website, because none of those is written by hand: the capability's page,
its rows on the module, MCP, API and benchmark pages and its links come out of the same run. Every externally callable operation is
benchmarked through the real transports and every claim about speed is a recorded
measurement (docs/CAPABILITIES.md, ADR 4).
A control plane for a person. The same server renders the registry as pages at
/cockpit: what this repository holds, which capabilities exist and where each came from,
a form generated from every capability's own input schema that calls its real route, the
graphs the layer already implies (rule dependencies, decisions and what they put in force,
the shape of .ai/), and one health report whose every check names the engine that decided
it. It is a projection, not a second application: adding a capability adds its page, its
navigation entry, its search entry and its benchmark target with no edit to the Cockpit
(docs/COCKPIT.md,
ADR 12).
Work you can watch. A capability that reads every file of the layer takes longer than a
request should be held open for, so it can be run as an execution: it gets an id, a
lifecycle and a stream of typed events, and a browser, an agent or a terminal follows it
while it happens. majordomus run objects.verify prints each step as the handler reports
it; the Cockpit's Executions page shows the same run live over a WebSocket at /events and
restores it from the server on a reload; executions.* answer the same facts over HTTP and
MCP. There is no action registry and no second implementation: an execution runs a
capability of the one registry, through the one executor, and what a client may offer —
whether to ask before running it, whether a Cancel button will do anything — is read from
that capability's own descriptor
(docs/EXECUTIONS.md,
ADR 22).
One server per repository. The first majordomus mcp binds the loopback HTTP
projection beside its stdio session and logs the URL and every surface it serves (the home
page at /, the documentation at /docs/, the Cockpit at /cockpit, Swagger UI at
/swagger, the OpenAPI document, MCP over HTTP at /mcp); every later majordomus mcp in the same repository
attaches to it instead of starting another, and the server ends when its last client
leaves. It writes one file, a lease under .ai/local/state/mcp/, and nothing under the
tracked tree. Loopback is the default and no tracked file can move it; a machine that
should be reachable from another exports MAJORDOMUS_HTTP_HOST=0.0.0.0 in its own
environment, and every server started there binds it
(docs/MCP.md).
One web surface, discovered from its producer. What the server serves is not a list
anybody keeps. A surface — the home page, the documentation, the Cockpit, Swagger UI, the
OpenAPI document, the capability routes, MCP over HTTP, a generated test or benchmark
report — is discovered from the thing that produces it: the executable's own declarations,
the site's configuration, or a surface.json a producer writes beside its output. One
resolution then feeds the router, the home page at /, the web.surfaces capability, the
validator, the publication and the generated reference; a surface that appears tomorrow
appears in all of them with no registration anywhere.
/ the home page: every surface this process serves, grouped and linked
/docs/ this repository's documentation, the same Zola source GitHub Pages renders
/swagger Swagger UI over the OpenAPI document
/openapi.json the OpenAPI document, generated from the registry
/api/v1/ the capability routes; /api/v1/web/surfaces is this topology as JSON
/cockpit the registry rendered for a person
/mcp MCP over HTTP for a second client
/docs is the documentation and /swagger is Swagger UI; neither name may be repurposed
for the other, which project.web-surface-declared-once states and majordomus web validate enforces. majordomus web list and web explain say what exists and where every
value came from: docs/WEB.md,
ADR 13.
Clients start it themselves. .mcp.json (Claude Code),
.gemini/settings.json (Gemini CLI) and
.codex/config.toml (Codex) name bin/majordomus-mcp,
which builds the executable when it must and runs majordomus mcp. Open the repository in
any of them and the server is there; open it in two and they share one. Which tools the
distribution has an adapter for, what each reads and where each keeps its scratch
checkouts is one generated table, docs/generated/providers.md,
never a list written here.
Peers see each other. Every attached client is a peer, named by what it said in
initialize; majordomus_peers lists them and majordomus_announce tells the others what
a client is working on and which paths it expects to touch, so several clients in one
checkout can avoid colliding, out of the box.
Every use case is executed, not described. What a person does with the tool is one
file under .ai/repo/use-cases/, naming the commands, rules and claims it relies on and
carrying a scenario the tool runs against itself; the page it becomes shows that
execution, its maturity is observed from the evidence, and a public command no use case
runs is a gap doctor reports and finish refuses. majordomus usecase lists, runs,
tallies, traces and scaffolds them: docs/USE_CASES.md.
It reads what the repository declares it may. What a worker reads of the repository is
declared once in .ai/repo/scope.yaml (in: the sources, the tests, the layer; out, which
wins: version control, the local half, dependencies, build outputs, secrets, generated
assets, archives, images, video, PDF, database dumps, fixtures over a limit, binary
content), and the executable discovers, indexes and serves nothing outside it. Any path can
be asked about: majordomus scope <path> answers in or out and names the rule. The
declaration, the judgement and its gates: docs/SCOPE.md.
What is canonical, how a repository adds a kind with its schema without a code change, and
how the pieces fail: docs/CAPABILITIES.md; the MCP surface, the
shared server's lifecycle and the client configurations:
docs/MCP.md. The kinds and their schemas are read at run time from
share/kinds.yaml and share/schemas/, and the shell
tool's allow-lists under share/allow/ are generated from those schemas.
Contributing
Read CONTRIBUTING.md and the generated AGENTS.md.
The best contribution is often a deletion. Run bash test/run.sh (just test runs it with
the Rust gate and the site data check); every behaviour has a success and a failure case.
The justfile lists what a person runs here, routed to the Rust executable
wherever it can do the job and to the shell tool for the lifecycle; just alone lists the
recipes. This repository supervises itself: bin/majordomus doctor runs in its own
pre-commit hook and in CI.
The website is a projection of this repository rather than a second copy of it: every
page is generated from README.md, docs/, the policy skeleton and docs/CLAIMS.yaml,
and CI refuses to deploy a tree whose derived files are stale. Editing a generated file
is never the fix. See
docs/GITHUB_PAGES_ARCHITECTURE.md.
Origin and licence
Majordomus distils operational patterns learned while building Prismatic, a much
broader cognitive and epistemic platform. The relationship is one-way; no code or
vocabulary flows back. The evidence is in
docs/EXTRACTION_REPORT.md. MIT licence.
来源:GitHub · github.com