跳到正文
GitHub·· 8 天前AI 评分58

Majordomus 发布 AI 编码智能体监督控制层

korczis/prismatic-majordomus

AI 导读

Majordomus 发布一个面向 AI 辅助工作的轻量监督控制层,为仓库维护一份统一的智能体操作策略,并据此生成各工具读取的指令文件。它把任务、可触碰路径和交接记录保存在仓库而非对话中,在契约未满足时拒绝将工作标记为完成,且完全本地运行、不调用任何模型。

正文

Prismatic Majordomus

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 | sh

The 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 | sh

Then, 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 through rules vendor update.
  • Which files are generated: the projections list in the policy.
  • What "done" means: verification.finish_requires in the policy, plus per-profile verification.* fields.
  • Budget for the always-loaded file: context.always_loaded_budget_lines.
  • What must be wired: the enforcement list; doctor reconciles 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 mcp and serve are 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.md says 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, finish checks only whether a test-shaped path was touched (a test or spec directory, 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