protean docs
The protean docs command group has two subcommands:
protean docs generaterenders architecture documentation from a domain or an IR JSON file.protean docs previewstarts a live preview server for Protean documentation.
protean docs generate
Generate architecture diagrams and catalogs from a domain's Intermediate Representation.
# From a live domain
protean docs generate --domain=my_app --type=events
# From an IR JSON file
protean docs generate --ir=domain-ir.json --type=event-model
Options
--domain,-d: Path to the domain module. Mutually exclusive with--ir.--ir: Path to an IR JSON file. Mutually exclusive with--domain.--type,-t: What to generate. One of:clusters: aggregate cluster class diagrams.events: command-to-event flow diagrams plus downstream consumers.handlers: handler wiring diagrams.catalog: an event and command catalog (Markdown tables).event-model: the EventModeling slice timeline (see below).llms: a versionedllms.txtcontext pack (see below).agents: a versionedAGENTS.mdnegative-constraint pack (see below).all(default): every section exceptevent-model,llms, andagents.
--format,-f:markdown(fenced code blocks, the default) ormermaid(raw diagram source).mermaidis not supported forcatalog,llms, oragents.--output,-o: Write to a file instead of stdout.--cluster: Filter to a single cluster FQN (only with--type=clustersor--type=all).--annotations: Path to an annotations TOML file (only with--type=event-model). Defaults to.protean/annotations.tomlwhen present. See Annotating the event model.
The event model slice timeline
--type=event-model renders the domain as an
EventModeling slice timeline. Each aggregate
cluster becomes one slice that reads left to right:
- Command(s) that trigger the aggregate.
- Aggregate (state) that decides and holds state.
- Event(s) the aggregate raises (fact events are omitted).
- Read models and automations that consume those events. Projectors are
read models, drawn as cylinders. Event handlers and process managers are
automations, drawn as hexagons, so a read model and an automation are told
apart at a glance. A process manager also shows its
start/endlifecycle, both in its node label and on the edge from the event.
Consumers are matched to a slice's events by the event type, so the whole diagram is derived from the IR alone.
In Markdown output each slice leads with a structural Given-When-Then before the diagram:
- Given the aggregate the slice is about.
- When the cluster's commands (the triggers).
- Then the cluster's non-fact events (the results).
The GWT is derived structurally from the IR. It is slice-level: a slice with
several commands or events lists them all on the When and Then lines rather
than pairing each command with the event it produces. Commands-only or
events-only slices drop the line they have nothing for. This GWT is a
Markdown-only lead; the --format=mermaid output stays a bare flowchart.
# One combined flowchart of all slices
protean docs generate --domain=my_app --type=event-model --format=mermaid
# One GWT-led diagram per slice, as Markdown
protean docs generate --domain=my_app --type=event-model
Unlike the other diagram types, event-model is its own view and is not
included in --type=all.
The llms.txt context pack
--type=llms renders a versioned context pack in the
llms.txt convention, aimed at an LLM agent working on a
Protean project. It has two layers:
- Framework layer (always present): an H1 naming Protean and the installed version, a one-line summary, and a "Core documentation" section of links to the published docs site for the core areas: aggregate clusters, events, command and event handlers, projections, and event evolution.
- Project overlay (present only with a source): a section derived from the IR listing the project's own aggregate clusters (with their commands, events, and handlers) and its projections.
--type=llms is the one type that runs with no --domain and no --ir: with
no source it emits the framework layer alone; with a source it adds the project
overlay. Like event-model, it is its own view and is not part of --type=all.
The output is byte-stable: for a given Protean version and project structure, generating twice yields identical bytes. The overlay reads only structural parts of the IR, never volatile fields like the build timestamp, so it does not churn between runs.
# Framework layer only (no source needed)
protean docs generate --type=llms
# Framework layer plus the project overlay
protean docs generate --domain=my_app --type=llms --output=llms.txt
The AGENTS.md constraint pack
--type=agents renders a versioned
AGENTS.md negative-constraint pack: the hard rules an agent
working on a Protean project must not break. It has one rule for every
error-level diagnostic code Protean can raise. Each rule is a prohibition built
from that code's registered meaning, rationale, and fix, and it ends with
the code so a reader can trace it back:
- **Do not** write code that causes this error: <meaning> <rationale> To
comply: <fix> (`CODE`)
Beyond the fixed wording in that template (the lead-in, To comply:, and the
code in backticks at the end), the generator adds nothing: it drops the three
registry fields in as they are written, joined with single spaces. The rule
reads as full sentences because each registry field already ends with a period.
Only error-level codes appear: advisory warning and info codes are style and
design nudges, not hard rules, so they are excluded. The set of rules is coupled
to the live registry, so adding or removing an error code changes the file.
--type=agents needs no --domain and no --ir: it derives only from the
registry and the installed Protean version, so it runs with no source. Like
llms and event-model, it is its own view and is not part of --type=all.
The output is byte-stable: for a given Protean version it renders identical bytes, because the header is version-stamped, the registry traversal is sorted, and nothing reads a timestamp.
A project generated with protean new already ships this file as AGENTS.md at
its root, kept byte-identical to this command's output. Regenerate it after
upgrading Protean:
# Print the constraint pack
protean docs generate --type=agents
# Refresh the project's AGENTS.md
protean docs generate --type=agents --output=AGENTS.md
Annotating the event model
The event model carries structure, not the business context a person adds: why
a slice exists, the rule behind it, which team owns it. Those notes live outside
the generated output, in .protean/annotations.toml, and merge back in on
render. Keeping them out of the diagram means the model stays disposable and the
notes stay under version control next to the code they describe. The file's
shape is recorded in ADR-0032.
The file has a top-level [annotations] map keyed by element FQN. Each entry
carries a required note (free text, the business context) and an optional
owner (the team or person accountable). The FQN is the value
protean.utils.fqn computes, module.QualifiedName; in a project generated
from the canonical layout, an Order aggregate in
src/myproj/example/aggregate.py has the FQN myproj.example.aggregate.Order.
The FQN must be quoted, because TOML reads its dots as table separators
otherwise:
[annotations."myproj.example.aggregate.Order"]
note = """
Orders are the fulfillment boundary. An order cannot ship until payment
clears, so PaymentConfirmed gates the shipment slice.
"""
owner = "Fulfillment"
On render, each note merges into every slice that draws the element it keys. A note on an aggregate, a command, an event, or a consumer (a projector, event handler, or process manager) shows after that slice's Given-When-Then and before its diagram. A consumer drawn in two slices shows its note in both. Because the key is the element's FQN, a note stays attached across a content change (adding a field, reordering elements, regenerating the model) and breaks on an identity change (renaming or moving the element), which changes the FQN.
A key that matches no drawn element is listed in an "Unmatched annotations" section at the end of the render, so a note orphaned by a rename or a typo stays visible and gets re-keyed. A fact event is filtered from the event model, so it draws no node; a note keyed to one is reported unmatched.
--annotations <path> reads the file from a non-default location. An explicit
path that does not exist is an error, as is a malformed file: either aborts the
command before any output is written. With no annotations file present, the
default path absent and no --annotations given, the render is exactly what it
is without the feature.
# Merge notes from the default .protean/annotations.toml
protean docs generate --domain=my_app --type=event-model
# Read notes from a non-default path
protean docs generate --domain=my_app --type=event-model \
--annotations=docs/event-model-notes.toml
Notes are Markdown prose and cannot sit inside a raw flowchart, so
--format=mermaid leaves the diagram body unchanged and appends only the
unmatched-annotation report, the one piece that belongs at the end of the
render.
protean docs preview
The protean docs preview command starts a live preview server for Protean
documentation. This allows you to view changes in real-time as you edit.
Usage
protean docs preview [OPTIONS]
Options
--help: Shows the help message and exits.
Running a Preview Server
To start the live preview server for your project's documentation, run the command without any additional options:
protean docs preview
This will start a local server, usually accessible via a web browser at a URL
such as http://localhost:8000. The exact URL will be displayed in your
command line interface once the server is running:
INFO - Building documentation...
INFO - Cleaning site directory
INFO - Documentation built in 0.56 seconds
INFO - [09:45:08] Watching paths for changes: 'docs', 'mkdocs.yml'
INFO - [09:45:08] Serving on http://127.0.0.1:8000/