From e83bfe1a0f74ec9f904affd56d35b8a9ccb3a258 Mon Sep 17 00:00:00 2001 From: Joe Beda Date: Wed, 23 Sep 2026 21:50:52 -0700 Subject: [PATCH] docs: align guides with current release state Signed-off-by: Joe Beda --- .claude/skills/SOURCES.md | 12 + .claude/skills/tech-writer/SKILL.md | 84 ++++++ .../tech-writer/references/anti-patterns.md | 64 +++++ .../tech-writer/references/explanation.md | 31 ++ .../tech-writer/references/how-to-guides.md | 41 +++ .../tech-writer/references/reference.md | 39 +++ .../tech-writer/references/style-guide.md | 267 ++++++++++++++++++ .../tech-writer/references/tutorials.md | 45 +++ CLAUDE.md | 5 + docs/02-getting-started.md | 129 ++++----- docs/03-understanding-your-model.md | 39 ++- docs/04-reading-the-diagrams.md | 13 +- docs/05-parking-garage/index.md | 42 +-- docs/06-schema-reference.md | 42 +-- docs/07-cli.md | 93 +++--- docs/08-github-action.md | 49 ++-- docs/09-local-development.md | 15 +- docs/10-vendoring.md | 56 ++-- docs/index.md | 23 +- internal/schema/schema.go | 10 +- 20 files changed, 815 insertions(+), 284 deletions(-) create mode 100644 .claude/skills/tech-writer/SKILL.md create mode 100644 .claude/skills/tech-writer/references/anti-patterns.md create mode 100644 .claude/skills/tech-writer/references/explanation.md create mode 100644 .claude/skills/tech-writer/references/how-to-guides.md create mode 100644 .claude/skills/tech-writer/references/reference.md create mode 100644 .claude/skills/tech-writer/references/style-guide.md create mode 100644 .claude/skills/tech-writer/references/tutorials.md diff --git a/.claude/skills/SOURCES.md b/.claude/skills/SOURCES.md index 1bdf612..ef1a0b6 100644 --- a/.claude/skills/SOURCES.md +++ b/.claude/skills/SOURCES.md @@ -8,6 +8,18 @@ origin of borrowed text is auditable. recording the upstream source, the commit it was taken at, its licence, and what was changed. Skills written from scratch for modelith don't need an entry. +## tech-writer + +- **Upstream:** [`stacklok/mecatl`](https://github.com/stacklok/mecatl), + `.claude/skills/tech-writer/`; copied through local checkout + `../mecatl-clean`. +- **Taken at:** skill commit `33a3747d9008691d4d51a872c9e82c050c43fafa` + (2026-09-23), from checkout `d96de1fb6f40a713754dd3c65da8bcecf45a920b`. +- **Licence:** Apache License 2.0; see the upstream checkout's `LICENSE`. +- **Changes:** adapted the model-specific references to point at + `docs/_docs-conventions.md`, `CLAUDE.md`, and `.claude/rules/`; retained the + upstream body and `references/` directory otherwise. + ## grilling - **Upstream:** [`mattpocock/skills`](https://github.com/mattpocock/skills), diff --git a/.claude/skills/tech-writer/SKILL.md b/.claude/skills/tech-writer/SKILL.md new file mode 100644 index 0000000..22fb0f3 --- /dev/null +++ b/.claude/skills/tech-writer/SKILL.md @@ -0,0 +1,84 @@ +--- +name: tech-writer +description: > + Use when writing or substantively editing user-facing documentation: drafting a new page, rewriting or restructuring an existing one, adding a major section, or turning engineering material (PR descriptions, specs, release notes, rough notes) into docs. Writes clear, focused technical documentation following the Diataxis framework and modelith's local documentation conventions. Use it even when the request doesn't mention writing quality; it governs how documentation gets written. Not for editorial review of finished work. +--- + +# Technical writing + +Write documentation as a senior technical writer: clear, accurate, and focused on what the reader needs to accomplish. Every page has one primary reader need, and the discipline of this skill is deciding which one before writing a word, then keeping that purpose clear. Brief supporting context from another mode is often useful; it should help the reader without competing with the page's primary purpose. + +Everything in this skill and its references is guidelines, not rules. Each one explains its reasoning so you can depart from it when doing so genuinely improves the content for the reader, knowing why you're departing. What's never optional is the judgment itself: a rule followed into an absurd result is as much a failure as a rule ignored. + +## Canonical sources + +Don't duplicate guidance; read it from where it lives: + +1. **The local [style guide](references/style-guide.md)** provides prose and + style guidance. +2. **[`docs/_docs-conventions.md`](../../../docs/_docs-conventions.md)** owns + public-documentation placement, links, and verification for modelith. +3. **`CLAUDE.md` and `.claude/rules/`** own contributor-documentation and + repository-process guidance. +4. **The mode references** in [`references/`](references/) provide Diataxis + discipline and write-time anti-patterns. + +## Workflow + +1. **Classify.** Use the compass below to decide the page's primary mode. Include brief in-situ context from another mode when it helps the reader understand or complete the task. Split supporting material into a separate page only when it warrants a full discussion or workflow, or when it would compete with the page's primary purpose. Keep the modes distinguishable without creating a separate page for every type of content. +2. **Place.** For public documentation, follow + [`docs/_docs-conventions.md`](../../../docs/_docs-conventions.md). For + contributor documentation, follow `CLAUDE.md` and the applicable + `.claude/rules/` guidance. Update the owning page; do not append the same + feature narrative to several pages. Create a page only for a distinct reader + need. +3. **Read.** Read the reference file for your mode, plus [the write-time anti-patterns](references/anti-patterns.md), plus the style guide sections your task touches. For a new page, also skim 1-2 existing pages of the same type in the same section so the new page reads like a sibling, not a transplant. +4. **Draft.** Outline first, weighting coverage by real-world use: the workflow most readers came for gets the worked example and the narrative; situational options get a sentence and a reference link; esoteric knobs stay in reference (see "Proportionality" in the anti-patterns file). Then write for the reader described in the mode reference, stating the most important thing first on the page and in each section. +5. **Self-check.** Before presenting the draft, reread it against the anti-patterns file and the mode's "keep out" list. Cut what fails. For substantial new content, use an independent editorial review when available; for small edits, the self-check is enough. + +## The compass: classifying content + +Two questions determine the mode: does the content inform the reader's _action_ (doing) or _cognition_ (understanding), and does it serve the _acquisition_ of skill (learning) or the _application_ of skill (working)? + +| Content... | ...serves skill... | Mode | It is... | +| ----------------- | ------------------ | ----------- | ------------ | +| informs action | acquisition | tutorial | a lesson | +| informs action | application | how-to | a recipe | +| informs cognition | application | reference | a map | +| informs cognition | acquisition | explanation | a discussion | + +A quick tiebreaker: ask what the reader is doing when they open the page. Learning by following along means tutorial. Getting a real task done means how-to guide. Looking something up means reference. Trying to understand why or how something works means explanation. + +## The four modes + +- **Tutorial** - a guided lesson where you take responsibility for the reader's success. Quickstarts and end-to-end getting-started pages. Read [the tutorial guidance](references/tutorials.md). +- **How-to guide** - a recipe for a competent user with a real task. Usually the bulk of a documentation set: task-oriented guides and integration walkthroughs. Read [the how-to guidance](references/how-to-guides.md). +- **Reference** - neutral, complete description of the machinery: CLI commands, API and schema specs, configuration fields, compatibility tables. Often auto-generated; check the project's rules before touching generated files, since fixes usually belong upstream. Read [the reference guidance](references/reference.md). +- **Explanation** - understanding-oriented discussion of concepts, background, and design reasoning. Concept pages and product introductions. Read [the explanation guidance](references/explanation.md). + +## Reference files + +| When you are... | Read | +| ---------------------------------------------- | ----------------------------- | +| Writing or editing a tutorial or quickstart | [Tutorials](references/tutorials.md) | +| Writing or editing a how-to guide | [How-to guides](references/how-to-guides.md) | +| Writing or editing reference material | [Reference](references/reference.md) | +| Writing or editing concept/explanation content | [Explanation](references/explanation.md) | +| Drafting anything (always, before self-check) | [Write-time anti-patterns](references/anti-patterns.md) | +| Checking style, structure, or terminology | [Style guide](references/style-guide.md) | + +## Self-check + +Before presenting a draft, verify: + +- [ ] The page has a clear primary mode. Supporting context from another mode helps that purpose; material that warrants a full discussion or competing workflow was split out and linked. +- [ ] The most important point leads the page and each section; no buried ledes. +- [ ] Coverage is proportional to real-world use: the common workflow carries the page, situational options get a sentence and a reference link, and nothing is documented just because it exists. +- [ ] Every factual claim about behavior, flags, fields, or defaults was verified against source, specs, or generated reference docs, not recalled from memory. Living docs describe implemented behavior, not merely an approved plan. +- [ ] Outdated and duplicate text was replaced or deleted. Only unique, verified knowledge was migrated; implementation chronology stays in PRs/Git rather than a catch-all notes page. +- [ ] Code examples work as written: real values for fixed things, `` placeholders for reader-supplied values, reserved domains (`example.com`) in URLs. +- [ ] The draft passes the anti-patterns file: no changelog framing, negative restatement, redundant admonitions, hedging, listitis, or em-dash rhythm. +- [ ] Front matter (where the site uses it) has `title` and a `description` whose first 70 characters stand alone. +- [ ] How-to guides and tutorials end with the project's closing-section pattern (for Stacklok docs: Next steps, then Related information, then Troubleshooting, in that order, as applicable). +- [ ] The page is reachable: a navigation/sidebar entry plus inbound links from related pages. +- [ ] Terminology matches the style guide's word list. diff --git a/.claude/skills/tech-writer/references/anti-patterns.md b/.claude/skills/tech-writer/references/anti-patterns.md new file mode 100644 index 0000000..6e88778 --- /dev/null +++ b/.claude/skills/tech-writer/references/anti-patterns.md @@ -0,0 +1,64 @@ +# Write-time anti-patterns + +[Technical writing skill](../SKILL.md) + +These are the habits that most often degrade documentation drafts. Read this list before drafting and again during self-check. Most of them are natural tendencies of LLM-generated prose, which is exactly why they need active resistance at write time rather than cleanup at review time. + +Apply the same catalog when reviewing documentation. When a new recurring +pattern warrants repository guidance, update this file rather than creating a +second review-only copy. + +## Proportionality + +The deepest form of over-documentation isn't repetition; it's flat coverage. Fix it at the outline stage, before any prose exists. + +**Flat option coverage.** A guide that gives every option, flag, and field equal billing reads like a control panel, not a recipe. The reader can't tell the paved road from the escape hatch, and the workflow most readers came for drowns in the long tail. Budget coverage by the share of readers who will actually use something: the common path gets the worked example and the narrative; a legitimate but situational option gets a sentence naming the situation and a link to reference; an esoteric knob gets nothing in the guide at all, because the reference already describes it and the readers who need it know to look. Two tests for any option you're about to document in a guide: + +- If you were helping a colleague do this task at their desk, would you bring this option up unprompted? If not, it doesn't belong in the guide's main path. +- Does the option change what the reader _does_, or just what a value _is_? Changed workflows can earn guide coverage; values that exist belong in reference. + +**Completeness reflex.** The instinct that a feature isn't fully documented until every capability appears in a guide somewhere. Completeness is reference's job, and the generated reference already provides it. A guide that covers less but is followable end to end serves more readers than one that covers everything. When an engineer's PR or spec lists ten configuration fields, that's an input inventory, not an outline; the outline comes from what readers are trying to do. + +## Rhythm and punctuation + +**Em dashes, and their disguises.** Never use `—` or `–`. Just as important: don't mechanically swap in a spaced hyphen or a comma while keeping the same "clause - punchy addendum" rhythm; that cadence reads as AI-generated even with the character fixed. Actually restructure: split the sentence, subordinate the clause, or cut the addendum. Spaced hyphens are only for list-style separators ("Topic - description" entries). + +**Symmetric triads and balanced pairs.** "Fast, secure, and reliable." "Not just X, but Y." These rhetorical rhythms are filler in technical prose. If the three adjectives each carry a verifiable fact, keep the facts and lose the drumbeat; usually only one of them matters. + +## Framing + +**Changelog framing.** "Starting in v0.41", "previously", "now supports", "moved from X to Y". Docs state current behavior; release notes tell the story of change. This leaks in most when drafting from a PR or release diff: you're looking at a delta, but the reader needs a state. Write the page as if the behavior had always been this way. (Narrow exception for breaking changes: see "Document current behavior" in the style guide.) + +**Negative restatement.** "Uses X, not Y." "Don't point it at the public endpoint." If the negation carries no new fact, cut it; if it carries one, fold that fact into the positive statement ("point it at the internal endpoint, which..."). + +**Buried lede.** The key fact ("this is automatic", "this requires Kubernetes") arrives after paragraphs of preamble. Lead every page and every section with the thing the reader most needs; the background can follow for those who keep reading. + +**Dramatized limitations.** State gaps and limitations accurately and neutrally ("the UI doesn't yet expose this setting; use the CLI"), without apology, alarm, or spin in either direction. + +**Marketing adverbs.** "Simply", "easily", "seamlessly", "powerful". If it's simple, the short instruction demonstrates that; saying so just mocks the reader for whom it isn't. + +## Substance + +**Hedging.** "May", "might", "could potentially", "should generally". Look up what actually happens and state it. If behavior is genuinely conditional, name the condition instead of hedging. + +**Hedged lists.** "Clients such as VS Code and Cursor..." when the supported set is knowable. State the full list or link to the canonical reference (usually a compatibility page); "such as" invites the reader to guess. + +**Over-explaining.** Restating what a command obviously does, defining concepts the audience (developers and DevOps professionals) already has, narrating the obvious consequence of the previous sentence. Trust the reader and cut. + +**PR jargon leak.** "Consumers", "surface area", "wire up", "shapes". These come from the engineering artifact you're drafting from, not the reader's vocabulary. Name the actual components, fields, and values. + +**Unverified facts.** Never write a flag name, field, default, or version constraint from memory. Verify against source, the schema, or the generated reference, and cite where it came from when presenting the draft. + +**Placeholder examples.** `my-server`, `example-org`, `foo` where a real value exists. Use real values for fixed things (commands, image names, registry servers), `` for values the reader supplies, and reserved domains (`example.com`) in URLs, never real domains. + +## Structure + +**Listitis.** Bullets are for genuinely enumerable items. Consecutive single-sentence bullets that each begin with a bolded phrase are prose wearing a costume; readers can't follow an argument chopped into fragments. Write connected paragraphs for reasoning and comparisons (or a table when the items are truly parallel facts). + +**Mirror-image sections.** "When to use X" followed by "When to use Y" that just inverts it. Write the comparison once, as a table or one honest paragraph of trade-offs. + +**Admonition abuse.** An admonition must add information beyond the adjacent prose. If a note restates or negates what the paragraph just said, cut it. If it contains the only documentation of a feature, a worked example, or a full config block, it isn't a note; promote it to a section with a heading. One or two admonitions per page is the norm. + +**Heading proliferation.** A heading per paragraph makes a page read like an outline and bloats the ToC. Sections need enough substance to deserve a name; merge stubs. + +**Duplicated content.** Detailed content lives in exactly one place; other pages link to it with a line of context. If you're pasting a paragraph you wrote for another page, stop and link instead. diff --git a/.claude/skills/tech-writer/references/explanation.md b/.claude/skills/tech-writer/references/explanation.md new file mode 100644 index 0000000..5e11b8f --- /dev/null +++ b/.claude/skills/tech-writer/references/explanation.md @@ -0,0 +1,31 @@ +# Writing explanation + +[Technical writing skill](../SKILL.md) + +Explanation is a discussion. The reader is away from the keyboard, or at least away from the task, and wants to deepen their understanding: how does this work, why is it designed this way, how does it relate to the alternatives? Explanation is the mode that serves study rather than work, and it's the only mode where context, background, opinion, and trade-offs belong. + +Explanation typically lives in a concepts section (protocol primers, architecture overviews, security frameworks) and in each product section's Introduction page, which explains what the product is and who it's for. + +## Principles + +**Answer "why", not "how".** Explanation provides the reasoning, history, constraints, and mental models behind the machinery: why ToolHive runs MCP servers in containers, how the authorization pieces fit together, when vMCP makes sense. The moment you're writing numbered steps, you've left the mode. + +**Bound the topic.** A useful test: the page should make sense with "About" in front of its title ("About network isolation"). If it wouldn't, the scope is fuzzy. Say early what the page covers, and keep it there; explanation has no natural task boundary, so unbounded pages sprawl. + +**Make connections.** Explanation earns its keep by joining things: how a feature relates to its neighbors, to the broader ecosystem, to what the reader already knows from elsewhere. Comparisons, context, and even relevant history (why the ecosystem settled on a pattern) are welcome here in a way they're welcome nowhere else. Weigh that against the "document current behavior" rule in the style guide: product-version changelog narration is still out, but design rationale and ecosystem background are fair game. + +**Discuss trade-offs and admit alternatives.** Explanation is where "when to use X vs. Y" content belongs, with honest weighing rather than a sales pitch. State limitations accurately and neutrally; don't dramatize them, and don't paper over them. + +**Opinion is allowed; keep it justified.** Perspectives and recommendations are legitimate here ("for production deployments, the operator is the better fit because..."), provided the reasoning comes with them. + +**Serve understanding, not completeness.** Explanation doesn't need to mention every field or cover every case; that's reference's job. Choose the details that build the mental model and link out for the rest. + +## Keep out of explanation + +- Step-by-step instructions (link to the how-to guide) +- Exhaustive technical description, field lists, option tables (link to reference) +- Anything the reader must do; explanation should be safely skippable by someone who just wants to get the task done + +## Structure + +Explanation is prose-first. Headings mark the major facets of the topic, and paragraphs, not bullet fragments, carry the reasoning; connected argument is the whole point of the mode, and bullets break the connections. Diagrams (Mermaid) help when the relationships are structural. Close with Next steps or Related information pointing to the quickstart or guides where the reader applies the understanding. diff --git a/.claude/skills/tech-writer/references/how-to-guides.md b/.claude/skills/tech-writer/references/how-to-guides.md new file mode 100644 index 0000000..edd4971 --- /dev/null +++ b/.claude/skills/tech-writer/references/how-to-guides.md @@ -0,0 +1,41 @@ +# Writing how-to guides + +[Technical writing skill](../SKILL.md) + +A how-to guide is a recipe. The reader is a competent user with a real task in front of them: they know what they want to achieve and roughly what they're doing, and they need reliable directions for this specific goal. Unlike a tutorial's learner, they can adapt, fill small gaps, and recover from minor surprises. Respect that competence. + +How-to guides are usually the bulk of a documentation set: the task-oriented guide pages for each product area and the third-party integration guides. + +## Principles + +**Address the reader's problem, not the tool's features.** Name the page after the task ("Customize permission profiles", "Send telemetry to Datadog"), not the mechanism ("The --permission-profile flag"). Organize by what the reader is trying to do; a guide that walks through a feature's options in the order the code defines them is reference material wearing the wrong hat. + +**Solve one task per page.** A guide that covers several loosely related tasks serves none of them well and is hard to find. If a page needs "and" in its title, it probably needs splitting. + +**Prefer usability over completeness.** A how-to guide gets the reader to a working result; it doesn't enumerate everything the feature can do. Cover the common path thoroughly, cover the most likely failure the reader will hit, and link to reference material for the full option surface. An incomplete guide the reader can follow beats a complete one they can't. + +**Sequence the actions.** The heart of a guide is an ordered series of steps toward the goal. Keep each step an action; put decisions the reader must make at the point they must make them, with just enough context to choose ("Use the `sse` transport if your client doesn't support streamable HTTP"). + +**Omit teaching and background.** The reader is working, not studying. A sentence of orientation is fine; paragraphs of theory are not. Link to concept pages for the why and to reference material for the details, and keep the guide moving. + +**Make prerequisites operational.** Don't just list what must exist; say what state it must be in and what that implies ("keep this command running in a separate terminal while you complete the next section"). + +**Scope to this project's job.** For guides involving third-party tools or services, the guide's job is getting the thing working with this product. Upstream quirks and caveats get at most a line and a link to the upstream docs; reproducing upstream documentation makes the page long and instantly stale. + +## Keep out of how-to guides + +- Conceptual explanation beyond a sentence of orientation (link to concept pages) +- Exhaustive flag, field, or option listings (link to reference material) +- Teaching-style hand-holding ("congratulations!", restating what a command obviously does) +- Multiple unrelated tasks +- Upstream product documentation restated at length + +## Structure + +1. Front matter: `title` and `description` (see the style guide) +2. A sentence or two on what the guide accomplishes and when you'd want it +3. Prerequisites, with operational context +4. The steps, organized by the reader's workflow; use tabs (where the site supports them) for genuinely parallel variants (UI vs. CLI, macOS vs. Windows), not for optional extras +5. Verification: how the reader confirms it worked +6. Next steps (required: 1-3 links following the reader's journey, for example: install, use, secure, operate, optimize) +7. Related information, then Troubleshooting, in that order, if applicable diff --git a/.claude/skills/tech-writer/references/reference.md b/.claude/skills/tech-writer/references/reference.md new file mode 100644 index 0000000..41e04aa --- /dev/null +++ b/.claude/skills/tech-writer/references/reference.md @@ -0,0 +1,39 @@ +# Writing reference material + +[Technical writing skill](../SKILL.md) + +Reference is a map. The reader is working and needs to look something up: a flag, a field, a default, a supported version. They consult reference material the way they consult a dictionary; nobody reads it front to back. Its entire value is that the reader can trust it and find things in it fast. + +Reference material typically covers CLI commands, API and schema specs, configuration fields, and compatibility tables, either in a dedicated reference section or as reference pages inside product sections. + +**Check what's auto-generated first.** CLI reference pages, CRD reference pages, and API specs are often generated from upstream sources; check the project's conventions (CLAUDE.md or a docs README usually lists the paths and rules). Never hand-edit generated files: fixes go upstream, and hand-written framing usually has a designated home the generator preserves. This reference file applies to hand-written reference pages and to reference sections within other pages. + +## Principles + +**Describe, and do nothing but describe.** State what exists, what it does, its type, its default, its constraints. Instruction, persuasion, opinion, and explanation all belong elsewhere. The single job is accurate description. + +**Be austere and neutral.** Plain, factual statements in consistent patterns. Reference material is where dry writing is a virtue: the reader is scanning, and flourishes slow them down. Say each thing once, the same way you said the analogous thing about the neighboring field. + +**Mirror the structure of the machinery.** Organize reference the way the thing itself is organized: fields in the order the schema defines them, subcommands under their parent command, one section per resource. The reader navigates the docs with their mental map of the product; the two should match. + +**Be complete and accurate above all.** A reference that omits a field or describes a stale default is worse than none, because readers stop trusting all of it. Verify every value against the current source or schema, never memory. When the product changes, the reference changes; state only current behavior. + +**Consistency beats elegance.** Identical things get identical treatment: same column order in every table, same sentence pattern for every field description, same placeholder conventions. Predictability is what makes reference scannable. + +**Examples illustrate; they don't teach.** A short example showing a field's valid values or a typical stanza is good reference. A worked scenario with narrative is a how-to guide leaking in; move it. + +## Keep out of reference + +- Step-by-step instructions (link to the how-to guide instead) +- Explanations of why the design is the way it is (link to concept pages) +- Recommendations and opinions ("we suggest...", "the best option is...") +- Marketing language; reference has no adjectives to sell +- Changelog framing; describe the current version of the machinery + +## Structure + +Reference structure follows the shape of the thing described, so there is no fixed skeleton. Common patterns: + +- Tables for enumerable facts (clients, versions, fields, defaults) with explanation kept to prose around the table, not crammed into cells +- One heading per command, resource, or field group, in the product's own order +- Front matter `title` and `description` like every page; a "Related information" closing section linking to the guides that use this machinery diff --git a/.claude/skills/tech-writer/references/style-guide.md b/.claude/skills/tech-writer/references/style-guide.md new file mode 100644 index 0000000..e93c982 --- /dev/null +++ b/.claude/skills/tech-writer/references/style-guide.md @@ -0,0 +1,267 @@ +# modelith documentation style guide + +[Technical writing skill](../SKILL.md) + +This guide is adapted from Stacklok's [shared tech-writer skill](https://github.com/stacklok/claude-plugins/tree/main/plugins/docs/skills/tech-writer) and kept in this repository so every contributor and coding harness can apply the same writing guidance. For modelith documentation ownership, links, and verification, follow [`docs/_docs-conventions.md`](../../../../docs/_docs-conventions.md). + +## Content framework + +We follow the [Diátaxis framework](https://diataxis.fr/#) of documentation structure, which defines four distinct types of documentation: tutorials, how-to guides, reference, and explanation. + +This guide does not seek to reproduce the definitions and background found on the Diátaxis site. The best starting point for background is [Applying Diátaxis](https://diataxis.fr/application/). The tech-writer skill's mode references cover how each type is written. + +## Writing style + +This list is not exhaustive, it is intended to reflect the most common and important style elements. For a more comprehensive guide that aligns with our style goals, or if you need more details about any of these points, refer to the [Google developer documentation style guide](https://developers.google.com/style). + +### Language + +The official language is **US English**. + +Avoid slang and colloquial expressions. Use clear, straightforward language and avoid overly complex jargon to make content accessible to a wide audience. + +Translate engineering shorthand into concrete, reader-facing terms. Language from PR descriptions and commit messages ("consumers", "shapes", "surface area") rarely belongs in docs; name the actual components, fields, and values instead. + +### Tone and voice + +Strive for a casual and conversational tone without becoming overly informal. We aim to be friendly and relatable while retaining credibility and professionalism, approachable yet polished. + +#### Active voice + +Use **active voice** instead of passive voice. Active voice emphasizes the subject performing the action, making the writing more direct and engaging. Passive voice focuses on the recipient of the action rather than the actor, often resulting in unclear sentences and misinterpretation of responsibility. + +:white_check_mark: Yes: Click **Install** to install the MCP server.\ +:x: No: MCP server is installed when the "Install" button is clicked. + +:white_check_mark: Yes: Set the `debug` flag to `true` to enable verbose logging.\ +:x: No: Verbose logging is enabled when the `debug` flag is set to `true`. + +#### Speak to the reader + +Address the reader using the **second person** ("you", "your"). Avoid the first person ("we", "our") and third person ("the user", "a developer") in task-oriented guidance. + +First-person plural is appropriate in explicitly labeled roadmap or direction content when it communicates project intent, for example, "We are working toward." Do not use it to lead readers through instructions. + +### Document current behavior + +Docs describe how the product works today; they are not a changelog or upgrade guide. + +Don't narrate history or transitions. Avoid "Starting in vX.Y", "previously", "changed from X to Y", "new in this release", and version-conditional notes. Release notes and blog posts tell that story; guides and references state only the current behavior. Upgrade sequencing and migration caveats likewise belong in release notes, not inline in how-to guides. + +:white_check_mark: Yes: The JSON-RPC error code for rate limiting is `429`.\ +:x: No: The JSON-RPC error code moved from `-32029` to `429` in v0.41.0. + +Prefer positive statements. Say what the product does and what the reader should do; don't restate a positive statement in negative form. If a negation carries a genuinely new fact, fold that fact into the positive statement. + +:white_check_mark: Yes: ToolHive uses the registry policy's `server_api_url` value.\ +:x: No: ToolHive reads `server_api_url`, not `api_url`. + +**Exception for breaking changes**: a breaking change or major behavioral change (a changed default, behavior that silently differs for existing setups) may carry a clearly labeled, versioned admonition (e.g. `:::info[Changed in v0.30.1]`) when upgraders need an explanation or action they can't infer from an error message. Keep the surrounding prose standalone about current behavior; the admonition carries only the upgrade delta and action. Remove these notes after a few releases, once the upgrade audience has moved on. Changes that fail loudly at startup with an obvious cause don't qualify. + +Large migrations (API version promotions, multi-field removals) get a dedicated migration guide; guide pages link to it with a short pointer rather than carrying the details inline. + +Deprecation notices are current state, not changelog. Documenting that a feature is deprecated, still read but warned about, or scheduled for removal is fine. + +### Capitalization + +Capitalize **proper nouns** like names, companies, and products. Generally, **don't** capitalize features or generic terms. For non-Stacklok terms, follow the norms of the third-party project/company (ex: npm is stylized in lowercase, even when it begins a sentence). + +:white_check_mark: Yes: Organize MCP servers into groups\ +:x: No: Organize MCP Servers into Groups + +Use **sentence case** in titles and headings. + +:white_check_mark: Yes: Configuration file structure\ +:x: No: Configuration File Structure + +Use `` to indicate placeholder text/parameters, where the reader is expected to change a value. + +### Punctuation + +**Oxford comma**: use the Oxford comma (aka serial commas) when listing items in a series. + +:white_check_mark: Yes: ToolHive requires minimal CPU, memory, and disk space.\ +:x: No: ToolHive requires minimal CPU, memory and disk space. + +**Quotation marks**: use straight double quotes and apostrophes, not "fancy quotes" or "smart quotes" (the default in document editors like Word/Docs). This is especially important in code examples where smart quotes often cause syntax errors. + +**Dashes**: avoid em dashes (`—`) and en dashes (`–`). They are hard to type, easy to miss in editors, and have proliferated with AI-generated content. Instead, rephrase naturally: use commas, split into two sentences, or restructure. If a separator is truly needed (for example, between a link and its description in a list), use a spaced hyphen (`-`). + +Tip: if you are drafting in Google Docs, disable the "Use smart quotes" setting in the Tools → Preferences menu to avoid inadvertently copying smart quotes into Markdown or other code. + +### Links + +Use descriptive link text. Besides providing clear context to the reader, this improves accessibility for screen readers. + +:white_check_mark: Yes: For more information, see [Tone and voice](#tone-and-voice).\ +:x: No: For more information, see [this section](#tone-and-voice). + +Note on capitalization: when referencing other docs/headings by title, use sentence case so the reference matches the corresponding title or heading. + +### Formatting + +**Bold**: use when referring to UI elements; prefer bold over quotes. For example: Click **Install server** to start the installation process. + +**Italics**: emphasize particular words or phrases, such as when introducing/defining a term. For example: Custom permissions are defined using _permission profiles_. + +**Underscore**: do not use; reserved for links. + +**Code**: use a `monospaced font` for inline code or commands, code blocks, user input, filenames, method/class names, and console output. + +## Screenshots and images + +Considerations for screenshots and other images: + +- Don't over-use screenshots: + - Screenshots are useful for complex UIs or to point out specific elements that are otherwise hard to describe with text. But for example, an input form doesn't need a screenshot when text can just as easily list the fields and their purpose. + - Screenshots age rapidly. + - Too many screenshots can become visually overwhelming and interrupt the flow of documentation. +- Don't use images of text, code samples, or terminal output. Use actual text so readers can copy/paste and find the contents via search engines. +- Use alt text to describe images for readers using screen readers and to assist search engines. +- Be consistent when taking screenshots - use the same OS if possible (macOS has been used in Stacklok docs to date) and zoom level (ex: zoom twice in VS Code, 125% in browsers). +- Crop screenshots to the relevant portion of the interface. +- Use the primary brand colors (`#2D684B` on light backgrounds, `#BDDFC2` on dark backgrounds) for annotations like callouts and highlight boxes. + +## Page structure + +Every how-to guide and tutorial page follows a consistent structure. This ensures readers always know what to expect and never hit dead ends. + +### Front matter + +On sites that use front matter (Docusaurus and most static site generators), every page must have front matter with at least a `title` and `description`. + +#### Descriptions + +The `description` field serves double duty: it appears in index/preview cards (truncated at roughly 70-75 characters) and as the page's `` description for search engines (ideally 80-150 characters total). + +Write descriptions to work at both lengths: + +- **Front-load the value.** The first 70 characters must stand alone as a useful summary, because that's all a preview card shows before cutting off. +- **Add SEO detail after the natural break.** Use the remaining characters for keywords and context that help search engines. +- **Lead with the action or topic, not filler.** Avoid openers like "Learn how to," "Understanding," "A guide to," or "This page describes." +- **Avoid special YAML characters in unquoted values.** Colons (`:`) inside a description can break YAML parsing. Either rephrase, use a comma, or wrap the value in quotes. + +Examples: + +:white_check_mark: `Install the ToolHive CLI and run your first MCP server in minutes.`\ +:x: `A step-by-step guide to installing the ToolHive CLI and running your first MCP server.` + +:white_check_mark: `Groups organize MCP servers into logical sets and control which clients can access them.`\ +:x: `Understanding when and why to use groups for organizing MCP servers and controlling client access.` + +### Closing sections + +Every how-to guide and tutorial page ends with closing sections in this order: + +1. `## Next steps` (required) - 1-3 links to the next logical pages, following the reader's journey (for example: install, use, secure, operate, optimize). +2. `## Related information` (optional) - links to background reading, reference docs, or external resources that don't represent a next action. +3. `## Troubleshooting` (optional) - common issues and solutions, typically using collapsible `
` blocks. + +Example: + +```mdx +## Next steps + +- [Run MCP servers](./run-mcp-servers.mdx) to deploy your first server. +- [Client configuration](./client-configuration.mdx) to connect your IDE. + +## Related information + +- [Understanding MCP](../concepts/mcp.mdx) for background on the protocol. + +## Troubleshooting + +
+Server fails to start + +Check that the container runtime is running... + +
+``` + +### Introduction pages + +Each major product or component section starts with an Introduction page that explains what the component is, who it's for, and where to start. Make it an explicit navigation entry, not a hidden category-link page. + +### Cross-references + +Link to related content in other sections where it adds value. When a page's topic connects to a concept page, a reference page, or a peer product's docs, link to it rather than restating the content. When products or components have overlapping names (for example, a built-in registry versus a registry server product), disambiguate at the point of reference. + +## Markdown style + +Just like a consistent writing style is critical to clarity and messaging, consistent formatting and syntax are needed to ensure the maintainability of Markdown-based documentation. + +We generally adopt the [Google Markdown style guide](https://google.github.io/styleguide/docguide/style.html), which is well-aligned with default settings in formatting tools like Prettier. + +Our preferred style elements include: + +- Headings: use "ATX-style" headings (hash marks - `#` for Heading 1, `##` for Heading 2, and so on); use unique headings within a document +- Unordered lists: use hyphens (`-`), not asterisks (`*`) +- Ordered lists: use lazy numbering (`1.` for every item and let Markdown render the final order. This is more maintainable when inserting new items) + - Note: this is a "soft" recommendation. It is also intended only for Markdown documents that are read through a rendering engine. If the Markdown will be consumed in raw form, use real numbering. +- Code blocks: use fenced code blocks (` ``` ` to begin/end) and explicitly declare the language, like ` ```python ` or ` ```plain ` +- Add blank lines around headings, lists, and code blocks +- No trailing whitespace on lines + - Use the `\` character at the end of a line for a single-line break, not the two-space syntax which is easy to miss +- Line limit: wrap lines at 80 characters; exceptions for links, tables, headings, and code blocks + +### Docusaurus + +Specific guidelines for sites built with Docusaurus: + +- Define the page title in front matter and repeat it as a matching Markdown H1. Sections within a page begin with Heading 2 (`##`). Follow [`docs/_docs-conventions.md`](../../../../docs/_docs-conventions.md). +- Use relative links that include the numbered filename prefix for cross-page links, such as `[CLI](./07-cli.md)`. +- Use the `.md` extension for pages under `docs/`. +- Use the front matter section on all pages. At a minimum, set the `title` (this is rendered into the page as an H1) and a short `description`. [[Reference](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-docs#markdown-front-matter)] +- Use titles and line highlights in code blocks to provide context and improve readability. [[Reference](https://docusaurus.io/docs/markdown-features/code-blocks)] + - Titles are added using the `title="..."` attribute in the opening code fence. + - Line highlights are added using comma-separated `{number}` or `{start-end}` ranges in the opening code fence, or `highlight-next-line`, `highlight-start`, and `highlight-end` comments within the code block. +- Use admonitions for notes, tips, warnings, and other annotations. This provides a consistent look and feel across the site. [[Reference](https://docusaurus.io/docs/markdown-features/admonitions)] + - Use square brackets to add a custom title, e.g. `:::info[My title]`. + - Add empty lines around the start and end directives to avoid formatting issues with Prettier. + - Don't overuse admonitions; they are best for callouts that add value beyond the main content. Too many admonitions can become visually overwhelming and interrupt the flow of documentation. + - An admonition must add information beyond the surrounding text. Don't use one to restate or negate what the adjacent prose already says. +- Place images in `static/img` using WebP, PNG, or SVG format. +- Use the [`ThemedImage` component](https://docusaurus.io/docs/markdown-features/assets#themed-images) to provide both light and dark mode screenshots for apps/UIs that support both. + +## Products and projects + +These are the products and projects Mecatl documentation may need to mention. + +**ToolHive**: An open source runtime for deploying and operating MCP servers. Write it as one bi-capitalized word (not "Toolhive" or "Tool Hive"). + +**Mecatl**: An open source, cloud-native agent harness for running AI agents as production workloads on infrastructure you operate. Style Mecatl as a proper noun. Write the component and binary names `mecatui`, `mecated`, `mecak8s`, and `mecatequi` in lowercase code font. + +## Word list & glossary + +Common terms used in Mecatl documentation: + +**open source**: We prefer using two words over the hyphenated form (not "open-source"). It's not a proper noun, so don't capitalize unless it starts a sentence. + +**cloud-native harness**: A generic architectural description, not a proper noun. Keep it lowercase and hyphenate "cloud-native" when it modifies "harness." + +**OSS**: Abbreviation for "open source software". + +**Stacklok**: The company behind Mecatl and ToolHive. It's written as one word with a single capital (not "StackLok" or "Stacklock"). + +### Products/brands + +**Copilot** - GitHub's AI coding assistant. It's written with only a leading capital (not "CoPilot"). + +**Git**: The most popular distributed version control system. It underpins most commercial VCS offerings like GitHub, Bitbucket, and GitLab. Unless specifically referring to the `git` command-line tool, it's a proper noun and should be capitalized. + +**GitHub**: The most popular source code hosting provider, especially for open source. It's written bi-capitalized as one word (not "Git Hub" or "Github"). + +**JetBrains**: A company that makes IDEs for many languages, including IntelliJ IDEA, PyCharm, GoLand, and more. It's written bi-capitalized as one word (not "Jet Brains" or "Jetbrains"). It's proper to reference a specific JetBrains IDE when needed, or simply refer to "all JetBrains IDEs". + +**LLM**: large language model, a type of machine learning model designed for natural language processing tasks. LLM is an abbreviation, so it's written in all caps. Written out, it is lower-cased. + +**MCP**: Model Context Protocol. MCP is an open protocol that standardizes how applications provide context to LLMs. MCP is an abbreviation, so it's written in all caps. Written out, it is proper-cased. + +**Microsoft Entra ID**: Microsoft's cloud identity and access management platform. Microsoft rebranded it from "Azure AD" (Azure Active Directory) in 2023, so don't use "Azure AD" except when quoting a literal API, CLI, or field value that still uses the old name. Use "Microsoft Entra ID" on first reference, "Entra ID" thereafter. + +**npm**: The registry for JavaScript packages (the "npm registry"), and the default package manager for JavaScript. Since it's both the registry _and_ the package manager, it may be useful to disambiguate "the npm registry". It's not an abbreviation, so it's not capitalized; it's written all lowercase (not "NPM"). + +**OpenAI**: The company behind the GPT models and ChatGPT. It's written bi-capitalized as one word (not "Open AI" or "Openai"). + +**Visual Studio Code**: A popular free integrated development environment (IDE) from Microsoft. Per Microsoft's [brand guidelines](https://code.visualstudio.com/brand#brand-name), use the full "Visual Studio Code" name the first time you reference it. "VS Code" is an acceptable short form after the first reference. It's written as two words and there are no other abbreviations/acronyms (not "VSCode", "VSC", or just "Code"). diff --git a/.claude/skills/tech-writer/references/tutorials.md b/.claude/skills/tech-writer/references/tutorials.md new file mode 100644 index 0000000..b97a3b0 --- /dev/null +++ b/.claude/skills/tech-writer/references/tutorials.md @@ -0,0 +1,45 @@ +# Writing tutorials + +[Technical writing skill](../SKILL.md) + +A tutorial is a lesson. The reader is a learner who wants to acquire skill by doing something under your guidance. They don't yet know what they need to know, so you, the teacher, carry all responsibility for their success. If a learner follows the steps exactly and something fails, the tutorial failed, not the learner. + +Tutorials are typically the quickstarts inside each product section and any end-to-end getting-started pages. A quickstart should deliver a working result in under 10 minutes. + +## Principles + +**Deliver visible results early and often.** Every step, or small group of steps, should produce something the learner can see: output, a running container, a response from a tool. Visible progress builds the confidence that keeps a learner going. Never string together long sequences of setup with nothing to show for it. + +**Get the learner started, not educated.** The goal is a completed experience and earned confidence, not comprehensive knowledge. Resist covering options, alternatives, and edge cases. There is one path through a tutorial, and you choose it. + +**Minimize explanation.** A learner mid-task cannot absorb theory; explanation interrupts the doing. Offer only the minimum context a step needs ("You need Docker because ToolHive runs MCP servers in containers"), and link to concept pages for anything deeper. If you find yourself writing paragraphs of background, that content belongs in an explanation page. + +**Give no choices.** "You can use X or Y" is poison in a tutorial. The learner has no basis for choosing and every fork doubles the ways the lesson can go wrong. Pick one client, one server, one installation method. Alternatives belong in how-to guides. + +**Be concrete and specific.** Real commands, real components, real output. Show the learner what they should see after each significant step ("You should see the server listed with status `running`") so they can confirm they're on track before continuing. + +**Guarantee repeatability.** The tutorial must work, exactly as written, for every learner in a reasonable environment, every time. This is the hardest and most important obligation. State prerequisites completely, pin anything that drifts, and test the steps end to end before publishing. + +**Signpost the journey.** Tell the learner what they'll accomplish at the start ("In this tutorial, you'll run your first MCP server and connect it to VS Code"), mark progress along the way, and close by naming what they achieved. + +## Keep out of tutorials + +- Explanations longer than a sentence or two (link to concept pages instead) +- Options, alternatives, and configuration surveys (how-to material) +- Complete listings of flags or fields (reference material) +- Troubleshooting content beyond the one or two failures learners actually hit, placed in a closing Troubleshooting section, never inline +- Abstractions and generalization; the lesson is this concrete task + +## Structure + +1. Front matter: `title` and `description` (see the style guide) +2. What you'll do and what you'll have at the end, in a sentence or two +3. Prerequisites, complete and verifiable +4. Numbered steps, each with a visible result +5. A closing recap of what the learner accomplished +6. Next steps (1-3 links: the natural follow-on guides) +7. Related information, then Troubleshooting, if applicable + +## Voice notes + +Diataxis suggests first person plural ("we") for tutorials; Stacklok docs use second person ("you") everywhere instead, per the style guide. Keep the teacher's encouraging, confident tone without the "we": "You'll notice the server appears in the list", "Now connect your client." diff --git a/CLAUDE.md b/CLAUDE.md index 306ee92..638e7da 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,6 +114,11 @@ publishes the GitHub Release, and pushes the Homebrew formula to source (see [`docs/08-github-action.md`](./docs/08-github-action.md)) — skip this step and the action keeps installing an old release indefinitely, with no error to flag it. +- **Update `docs/08-github-action.md` for the new tag.** Change its example + action reference and documented `version` default to the release tag, then + revise any feature-availability notes that depended on the former default. + The Action page is a copy-paste entry point; leaving it on an old tag makes + its instructions disagree with `action.yml`. - **Bump `plugin/.claude-plugin/plugin.json`'s `version` to match, if the plugin/skills changed.** The plugin ships next to the binary it drives so the two stay in lockstep (see "Repository layout" above) — this doesn't diff --git a/docs/02-getting-started.md b/docs/02-getting-started.md index e4e8bcb..618c638 100644 --- a/docs/02-getting-started.md +++ b/docs/02-getting-started.md @@ -1,7 +1,7 @@ --- sidebar_position: 2 title: Getting Started -description: Install the CLI and the Claude Code plugin, then build your first domain model by talking to an agent. +description: Install the CLI and authoring skills, then build your first domain model by talking to an agent. --- # Getting Started @@ -14,10 +14,8 @@ not the starting point. This page gets you set up and into that loop. ## Install You need two things, in this order: the **CLI** (the engine that lints and -renders the YAML) and the **Claude Code plugin** (the skills that drive it by -conversation). The skills shell out to the `modelith` binary, so install it -first — otherwise your first skill invocation fails on a missing binary -instead of doing anything useful. +renders the YAML) and the **authoring skills** that drive it by conversation. +The skills shell out to the `modelith` binary, so install it first. ### 1. Install the CLI @@ -29,68 +27,32 @@ Or download a prebuilt binary, or build from source with `go install` — see the full [CLI installation](./07-cli.md#installation) instructions for every option. -### 2. Install the plugin +### 2. Install the authoring skills -The skills ship as a **Claude Code plugin**. The plugin files live in -[`plugin/`](https://github.com/stacklok/modelith/tree/main/plugin), next to -the binary they drive, so the skills and CLI version stay in lockstep. - -:::caution[Marketplace listing pending review] - -The plugin has been submitted to `anthropics/claude-plugins-community` and is -awaiting Anthropic's approval. **The `claude plugin marketplace` commands below -will fail until it's listed.** - -In the meantime, use the skills CLI below, which doesn't depend on the -marketplace listing, or install from a local checkout: -[Developing the plugin locally](./09-local-development.md#developing-the-plugin-locally). - -::: - -### Skills CLI install (works today) - -Install with the [skills CLI](https://skills.sh) (also works with Cursor, -Windsurf, and other agents) — this doesn't depend on the marketplace listing: +The authoring skills ship in the repository's +[`plugin/`](https://github.com/stacklok/modelith/tree/main/plugin) directory. +Install them with the [skills CLI](https://skills.sh): ```sh npx skills add stacklok/modelith ``` -### Marketplace install (once approved) - -Add the community marketplace once, then install: - -```sh -claude plugin marketplace add anthropics/claude-plugins-community -claude plugin install modelith@claude-community -``` +The skills require the `modelith` binary on your `PATH`. -Restart Claude Code (plugin changes apply on the next session). The skills are -namespaced under the plugin name — invoke them as `/modelith:domain-model-author`, -`/modelith:domain-model-lint`, and `/modelith:domain-model-context`. You can also -browse and install interactively with the `/plugin` command. +### Claude Code plugin -
-If the install looks like it did nothing - -A plugin install can occasionally wedge into a half-state — the UI shows the -plugin but flags it "not cached (not recorded)", and you can't enable or -uninstall it. Recover by clearing it and reinstalling: +The plugin has not been approved for the public Claude Code catalog. Until it is, +clone this repository and launch Claude Code with the plugin directory: ```sh -claude plugin uninstall modelith@claude-community # may fail; continue anyway -claude plugin marketplace update claude-community -claude plugin install modelith@claude-community +git clone https://github.com/stacklok/modelith.git +claude --plugin-dir /path/to/modelith/plugin ``` -If `uninstall` errors with "not installed in user scope," remove -`"modelith@claude-community"` from `enabledPlugins` in `~/.claude/settings.json` -and delete `~/.claude/plugins/cache/claude-community/modelith`, then reinstall. - -
- -To develop or test the plugin from a local checkout instead of the marketplace, -see [Developing the plugin locally](./09-local-development.md#developing-the-plugin-locally). +Replace `/path/to/modelith` with the cloned checkout. The skills are namespaced +under the plugin name, for example `/modelith:domain-model-author`. See +[Developing the plugin locally](./09-local-development.md#developing-the-plugin-locally) +for using this workflow from a model repository. ## The skills @@ -151,32 +113,34 @@ the value is in the questions it asks, not the typing: > **You:** One project only, and deleting the project deletes its policies. A > policy is meaningless without its project. > -> **Agent:** That's an owned, one-to-many relationship — a `Project` has many -> `Policies`, each part of exactly one. I'll declare it from the **parent** (the -> owning `Project` side, by convention) and add the `Policy` entity with its -> invariant: -> -> ```yaml -> Project: -> relationships: -> - entity: Policy -> cardinality: "1:n" -> ownership: owned -> -> Policy: -> definition: > -> A rule set evaluated by the system, belonging to exactly one `Project`. A -> `Policy` has no meaning outside its owning `Project`. -> invariants: -> - id: belongs-to-one-project -> statement: "Belongs to exactly one `Project`" -> ``` +> **Agent:** I'll add this to the model and run `modelith lint`. This excerpt +> needs a scenario that exercises `Policy` before the model passes +> `--completeness error`. > -> Running `modelith lint`… clean. Regenerating `model.modelith.md`. One gap: no scenario -> exercises `Policy` yet — want to add one (e.g. "attach a policy to a project")? +The YAML below is an excerpt from the model's `entities` section: + +```yaml +Project: + relationships: + - entity: Policy + cardinality: "1:n" + ownership: owned + +Policy: + definition: > + A rule set evaluated by the system, belonging to exactly one `Project`. A + `Policy` has no meaning outside its owning `Project`. + invariants: + - id: belongs-to-one-project + statement: "Belongs to exactly one `Project`" +``` + +> **Agent:** `modelith lint` reports the expected gap: no scenario exercises +> `Policy` yet. Want to add one, such as "attach a policy to a project"? -The agent drafted YAML, validated it, regenerated the Markdown, and surfaced a -completeness gap as a question — the loop you'll repeat as the model grows. +The agent drafted the YAML, validated the model, regenerated the Markdown, and +surfaced the remaining completeness gap as a question. That is the loop you'll +repeat as the model grows. For a full session start to finish — building a parking-garage model from nothing across all three passes, including a moment where the agent catches a @@ -190,3 +154,10 @@ space: the agent knows what entities exist, what they're called, how they relate, and what invariants must hold. That produces more consistent code, better names, and fewer wrong guesses. The more complete and accurate the model, the less you have to correct. + +## Next steps + +- [Build the parking-garage model](./05-parking-garage/index.md) in a complete + authoring session. +- [Understand the model files](./03-understanding-your-model.md) that the agent + produces. diff --git a/docs/03-understanding-your-model.md b/docs/03-understanding-your-model.md index 05fc3d0..780ee9b 100644 --- a/docs/03-understanding-your-model.md +++ b/docs/03-understanding-your-model.md @@ -1,13 +1,14 @@ --- sidebar_position: 3 title: Understanding Your Model -description: What the agent produces — the YAML's four sections, the backtick convention, and the two files you commit. +description: "What the agent produces: the YAML's core concepts, backtick convention, and two files you commit." --- # Understanding Your Model You author by conversation, but you still own the result — and you should be -able to read it without the agent. Every model produces **two committed files**: +able to read it without the agent. For a model your repository owns, commit two +files: - **`model.modelith.yaml`** — the canonical source. Self-describing (`kind` + `version`), it's what the agent edits and what CI validates. @@ -16,11 +17,13 @@ able to read it without the agent. Every model produces **two committed files**: directly, and CI fails if it drifts from the YAML. Commit both. The Markdown is generated from the YAML — never hand-edit it; change -the YAML (via the agent) and re-render. +the YAML (via the agent) and re-render. A model [vendored from another +repository](./10-vendoring.md) is a copy with different rendering obligations. -## The four sections +## Model contents -A `*.modelith.yaml` file has four top-level sections: +A model can define `glossary`, `enums`, `entities`, `scenarios`, and model-level +`invariants`: - **`glossary`** — ubiquitous-language terms that aren't entities (roles like `Owner`, states, domain nouns), each with a definition. @@ -32,12 +35,17 @@ A `*.modelith.yaml` file has four top-level sections: whether the model hangs together. Scenarios render as formatted text steps today; `sequenceDiagram` rendering is a roadmap item. -A fifth, optional top-level section — **`invariants`** — holds rules that span -several entities and have no natural single owner (e.g. "when a `Project` is -archived, none of its `Policies` remain enabled"). It uses the same -`{id, statement}` shape as entity invariants and shares their id namespace; reach -for it only when a rule genuinely has no single home (see the -[Schema Reference](./06-schema-reference.md#invariant)). +Model-level `invariants` hold rules that span several entities and have no +natural single owner (for example, "when a `Project` is archived, none of its +`Policies` remain enabled"). They use the same `{id, statement}` shape as entity +invariants and share their ID namespace. + +A model can also declare `imports` to reference enums and entities defined by +another model. Imports use files already in your repository. If the other model +originates elsewhere, [vendoring](./10-vendoring.md) copies it into your +repository; it remains a vendored model with different rendering obligations. +See the [Schema Reference](./06-schema-reference.md) for these and all other +top-level fields. A minimal model looks like this: @@ -83,7 +91,8 @@ the Diagrams](./04-reading-the-diagrams.md) for how the rendered ER diagram work In freeform text (definitions, steps, invariants), entity names are wrapped in backticks — `` `Project` `` — so the renderer formats them as code and the -linter can check they reference real entities. In structured fields that already -imply an entity (`actors`, relationship `entity:`, entity keys), the backticks -are skipped. The agent follows this automatically; it's worth recognizing when -you read the YAML. +linter can check they reference real entities. Freeform text renders as +Markdown; raw HTML is rendered as literal text. In structured fields that +already imply an entity (`actors`, relationship `entity:`, entity keys), the +backticks are skipped. The agent follows this automatically; it's worth +recognizing when you read the YAML. diff --git a/docs/04-reading-the-diagrams.md b/docs/04-reading-the-diagrams.md index 4bd9aad..86f3927 100644 --- a/docs/04-reading-the-diagrams.md +++ b/docs/04-reading-the-diagrams.md @@ -11,9 +11,10 @@ diagram** in *crow's-foot* notation. It's a compact map of the nouns in the domain and how they connect. If you haven't read one before, this page is the key. -The diagrams render automatically on GitHub and in the docs site (every fenced -` ```mermaid ` block below is a live diagram). You never write this notation by -hand — `modelith render` generates it from the `*.modelith.yaml`. +GitHub renders the Mermaid blocks in committed Markdown, and this documentation +site renders the live examples below. Other Markdown viewers may show the source +instead. You never write this notation by hand: `modelith render` generates it +from `*.modelith.yaml`. ## What the diagram shows (and what it doesn't) @@ -241,5 +242,7 @@ Reading it: - **the `Project self` row** — a `Project` optionally points at the archived `Project` it replaced, its `Predecessor`. -To go deeper on the underlying fields, see the [Schema -Reference](./06-schema-reference.md). +## Related information + +- [Understand the model contents](./03-understanding-your-model.md). +- [Review the diagram fields in the schema](./06-schema-reference.md#relationship). diff --git a/docs/05-parking-garage/index.md b/docs/05-parking-garage/index.md index 9fd1122..9203166 100644 --- a/docs/05-parking-garage/index.md +++ b/docs/05-parking-garage/index.md @@ -64,11 +64,13 @@ important decision in the model. ## Pass 1 — the skeleton The agent names every entity, writes a crisp definition for each, and declares -the relationships and cardinality. **Nothing else** — no attributes, rules, or +the relationships and cardinality. **Nothing else** - no attributes, rules, or scenarios yet. This is the highest-leverage step, and it already renders a real -ER diagram. +ER diagram. It is structurally and semantically valid, but it still has the +expected completeness advisories until later passes add rules and scenarios. -> **Agent:** Skeleton's in — 8 entities, lints with 0 errors. Here's the diagram: +> **Agent:** Skeleton's in - 8 entities, 0 errors, and expected completeness +> advisories for the rules and scenarios that Pass 2 will add. Here's the diagram: ```mermaid erDiagram @@ -92,8 +94,9 @@ erDiagram Visit ||--|| Ticket : "" ``` -> **Agent:** This is the minimum useful model — honest to stop here and -> circulate. Three things to sanity-check before I add behavior: +> **Agent:** This is the minimum useful model. You can circulate it now, then +> address the completeness advisories as you add behavior. Three things to +> sanity-check before I add behavior: > > 1. **`Spot → Account` is 1:1** — one reserved spot per parker. If a parker can > hold several, that's `n:1`. @@ -232,12 +235,12 @@ the source. Want to try it yourself? See [Authoring with Agents](../02-getting-started.md). -## Advanced: borrowing a concept from another model +## Beyond the three core passes: import a shared concept :::note[Advanced] -Skip this until you have two models. One model is the normal case, and -everything above works without it. +This is an extension beyond the three core passes, not a fourth required pass. +Use it when a second model already owns a concept. ::: @@ -266,9 +269,9 @@ enums: - name: account ``` -The garage lists the file and writes `scope.Name` at the reference site. The -scope comes from the filename, so `./payments.modelith.yaml` is written -`payments.`: +The garage lists the file and uses `scope.Name` at the reference site. The +importing model derives the scope `payments` from +`./payments.modelith.yaml`; `payments.PaymentMethod` uses that scope: ```yaml # garage.modelith.yaml @@ -286,12 +289,17 @@ an error, not a shrug — and the [rendered model](./garage.modelith.md) links [payments model](./payments.modelith.md). One definition, in the context that owns it. -The one thing the payments model *may* want to say is that it is on the -receiving end of this. A model whose enums are used only by the models that -import it collects an "enum is defined but no attribute uses it" advisory for -each one, since the uses are in files it cannot see. `shared: true` at the top -level retires that class of advisory — and nothing else. This one declares it, -though its own `Payment` happens to use the enum too. +The fixture declares `shared: true` because the payments model is intended for +other models to import. Its own `Payment` also uses `PaymentMethod`, so this +fixture has no unused-enum advisory to suppress. In a vocabulary-only model, +`shared: true` suppresses the unused enum and glossary-term advisories caused by +uses that live in importing files; it changes nothing else. Full rules, including how to name a scope explicitly when the filename won't do: [Imports](../06-schema-reference.md#imports). + +## Next steps + +- [Create a model with the authoring workflow](../02-getting-started.md). +- [Use imports and `shared`](../06-schema-reference.md#imports) when models need + the same enum or entity. diff --git a/docs/06-schema-reference.md b/docs/06-schema-reference.md index 6e82d46..6fcf3a0 100644 --- a/docs/06-schema-reference.md +++ b/docs/06-schema-reference.md @@ -259,6 +259,7 @@ entity-level ones render with their entity. | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Short title. | +| `description` | string | no | Short prose summary of what the scenario tests or demonstrates. | | `actors` | list of string | no | Entity names or glossary roles involved. Ad-hoc participants (e.g. `TargetUser`) are allowed and not required to be glossary terms. | | `steps` | list of string | yes | Ordered steps. Backtick entity names. | | `invariants_touched` | list of string | no | **Ids** of invariants this scenario exercises. Each must reference a declared invariant. | @@ -336,11 +337,10 @@ Six rules are worth knowing before you use this: `shipping`, `shipping.Carrier` is not available in `garage` — import it there too. Mutual imports (`a` lists `b`, `b` lists `a`) are therefore legal and terminate. -- **Only an attribute `type` may be qualified.** Cross-model references in - `relationship.entity` and `subtypeOf` are not supported; the linter says so - plainly rather than letting the name-pattern rejection speak for it. Whether - the ER diagram should draw a foreign entity — and how reciprocity would work - across a boundary — has no answer yet, and no live model needs one. +- **Qualified references name imported items.** An attribute `type` can name an + imported enum as `scope.Enum`. `relationship.entity` and `subtypeOf` can name + an imported entity as `scope.Entity`. Each resolves only through a direct + import; an imported model's own imports are not in scope. - **Nothing is fetched.** `imports` names files that are already in your repository; `lint` and `render` never touch the network ([ADR-0011](https://github.com/stacklok/modelith/blob/main/project-docs/adr/0011-network-boundary.md)). @@ -356,19 +356,19 @@ Six rules are worth knowing before you use this: is out of reach. Rendered Markdown names each import, shows the path as written, and links -separately to that model's rendered `.md`; a qualified type links straight to -the item's heading there. The renderer never opens an imported file, so a link -points at where the Markdown *would* be: render the imported model too, or the -link dangles. That location is the imported model's **default** rendered path -— beside its own `.yaml`, per [`modelith render`](./07-cli.md) with no `-o` — -expressed relative to wherever this Markdown is written, so `-o` a different -directory than the source keeps the link resolving. `--stdout` has no output -location to relativize against, so its links stay relative to the source, as -they would from a default, beside-the-source render. - -The linter reports a qualified type that doesn't resolve as an **error**, while -an *unqualified* PascalCase type that names no enum is only a **warning**. The -asymmetry is deliberate: `PaymentMethod` might be a primitive the author +qualified references straight to the item's heading there. The renderer never +opens an imported file, so a link points at where the Markdown *would* be: +render the imported model too, or the link dangles. That location is the imported +model's **default** rendered path — beside its own `.yaml`, per [`modelith +render`](./07-cli.md) with no `-o` — expressed relative to wherever this +Markdown is written, so `-o` a different directory than the source keeps the +link resolving. `--stdout` has no output location to relativize against, so its +links stay relative to the source, as they would from a default, +beside-the-source render. + +The linter reports an unresolved qualified reference as an **error**, while an +*unqualified* PascalCase attribute type that names no enum is only a **warning**. +The asymmetry is deliberate: `PaymentMethod` might be a primitive the author invented, so the linter can only suggest; `payments.PaymentMethod` can be nothing but a cross-model reference, so failing to resolve it is a broken reference. @@ -466,8 +466,10 @@ The JSON Schema covers structure. [`modelith lint`](./07-cli.md) adds: filename yields no valid slug, resolves outside the repository holding this model, contains a control character, or declares a schema version this modelith doesn't support; - - a qualified attribute `type` whose scope isn't imported, or that names - no enum in the model it resolves to. + - a qualified attribute `type` whose scope is not imported or that names no + enum in the model it resolves to; or a qualified relationship target or + subtype parent whose scope is not imported or that names no entity in the + model it resolves to. - **Warnings** (likely-but-not-certainly wrong): - a backticked term in freeform text that resolves to no entity, glossary term, role, or actor; diff --git a/docs/07-cli.md b/docs/07-cli.md index 0c60b84..8f220f7 100644 --- a/docs/07-cli.md +++ b/docs/07-cli.md @@ -6,10 +6,10 @@ description: Lint and render domain models from the command line. # The `modelith` CLI -`modelith` lints domain-model YAML and renders it to Markdown. It's the **engine -the [authoring agent](./02-getting-started.md) and CI run for you** — you'll -rarely invoke it directly. This page is the reference for when you do: every -command, flag, and the one-time install. +`modelith` validates domain-model YAML and renders it to Markdown. Use it directly +when you want to lint a model, regenerate its committed Markdown, or inspect the +schema. The [authoring agent](./02-getting-started.md) and CI use the same +commands. ## Installation @@ -65,6 +65,8 @@ Renders the model to a single Markdown document with an embedded Mermaid | `--stdout` | `false` | Write to stdout instead of a file. | | `--check` | `false` | Verify the committed output is up to date; non-zero exit on drift. | +`--stdout` cannot be combined with `--out` or `--check`. + If the model has [`imports`](./06-schema-reference.md#imports), the rendered links to them are relative to wherever `-o` writes — `-o` a different directory than the source and they still resolve, as long as the imported @@ -90,84 +92,55 @@ modelith schema > modelith.schema.json ## `modelith deps` -Manages models **vendored** from other repositories — copies committed here and -marked with a provenance header. This is the only command group that uses the -network; `lint` and `render` never do. +Manages vendored copies from other repositories. It is the only command group +that uses the network; `lint` and `render` run offline. See [Vendoring a model +from another repository](./10-vendoring.md) for provenance headers, ownership +semantics, and refresh behavior. ### `modelith deps import [dir]` -Fetches a model and writes it into `dir` (the working directory by default) as -a vendored copy. - -```sh -modelith deps import https://github.com/acme/billing/blob/main/docs/payments.modelith.yaml docs/ -``` +Fetches a GitHub model and writes a vendored copy to `dir`, or the working +directory when omitted. The filename comes from the origin. It requires an +installed, authenticated [`gh`](https://cli.github.com) CLI and prints the +`imports:` entry to add; it does not edit your model. | Argument / flag | Meaning | |---|---| | `` | The address of the file as it appears in a browser on github.com. | -| `[dir]` | Destination **directory**, defaulting to `.`. The filename always comes from the origin. | -| `--ref` | Ref to fetch, overriding the one in the URL. A tag pins the copy; naming a branch whose name contains a slash is also how you tell modelith where the ref ends and the path begins. | +| `[dir]` | Destination directory. | +| `--ref` | Ref to fetch, overriding the ref in the URL. A tag pins the copy. | -A browse URL gives no way to tell a slashed ref from the path after it, so -modelith splits at the first segment. `--ref` fixes that split only when it -names the ref that is *in* the URL — it cannot both pin a different ref and -re-split the path. To pin, open the file on the ref you want and import that -URL. When a fetch fails, the error says how the URL was split. +When a branch or tag contains `/`, pass `--ref` only when it names that same ref in +the URL: it tells modelith where the ref ends and the file path begins. For +example, use `--ref release/v2` with a URL containing +`/blob/release/v2/docs/payments.modelith.yaml`. For an ordinary single-segment +ref in the URL, a different `--ref` works. But `--ref` cannot both select a +different ref and disambiguate a URL whose ref itself contains `/`; in that +ambiguous case, copy the browser URL for the file at the target ref. -Fetching is delegated to [`gh`](https://cli.github.com), which must be installed -and authenticated. The command writes the file and prints the `imports:` entry -to add — it does not edit your model. It refuses to overwrite a file at the -destination that is not an earlier copy of the same model. +```sh +modelith deps import https://github.com/acme/billing/blob/main/docs/payments.modelith.yaml docs/ +``` ### `modelith deps check ...` -Reports which vendored copies have fallen behind their origins. Writes nothing, -and exits non-zero when any copy is stale — or when one could not be reached, -since not being able to tell is not evidence that it is current. +Checks vendored copies against their origins and exits non-zero when a copy is +stale or cannot be reached. It writes nothing and skips files without provenance +headers. ```sh modelith deps check docs/*.modelith.yaml ``` -A copy is stale when its origin serves different content, compared against the -digest in the copy's own header. A commit that touched the path without -changing the file is not a change. Whether a copy *here* has been hand-edited -is a different question, and `lint` answers it offline. - -Every line names the ref it checked against, because a copy pinned to a tag is -up to date for as long as that tag points where it did — modelith does not look -for newer releases. - ### `modelith deps update [--ref ] ...` -Brings vendored copies forward to what their origins serve. +Updates vendored copies from their origins. `--ref` re-pins one copy to a tag or +branch; it accepts exactly one file. The command does not edit `imports:` or +lint the result. ```sh modelith deps update docs/*.modelith.yaml modelith deps update --ref v2.2.0 docs/payments.modelith.yaml ``` -| Argument / flag | Meaning | -|---|---| -| `...` | Vendored copies to update. Files with no provenance header are skipped. | -| `--ref` | Re-pin the copy to this ref. Applies to **one file**: a single ref names a different version in every other repository. | - -A copy whose origin has not moved is left byte for byte alone, so running this -over a glob produces a diff only where something changed. A copy that was -hand-edited is not holding what its origin serves, so it is rewritten and the -edits go. - -It writes the copies and nothing else — it does not edit any model's `imports:` -and it does not lint. Run `modelith lint` afterwards: an item a copy used to -define may have been renamed or removed upstream, which breaks references -`update` cannot see. - -Both commands take file arguments the way `lint` does and skip files with no -provenance header, so the glob you already lint works unchanged. Find your -copies with `git grep -l '# modelith-vendored'`. - -See [Vendoring a model from another -repository](./10-vendoring.md) for what the header records, how a vendored file -is linted differently, how the two tracking modes differ, and why vendoring -fetches one file rather than a tree. +Use `modelith lint` after an update to find references that changed upstream. diff --git a/docs/08-github-action.md b/docs/08-github-action.md index 38913dd..a732bb5 100644 --- a/docs/08-github-action.md +++ b/docs/08-github-action.md @@ -6,8 +6,8 @@ description: Lint and verify domain models in CI. # GitHub Action -Any repo can lint its domain model and verify the committed Markdown in CI by -referencing this repo as an action. +Any repository can use this action to lint its domain models and verify committed +Markdown in CI. It supports Linux and macOS runners. ```yaml # .github/workflows/domain-model.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: stacklok/modelith@v1 + - uses: stacklok/modelith@v0.4.0 with: files: "model.modelith.yaml" completeness: warn @@ -33,7 +33,7 @@ jobs: | `files` | — (required) | YAML files or globs, space-, comma-, or newline-separated. | | `completeness` | `warn` | Treat completeness gaps as `warn` or `error`. | | `check-rendered` | `true` | Verify the committed `*.md` matches the YAML. | -| `version` | (pinned to a specific release) | `modelith` release to install, e.g. `v0.4.0`. | +| `version` | `v0.4.0` | Published `modelith` release to install. Set another published release to select it. | Multiple files / globs: @@ -46,36 +46,25 @@ with: ## How it works -The action downloads the prebuilt `modelith` release binary for the runner's OS/arch -and verifies it against the release's published checksums before running it. The -`version` input defaults to a specific release pinned in `action.yml` — combined with -pinning your `uses:` reference to a commit SHA, this keeps CI runs reproducible: a -given action commit always installs the same `modelith` version. +The action downloads the prebuilt `modelith` release binary for the runner's OS +and architecture, verifies it against the release's published checksums, and runs +it. The `version` input defaults to `v0.4.0`; set it to another published release +when you need a different version. Pinning your `uses:` reference to a commit SHA +keeps CI runs reproducible: a given action commit installs the selected +`modelith` version. ## Vendored models in the glob -If your glob matches a model [vendored from another -repository](./10-vendoring.md), the action treats it as somebody else's -document: +When the selected release supports vendored models, a glob can include a +[vendored model](./10-vendoring.md). A clean provenance header suppresses +completeness findings for that copy and lets `check-rendered` skip a missing +`.md` or a model this version cannot render. Structural and semantic findings, +including a provenance-header defect or a changed copy, still fail the action. -- **Completeness gaps in it are not reported**, so `completeness: error` cannot - fail your build over a model you did not write. -- **`check-rendered` skips it when you have not committed a `.md` for it**, so - you are not asked to commit one whose home is another repository. If you *do* - commit one — the way a deep link into a vendored model's Markdown gets a - target — it is checked for staleness from then on. -- **`check-rendered` also skips a copy this modelith cannot render**, such as - one written against a newer schema version. `lint` reports that, and repeating - it here would fail your build twice over one problem you cannot fix in your - own repository. - -Both skips take a clean provenance header to claim. A file whose header has a -defect in it is checked like any model you wrote, so a stray `# modelith-` -comment cannot quietly switch the gate off. - -Everything else still applies. A vendored file that is not a valid domain -model, or that has been edited since it was imported, fails the build — both -are about *your* repository's copy, and both are yours to fix. +The default `v0.4.0` binary does not include vendored-model support. Select a +published release that does before relying on this behavior. See [Vendoring a +model from another repository](./10-vendoring.md) for provenance and render +semantics. ## Regenerating the Markdown diff --git a/docs/09-local-development.md b/docs/09-local-development.md index 7f8b926..2997aba 100644 --- a/docs/09-local-development.md +++ b/docs/09-local-development.md @@ -15,9 +15,8 @@ bottom of this page. ## Prerequisites -- **Go** (a recent stable release) — the binary and tooling are pure Go. +- **Go 1.26** — the binary and tooling are pure Go. - **[Task](https://taskfile.dev)** — the task runner (`brew install go-task`). -- **`jq`** — used by the CI plugin check (`brew install jq`). - **The `claude` CLI** — only needed to validate/develop the plugin locally. - **Node.js / `npx`** — only needed for `task mermaid-check` (runs `@mermaid-js/mermaid-cli` via `npx`); not required for `task check`. @@ -41,7 +40,7 @@ The repo uses [Task](https://taskfile.dev). The one that matters before pushing: task check ``` -It runs the CI checks plus a local-only plugin validation. Run `task` with no +It runs the CI checks plus stricter local plugin validation. Run `task` with no arguments to list every target. | Command | What it does | @@ -57,15 +56,15 @@ arguments to list every target. | `task render-check` | Verify the committed Markdown is up to date. | | `task validate-plugin` | Validate the plugin with `claude plugin validate --strict` (needs the `claude` CLI). | | `task mermaid-check` | Parse every emitted/committed Mermaid diagram with the real `mermaid-cli` (needs `npx`/node). Not part of `task check` — see below. | -| `task check` | CI parity (vet, staticcheck, test, lint-models, render-check) plus `validate-plugin`. Does **not** run `mermaid-check`, so contributors without node/npm still get a green `task check`; CI runs the real Mermaid parse check as its own step instead. | +| `task check` | CI parity (vet, staticcheck, golangci-lint, test, lint-models, render-check) plus `validate-plugin`. It does **not** run `mermaid-check`, so contributors without node/npm still get a green `task check`; CI runs the Mermaid parse check separately. |
CI runs a lighter plugin check -`task check` runs the full `claude plugin validate ./plugin --strict` locally, -where you already have the `claude` CLI. CI instead does an equivalent `jq`-based -structural check (valid JSON, required fields, skill frontmatter present) so the -Go pipeline doesn't depend on the Claude Code CLI. Both gate the same thing. +`task check` runs `claude plugin validate ./plugin --strict` locally, which +performs the full plugin validation. CI uses a lighter `jq`-based structural +check for valid JSON, required manifest fields, and skill front matter, so the +Go pipeline does not depend on the Claude Code CLI.
diff --git a/docs/10-vendoring.md b/docs/10-vendoring.md index e61755c..531fa74 100644 --- a/docs/10-vendoring.md +++ b/docs/10-vendoring.md @@ -77,40 +77,28 @@ docs. Vendoring is designed for projects that already trust each other. ## How a vendored file is treated differently -Once a file carries a provenance header, modelith knows it is not your work. -Two things change, and nothing else: - -- **Completeness findings are suppressed.** Missing invariants, entities no - scenario exercises, unused enums and glossary terms are gaps in a document - its own authors control. Without this, the [GitHub - Action](./08-github-action.md) — which lints every matched file — would fail - your build over someone else's model. -- **Its own `imports:` do not receive semantic diagnostics.** A vendored - model's imports commonly name paths in *its* repository, which do not exist in - yours. Missing or broken nested edges stay silent, along with references that - resolve through them; readable local edges still participate in provenance - verification. - -**Structural and semantic checks still run.** A vendored file that is not a -valid domain model breaks your build, and that is your problem to solve — by -fetching a different ref, or by talking to whoever owns it. - -`modelith render --check` skips a vendored file that has no committed `.md`: -its rendered Markdown belongs to its home repository, so you are not asked to -commit one. Rendering a vendored model by naming it still works, which is how a -deep link into it gets something to point at — and once you commit that `.md`, -`--check` treats it like any other and tells you when refreshing the copy has -left it stale. - -The one thing `--check` will not do is fail over a vendored model this modelith -cannot render at all — one written against a newer schema version, say. It says -it skipped it and moves on; `modelith lint` is where that is reported, once. - -Being skipped is an exemption, and it takes a **clean** provenance header to -claim one. A file whose header has a defect in it — a misplaced line, a missing -key — is checked like any other model. That way a mistyped comment can never -quietly switch a gate off: the header defect fails `lint`, and the rendered -output is still checked. +A provenance line marks the file as a copy whose home is elsewhere. `lint` +reports every provenance-header defect as a semantic error. It still suppresses +completeness findings for that copy, because those findings are about content +owned by its origin. + +Its own `imports:` do not receive semantic diagnostics. A vendored model's +imports commonly name paths in its home repository that do not exist in yours. +Missing or broken nested edges stay silent, along with references that resolve +through them; readable local edges still participate in provenance verification. + +Structural and other semantic checks still run. A vendored file that is not a +valid domain model, or whose digest no longer matches its header, fails `lint`. + +`modelith render --check` applies its exemptions only to a vendored copy with a +clean provenance header. It skips a clean copy with no committed `.md`, and it +also skips a clean copy this version cannot render, such as one using a newer +schema version. If you commit the copy's `.md` for a deep link, `--check` +verifies it for staleness. A malformed header does not qualify for those +render-check exemptions, and `lint` reports the header error. + +The [GitHub Action](./08-github-action.md) applies the same `lint` and +`render --check` behavior to every matching file. ## What it will not overwrite diff --git a/docs/index.md b/docs/index.md index 911b13d..3c97d45 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,17 +6,17 @@ description: Author, validate, and render domain models by talking to an AI agen # Modelith - Domain Model Tooling -Tooling for authoring, validating, and rendering **domain models** — the -canonical, plain-language expression of what a system *is*: its concepts, how -they relate, and the rules that govern them. One model, one source of truth: -everyone reads from the same picture. +Modelith helps you author, validate, and render **domain models**: a +plain-language expression of a system's concepts, relationships, and governing +rules. Keep one model as the source of truth so everyone works from the same +picture. The model lives as a YAML file, but **you rarely write that YAML by hand**. You [author it by talking to an AI agent](./02-getting-started.md): you describe concepts in plain language, the agent drafts and validates the YAML, and it renders a Markdown version (with diagrams) that you commit alongside your code. -The `modelith` CLI is the engine the agent and CI run for you — it's there to -validate and render, not to be your starting point. +The `modelith` CLI is the engine the agent and CI run for you. It validates, +renders, and manages vendored models; it is not your starting point. ## The workflow @@ -33,14 +33,15 @@ model.modelith.yaml ─▶ canonical source (you edit this, via the agent) └─▶ modelith render : Markdown + Mermaid ─▶ model.modelith.md committed to the repo ``` -The rendered Markdown is **committed back next to the YAML** so people and -agents read the model directly, without running anything. CI regenerates it and -fails on drift (`modelith render --check`) — like a generated-code check. +The rendered Markdown is **committed next to the YAML** so people and agents can +read the model without running anything. Configure `modelith render --check` in +CI to fail on drift, like a generated-code check. The GitHub Action enables that +check by default. ## Where to start - **Authoring a model for the first time?** → [Getting Started](./02-getting-started.md) - — install the plugin, then build a model by conversation. + — install the CLI and authoring skills, then build a model by conversation. - **Want to understand a model someone produced?** → [Understanding Your Model](./03-understanding-your-model.md) and [Reading the Diagrams](./04-reading-the-diagrams.md). @@ -53,6 +54,6 @@ fails on drift (`modelith render --check`) — like a generated-code check. |---|---| | [Agent authoring](./02-getting-started.md) | The Claude Code plugin and skills — how you actually build a model | | [Schema](./06-schema-reference.md) | The JSON Schema that defines a valid model | -| [`modelith` CLI](./07-cli.md) | The `lint` / `render` engine the agent and CI run | +| [`modelith` CLI](./07-cli.md) | The `lint`, `render`, and `deps` commands the agent and CI run | | [GitHub Action](./08-github-action.md) | The same checks in CI | | [Vendoring](./10-vendoring.md) | Referencing a model that lives in another repository | diff --git a/internal/schema/schema.go b/internal/schema/schema.go index b146617..0e0bea4 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -1,13 +1,13 @@ // Package schema holds the canonical JSON Schemas for a Stacklok domain model, // one per format version, and helpers to compile them. The schema files -// themselves (vN/modelith.schema.json) are the source of truth; each is also -// published by URL (see URLFor) so editors can use it via a +// themselves (vN/modelith.schema.json) are the source of truth. Each has a +// canonical URL (see URLFor) for use in a // "# yaml-language-server: $schema=" header. // // This package is internal on purpose: modelith's contract is the CLI and the -// published JSON Schema, not a Go API. The schema living under internal/ has no -// effect on the file being reachable by URL — internal/ is a Go-compiler -// visibility rule only — it is published to modelith.sh by the release pipeline. +// JSON Schema, not a Go API. The schema living under internal/ is only a +// Go-compiler visibility rule. The CLI embeds the schema bytes, so it validates +// models without fetching their canonical URLs. package schema import (