This page is for maintainers working on the tutorial itself, not learners working through it. It assumes you can read Python and SysML and that you have the environment from Getting Started already set up.
What we accept¶
The contributions we want keep this tutorial current to its toolchain (the OpenSysML runtime, sysml-toolkit and the other pinned tools) and to the OMG SysML v2 specifications; we are not adding new content. Existing content may be refined, clarified or otherwise improved against three priorities: (1) conformance with the SysML v2 specifications (the OMG SysML v2 language, API and Services, and KerML specifications); (2) didactic clarity; (3) effective, demonstrative use of tools from the OpenSysML stack (the OpenSysML runtime and sysml-toolkit). An improvement is accepted only if it is strictly dominant: better on at least one of these and worse on none. The binding statement is AGENTS.md §1.12.
Keep current. Bump a pin in
pyproject.toml/uv.lock,package.json/package-lock.jsonorscripts/tool-pins.jsonand regenerate the outputs (below); retire a workaround whoseDEFERRED.mdresolution condition a new release meets, adding a dated status line to that entry (or updating the one it has), updating the comment cell at the workaround, and leaving the heading as it is (headings are linked anchors); re-check a clause that a new edition of one of the three OMG specifications changed. Reporting drift is a contribution too: open an issue naming the tool and version (or the specification edition), the chapter and cell, and the spec clause. Upstream issues are filed by the project itself, after mzargham (Z) has reviewed the text (AGENTS.md§1.9).Improve what is here. State in the pull request which priority improves and the evidence, and why the change is worse on none of the three; the pull-request template asks for exactly this. Adding text or a cell counts as improving only if the learner’s task gets harder without it, and the pacing rule and the one-construct-per-notebook rule still apply. An independent reviewer on a different AI model checks the statement; what the reviewer cannot tell goes to the ACE (the project’s triage role, described below). A trade-off (better on one priority, worse on another) is not an improvement under this policy: open an issue and Z decides.
Not accepted by pull request. A new chapter, notebook, exercise, construct or analysis operation, model element, judgment record or glossary term, or a new learning outcome. Replacing a recorded workaround with the spec-anchored construct a newer tool release accepts is keeping current, not new content. If you think the tutorial needs something new, open an issue; only Z decides that, and a glossary term is confirmed only by Z.
The reviewer applies this test. A pull request passes when one “better” line (or, for keeping current, the currency event) and all three “not worse” lines are evidenced; any “worse” means the change is not accepted; what the reviewer cannot tell goes to the ACE.
| Priority | “Worse” means | Verified by, with what evidence |
|---|---|---|
| (1) Spec conformance | Text or model violating a normative clause of one of the three specifications; a spec-anchored construct replaced by a tool idiom; a conformance check or its negative control dropped or weakened | Reviewer: the clause citation, uv run python scripts/check_conformance.py, a strict load under the pinned runtime; the OMG SysML v2 Pilot Implementation is the baseline where a clause is ambiguous |
| (2) Didactic clarity | Longer or denser without the learner’s task getting harder without it; a second construct or operation in one sub-notebook; a check presented as proof; the pacing rule broken; a figure whose omissions are no longer stated | Reviewer: the pull request’s statement, the pacing check, and a simulated-learner checkpoint when the change alters what a learner does or sees beyond wording |
| (3) Tool use | A construct described as working but not run under the pinned version; a new workaround without a DEFERRED.md entry and comment cell; a tool demonstration removed; meaning moved from the model into Python | CI (TOASTER_REQUIRE_TOOLS=1, myst build --strict, scripts/check-site.py) and the reviewer re-running the touched notebook; the executed outputs and the DEFERRED.md entries touched |
Who is Z¶
“Z” is the contributor identity mzargham (Michael Zargham, GitHub user
mzargham), the project’s author and chief engineer. Project files
such as AGENTS.md and the decision log in
decisions/ say “Z” for that person. Published pages that mention
“Z” name the handle at first mention, as “mzargham (Z)”. Other contributors are named by their
own handles, and “Z” is never reused for anyone else.
How this repo is built, tested, and reviewed¶
The tutorial’s content — every chapter notebook, exercise, and model file — is built and reviewed through a small multi-agent harness that lives alongside the content itself. Three root files and two directories carry that harness, and they’re worth knowing about before you touch anything, even if you never run an agent yourself:
AGENTS.mdstates what the tutorial teaches (the functional/logical/physical layering, where its terms come from, how models are built and queried) and the legacy role roster that used to own each file. It’s the harness’s own foundational reference, read first by every agent role before it does anything else.CLAUDE.mdis the entry point: read order, the glossary CLI, and the skill index below.DEFERRED.mdtracks known gaps in the toolchain (the OpenSysML runtime, sysml-toolkit) that the tutorial works around — what the workaround is, why it’s needed, and the condition under which it comes out once the upstream gap closes..claude/agents/defines the roles that do the work: anorchestratorthat turns a request into scoped contracts and integrates results;builder/reviewerpairs that implement and independently check each change (always on different AI models, never the same one reviewing its own work); alayer-auditorthat classifies model elements against the functional/logical/physical boundaries; asimulated-learnerthat executes a chapter as a persona-assigned reader and reports what it found; and theace, which triages questions between the team and the tutorial’s author, ruling where it can and escalating what it can’t..claude/skills/holds the how-to for each kind of work — the sub-notebook template and pacing rules (toaster-recipe), the boundary tests for classifying a model element (architecture-layers), the glossary’s own usage rules (tutorial-glossary), the simulated learner protocol (user-testing), and more — each one a reference an agent (or a human contributor) reads before doing that kind of work, not after.decisions/is the record of what was decided and why:log.md(the running decision log, one entry per substantive ruling or escalation),work-contract-template.md(the shape of a task handed to a builder or reviewer), andtask-states.md(what state a task is in and what moves it to the next one).
If you want to keep a chapter current, clarify a definition, or review didactic content, the harness
tools above are built for exactly that — start at CLAUDE.md’s own read order rather than
improvising a workflow from scratch. The sections below cover the maintenance tasks directly; none
of them require running an agent, but all of them follow conventions the harness itself enforces
(the recipe’s pacing rule, the layer boundary tests, the review gate), and every change passes the
test in What we accept.
Deployment status¶
CI builds the book on every pull request and deploys it from main to
https://deploy job runs
only for a push to main, and only after the build job passes. The workflow is
.github/workflows/ci.yml.
The build job provisions the tools with
scripts/provision-tools.py
(see Getting Started), runs the test suite, builds the
book with every notebook executed, and then runs the release gate,
scripts/check-site.py.
The gate fails the build unless all five checks pass:
the build log has no code-execution or Jupyter-session failure (MyST exits 0 even when a cell raised unless it is run with
--strict, which CI does; the log check backs that up, so a failed cell is caught even if the flag is ever dropped);the built content has the expected number of figures, recorded in
scripts/site-baseline.json;no built text file contains a host path (a home directory, a Homebrew prefix or a CI runner path);
MyST has not published
exercises/files orDEFERRED.mdas raw downloads underbuild/;every root-relative link and asset in the HTML carries the
/toasterbase path and points at a file that exists in the built site.
To reproduce the build and the gate locally, from a clone with the tools provisioned:
set -o pipefail
BASE_URL=/toaster uv run --frozen npx myst build --html --execute --strict 2>&1 | tee build.log
uv run python scripts/check-site.py --site _build/html --content _build/site/content \
--log build.log --base-url /toasterKeep current: update a dependency or tool pin and regenerate outputs¶
Change the version in
pyproject.toml(Python),package.json(Node) orscripts/tool-pins.json(thesysmlv2binary, Z3 and the PlantUML jar with their sha256 hashes, and the standard-library commit), thenuv lock/npm installto update the lockfile, oruv run python scripts/provision-tools.pyto re-provision.tools/. The OpenSysML runtime binary is pinned separately from theopensysmlpackage: change theversion="v0.9.0"defaults insrc/toaster/bootstrap.py(and its_CLI_SUMShashes) andsrc/toaster/connect.py, and everyopensysml.connect(version=...)call inscripts/check_conformance.py,scripts/check_construction.py,chapters/,exercises/andtests/(git grep -n 'connect(version=' -- src scripts chapters exercises tests), together. Code cells, stored outputs andmodels/are protected during editorial passes, not during a bump: the notebooks are re-executed and their outputs regenerate (step 3).scripts/probes/andscripts/diagram_study/are dated probes and keep the version they probed.Run
TOASTER_REQUIRE_TOOLS=1 uv run pytest tests/ glossary/tests/anduv run python scripts/check-tools.py(check-tools.pyon GitHub).Rebuild the local preview (
uv run npx mystmd start --execute) and spot-check a chapter that exercises the changed dependency; a version bump inopensysmlorsympycan change printed output even when no test fails.Re-read the
DEFERRED.mdentries that name the bumped tool. If the new release meets an entry’s resolution condition, retire the workaround, add a dated status line to the entry (or update the one it has), update the comment cell at the workaround (do not rename the heading), and recompute thecontent_hashof any judgment record the model change touches (below). A release that breaks something gets a new entry, not a silently dropped demonstration.Commit the lockfile or the pins alongside the version change; never bump a version without regenerating and committing what matches it. Update the version strings in
docs/setup.md,docs/reproducibility.mdandAGENTS.md§1.2 in the same change, and re-check the prose, tests andDEFERRED.mdentries that state a version (git grep -n 'v0\.9\.[01]' -- chapters exercises tests DEFERRED.md): a test or fixture that pins the version moves with it; a statement re-probed under the new release takes the new version; one not re-probed keeps the version it was probed against.
How a change is built and reviewed¶
New chapters are not accepted (What we accept); the rules that governed building the existing ones govern every change to them:
toaster-recipe’s sub-notebook skeleton andarchitecture-layers’ boundary tests bind any model element a change touches; both are binding, not stylistic suggestions.Each cumulative fixture (
models/chNN-cumulative.sysml) must keep containing everything the previous chapter’s fixture has; seetests/test_predecessor_containment.pyfor how that invariant is checked. After changing a notebook’s construction cells or a fixture, runuv run python scripts/check_construction.py --check --chapter=N(scripts/check_construction.py): it executes the registered construction zones and loads eachTOASTER_INCREMENTand the cumulative fixture. Update a notebook’s existingCONSTRUCTION_NOTEBOOKSentry only if its stubs change; new entries are for new notebooks, which are not accepted.Before committing prose, check that
uv run python -m glossary lintreports no hit the base branch does not (it is not a CI gate and exits 1 on pre-existing hits: run--write-baseline base.jsonon the base, then--baseline base.jsonon your branch, which exits 1 only on a new error); run the pacing check intutorial-style-guide(consecutive code cells with no markdown between them) on every notebook you touched.Get an independent review on a different AI model than whoever authored the change, per
decisions/task-states.md’s merge gate.
Change a model element and review stale judgment records¶
A ReviewRecord’s content_hash is computed from the model source it was written against.
Changing that source without updating the record leaves it silently stale.
A model change is accepted only as keeping current (a workaround retired) or as a strictly dominant improvement (What we accept); either way:
Find every
ReviewRecordwhosemodel_reftouches the element you are changing (grep -rl model_ref= chapters/).After changing the model, recompute each record’s
content_hashand re-runvalidate_recordon it.Re-read the record’s
claim,rationale, andcounterevidencefields against the new model state. A hash mismatch tells you the record is stale; it does not tell you whether the claim is still true. Rewrite what no longer holds; do not just refresh the hash.
Run the full CI pipeline locally¶
uv sync --locked
npm ci
uv run python scripts/provision-tools.py
uv run python scripts/check-tools.py
uv run pytest tests/ glossary/tests/ -v
uv run npx mystmd start --executeThe first five commands mirror the build job’s provisioning and tests (the job also installs
Graphviz and a Java runtime from apt); the last serves the book for preview rather than building
it. CI runs pytest with TOASTER_REQUIRE_TOOLS=1, so a missing external tool fails the tests
instead of skipping them; prefix the pytest line with it to get the same behavior locally. To run the build itself and
the release gate, use the two commands under Deployment status. There is no
local equivalent of the deploy job, and there should not be, since publishing is a decision, not
a build artifact.