Thank you for your interest in contributing to OpenSysML!
- Go 1.25 or later
- Git
- Make (recommended for build automation)
git clone https://github.com/Open-MBEE/OpenSysML.git
cd OpenSysML
make build # builds bin/sysml, bin/sysml-lsp, and bin/sysml-grpc with version info
make test # runs all tests
make lint # runs staticcheck and gosec, as CI does
./scripts/download-training-examples.sh # fetch the OMG corpus the gate needsThe OMG training-corpus gate (internal/core/model/training_examples_test.go) skips while
examples/sysml-v2-training/ is absent, so run the download script once before trusting a
local make test. CI runs the script itself and sets OPENSYSML_REQUIRE_TRAINING_CORPUS=1,
which makes a missing corpus a failure there instead of a skip.
# Build all binaries with version info
make build
# Build specific binary
make build-sysml
make build-lsp
make build-grpc
# Install to $GOPATH/bin
make install
# Python gRPC bindings
make python-proto # regenerate protobuf stubs
make python-install # install opensysml package
make python-test # run Python binding tests
# Clean build artifacts
make clean# All tests (using Makefile)
make test
# All tests (direct)
go test ./...
# With coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# With race detector
go test -race ./...
# Short tests (faster, no race detector)
make test-short
# Specific package
go test ./internal/core/parserParser-specific tests: When modifying the parser, ensure the four-layer test contract passes:
- Conformance gate:
go test -run TestStdlibConformance ./internal/core/libs - Golden ASTs:
go test -run TestGolden ./internal/core/parser - Negative tests:
go test -run TestNegative ./internal/core/parser - Update goldens (after intentional changes):
go test -run TestGolden -update ./internal/core/parser
See docs/internals/architecture.md for full details on the parser testing contract.
make lint runs the same two checks CI gates on, at the versions pinned in the
Makefile:
- staticcheck — must report nothing. Unused code is deleted rather than left behind; if a helper is genuinely needed by work in flight, land it with its caller.
- gosec — must report nothing. Generated protobuf code is excluded
(
-exclude-generated). Suppress a finding with#nosec <rule>only with a comment saying why it is safe; do not widen the exclusion list.
- Use
gofmt(enforced by CI) - Follow Effective Go
- Write tests for new functionality
- Document exported types and functions
Follow conventional commits format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation changestest: Test additions/changesrefactor: Code refactoringchore: Build/tooling changes
Examples:
feat(parser): add support for state machine transitions
fix(runtime): handle null values in expression evaluation
docs: update Quick Start guide with new commands
test(semantics): add conformance checking test cases
- Fork the repository
- Create a branch from
main:git checkout -b feat/my-feature - Make changes with clear commit messages
- Run tests:
go test ./... - Push to your fork
- Open a Pull Request targeting
main
- Include tests for new features
- Update documentation as needed
- Ensure CI passes (build + tests)
- Keep PRs focused (one feature/fix per PR)
- Respond to review feedback
For maintainers:
- Update version (if needed in code)
- Tag the release:
git tag -a v0.1.0 -m "Release v0.1.0: Initial public release" git push origin v0.1.0 - CI automatically:
- Builds binaries for all platforms
- Publishes to GitHub Releases
- All tests pass
- Documentation updated
- CHANGELOG updated (if maintained)
- Version tag follows semver (
vX.Y.Z) - Release notes prepared
We use Semantic Versioning:
- MAJOR (v1.0.0 → v2.0.0): Breaking changes
- MINOR (v0.1.0 → v0.2.0): New features (backward compatible)
- PATCH (v0.1.0 → v0.1.1): Bug fixes (backward compatible)
Current status: Pre-1.0, APIs subject to change
.github/workflows/pr.yml is the check that gates pull requests: gofmt, go vet,
make lint, the race-enabled test suite, and the binaries. It downloads the OMG training
corpus before the suite and runs the corpus gate as its own step, so that gate is required
rather than skipped.
All commits and tags trigger CI:
On every commit:
- Build for Linux/macOS/Windows
- Run full test suite
- Run race detector
On tags (v*):
- Build release binaries (all platforms)
- Create GitHub Release
- Upload binaries
PRs must pass:
- Build succeeds
- All tests pass
- No race conditions
- Code formatted (
gofmt) - Static analysis clean (
make lint: staticcheck and gosec) - OMG training-corpus gate runs (not skipped) and matches its expectations
github.com/Open-MBEE/OpenSysML
├── cmd/ # Binaries (sysml, sysml-lsp, sysml-grpc)
├── internal/core/ # Core implementation
│ ├── source/ # Source file handling
│ ├── lexer/ # Tokenization
│ ├── parser/ # Parsing
│ ├── ast/ # AST nodes
│ ├── symbols/ # Symbol tables
│ ├── resolve/ # Name resolution
│ ├── semantics/ # Type system
│ ├── passes/ # Validation
│ ├── lower/ # AST → execution IR (ActionGraph/StateGraph)
│ ├── runtime/ # Execution
│ ├── model/ # Workspace
│ └── libs/ # Standard library bundling
├── internal/lsp/ # LSP implementation
├── internal/grpc/ # gRPC service implementation
├── internal/repl/ # REPL implementation
├── python/ # Python client bindings (opensysml)
├── docs/ # Documentation
│ ├── guide/ # The handbook, in reading order
│ ├── reference/ # CLI, REPL, environment, APIs, RDF mapping
│ ├── internals/ # Architecture, testing, performance, design notes
│ └── project/ # Compliance, roadmap, releasing, measurements
├── testdata/ # Test fixtures
└── .circleci/ # CI configuration
Documentation is organized by what a reader wants, not by the feature that landed. Four areas, mapped in docs/README.md:
- docs/guide/ — how do I use it? A numbered handbook read in order.
- docs/reference/ — what does this flag, command, API or triple mean? Look-up material, exhaustive rather than narrative.
- docs/internals/ — how is it built? For someone changing the code.
- docs/project/ — where does the project stand? Compliance, roadmap, release and testing process, and measured counts.
When a change needs documenting:
- Extend the chapter that already covers the surface rather than adding a page per feature —
a new REPL command belongs in reference/repl-commands.md and
the chapter that teaches the workflow, not in a new
MY_FEATURE.md. - Teach in the guide, enumerate in the reference. Don't repeat a flag table in both; link.
- Measured numbers have one home (fixture, corpus and conversion counts live in
docs/project/); elsewhere, link to it instead of restating a number that
will drift. Three release-gate surfaces are the deliberate exception, because
docs/project/releasing.md checks the numbers they print:
README.md's coverage line and status table, and the gate tables in docs/project/roadmap.md and docs/project/training-examples.md. Recount all four together, in one commit. - Moving or renaming a page means updating its inbound links. Run
python3 scripts/check-doc-links.py— it fails on a link to a missing file or heading and runs in CI. Where a releasedREADME.mdlinked the old path, leave a one-paragraph pointer behind there (docs/ARCHITECTURE.mdand friends are such pointers); a page only ever linked from insidedocs/is moved outright.
See ARCHITECTURE.md for detailed design.
Key principles:
- Immutable AST: Syntax-only, never mutated
- Side tables: Semantic info keyed by node/symbol
- Lazy evaluation: Compute on-demand, memoize
- Incremental: Invalidate only affected parts
- Unit tests: Per-package (
*_test.go) - Integration tests: Cross-package scenarios
- Fixtures: Real SysML v2 models in
testdata/ - Golden files: Expected outputs (where applicable)
- GitHub Issues: Bug reports, feature requests
- Discussions: Questions, ideas, help
- Pull Requests: Code contributions
- Be respectful and inclusive
- Focus on constructive feedback
- Help others learn and grow
- Follow project conventions
By contributing, you agree that your contributions will be licensed under the same license as the project (see LICENSE).
Thank you for contributing to OpenSysML! 🚀