Workflow YAML under .github/workflows/ is the source of truth. This page explains the boundaries; it does not duplicate every job or pinned version.
ci.yml always classifies the diff. Only recognized documentation files may take the documentation-only path; a text fixture under a crate is source code.
Branch protection currently requires these reporting contexts:
Classify ChangesCheck AGENTS.md LinksCheck Workflow Action PinsGraph Vocabulary GuardTest omnigraph-server --features awsFormat (rustfmt)Lint (clippy)GQ Logic TestsFix Regression Gate
GQ Logic Tests (gq-logic-tests.yml) owns the complete .gqt corpus as a
required context aggregating three qualification jobs. GQT (ordinary) checks
unit tests and unavailable-DST refusal from the workspace root. GQT (dst)
runs the whole package, while GQT (dst-clippy) checks all package targets
with Clippy. Both run from crates/omnigraph-gqt, whose Cargo configuration
enables the seeded Tokio runtime. Each job has its own
45-minute budget and cache key. Matrix fail-fast cancels the remaining jobs
when one fails; Cargo retains its default fail-fast between test targets.
The required context fails if classification or any qualification fails,
is cancelled, or is skipped. A successful run still requires all three jobs
to pass; fail-fast never turns incomplete qualification into success.
Test jobs upload invocation reports, and all three jobs upload available
Cargo build timings separately, including on failure.
Every corpus case is enrolled, including cases whose required graph
behavior currently fails. Test Workspace excludes this separately tested
package; it does not silently skip DST cases. GQ Logic Tests takes the documentation-only skip the way
the AWS job does and reports success without building; its workflow carries a
verbatim copy of the Classify Changes job under the name
Classify Changes (GQ Logic Tests), and scripts/check-classify-copy.py
holds that copy identical to ci.yml. Fix Regression Gate
(fix-regression-gate.yml) holds every issue the PR body closes by keyword
(Closes #N, Closes ModernRelay/omnigraph#N, or the issue URL) to a
regression in the diff: a top-level .gqt case or a #[test]-attributed
issue_N function, added or strengthened, in a top-level test target
(tests/<name>.rs) or src/ module under crates/* or tools/* (an owner
test not yet named for the issue is renamed to carry issue_N when
extended). A PR whose diff changes no path under crates/ or tools/
(Markdown files there aside) and neither root Cargo.toml nor Cargo.lock
passes unexamined, with a notice annotation saying so: a fix in a workflow, a
script, a document, or a deployment file has no logic or Rust test that could
witness it. Owners the gate does not recognize inside those paths (helper and
fixture modules, a script under a crate, a rustdoc-only change) go through
the no-repro label, which a maintainer applies to waive the check per PR;
scripts/check-fix-regression.py is the check. A failure names the code
paths that made the gate look, the ways through, any near miss in the diff (a
case whose header says # issue: N under another name or a subdirectory, a
test named with the bare number, moved, under a leading _, or in a helper
module, an issue-named function with no test attribute), and a case skeleton,
as a log line and as a GitHub error annotation. It is a policy check, so it runs on pull_request_target: the
workflow and the script come from main, and the pull request head is fetched
only as data for the diff range, never checked out or executed. It runs on
body edits and label changes as well as pushes, builds nothing, and takes no
documentation-only skip.
The Check AGENTS.md Links context also runs scripts/check-docs.py, which
validates local documentation links, user/developer audience boundaries, RFC
location and metadata, registry agreement, and the absence of committed
merge-conflict markers in Markdown. Before the documentation checks run,
the same context also rejects any pull request whose own diff adds a
conflict-marker line in any file type, annotating each offending file and
line; markers already on the base branch never fail an unrelated pull
request. There is no exemption; a document that must quote a conflict block
indents the markers one space. After the documentation checks, the same
context runs typos (crate-ci/typos, pinned by commit) over every tracked
text file, hidden paths such as .github/ included; the tool itself skips
Cargo.toml manifests, lock files and binaries. It matches each word against
a list of known misspellings, not a dictionary: an unknown word never fires,
and an identifier fires only when one of the words it splits into is on the
list (a CamelCase fragment or a short abbreviation can be one). A
flagged token that is correct where it occurs gets one commented line in
.typos.toml: under [default.extend-words] when it appears in prose and
code alike, under [type.rust.extend-words] (or extend-identifiers for one
exact identifier) when it exists only in Rust sources, so the same
misspelling in Markdown still fires; a hyphenated prefix goes in
extend-ignore-re; a generated text file gets an extend-exclude glob.
Every exemption lives in that one file: the job refuses a sibling
typos.toml or _typos.toml, and CI ignores a config file in a
subdirectory (a local run inside that subdirectory would not).
Graph Vocabulary Guard remains a required reporting context, but its
substrate-sized audit steps are currently disabled everywhere (decision of
2026-08-28; the job-level VOCABULARY_AUDIT_ENABLED variable in ci.yml is
the single switch). The job still
runs its unit tests and reports success so the exact-SHA release gates stay
wired. When re-enabled it checks OpenAPI, Rust presentation strings, and public
Rust against the reviewed terminology inventory after merge, on tags, and by manual
dispatch. User documentation is intentionally outside this exact-occurrence
audit and is owned by scripts/check-docs.py. The AWS job reports a successful
skip for a documentation-only change; formatting and Clippy are also skipped by
the classifier without leaving required contexts pending.
Automatic edge and versioned publication are jobs in the same CI run and cannot
start until that run's vocabulary audit succeeds. Each publishing workflow then
re-verifies the authorizing CI run, resolves its source once to an immutable
commit, and builds only that commit. Version tags are checked again immediately
before publication; a stale main audit cannot move the rolling edge tag
backward. A manual backfill must already have a successful non-PR vocabulary
audit for the exact commit. The current manual workflows therefore fail closed
for historical pre-guard tags rather than offering a force bypass.
GitHub evaluates a tag-push workflow from the commit selected by that tag. A
new v* tag must not be created against a commit that predates these gates,
because no later workflow edit can retroactively replace that commit's old
publisher definitions. Enforcing that administrative boundary against tag
creators requires repository tag policy in addition to the checked-in workflow
gate.
Formatting and Clippy use the repository's pinned toolchain. Lints remain warnings in the workspace; CI applies -D warnings. Clippy runs both the default and failpoint-superset graphs.
Repository metadata gates also check:
- immutable commit SHAs for external Actions and reusable workflows;
- agreement between container and package binary sets;
- the dependency direction around
omnigraph-azure-admission.
Container entrypoint and Azure deployment-validation jobs test argument composition, non-destructive Bicep validation, bootstrap readiness/admission modes, and non-root image ownership.
The workspace suite (Test Workspace) runs on every non-documentation pull request, on every push to main, on release tags, and by manual dispatch. GQT has its own configured owner above. The main, tag, and dispatch form (a pull request drops --no-fail-fast):
cargo test --workspace --exclude omnigraph-gqt --locked --no-fail-fast \
--features omnigraph-engine/failpoints,omnigraph-cluster/failpointsOn a pull request it is a reporting context, not a required one
(branch-protection.md), and it fails fast: wait for
it to report, and read a red result, before merging. On main, tags, and
dispatch it is the post-merge detection channel and keeps --no-fail-fast,
so every independent failure stays attributable; a red run there is
stop-the-line. The job compiles in one step (cargo test --no-run) and runs
in the next, so compile and run wall clock read apart in the log.
The main run also seeds the dependency cache that pull requests restore.
Every Swatinem/rust-cache step in ci.yml, gq-logic-tests.yml, and
dst.yml saves only from main (save-if): a save from any other ref, a
pull-request branch or a tag, is restorable by no pull request and only
evicts shared entries under the repository cache cap. The pull-request-path
jobs also save when red (cache-on-failure): dependency artifacts are valid
whatever the test verdict, and a red seed run would otherwise leave every
pull request cold until main is green again.
Every Rust job in those three workflows installs the rust-toolchain.toml
pin with a bare rustup toolchain install; the rustc version is part of
every cache key, so the pin is what keeps caches warm across Rust releases.
The release and publish workflows still build on the floating stable
action and save their caches from the tag ref; they are outside this rule.
The remaining jobs own contracts that need special infrastructure. They run after merge, on tags, and by manual dispatch; three of them, the format fence, the RustFS S3 integration, and the AWS feature build, also run on pull requests:
- Graph vocabulary audit checks OpenAPI, Rust presentation strings, and public Rust against the reviewed terminology inventory (audit steps currently disabled; see above).
- V5 ↔ V6 format fence builds the immutable final-v5 CLI and proves mutual refusal plus the documented export/init/load rebuild. It also runs on every non-documentation pull request, as a reporting context: the rebuild check compares the rebuilt export against the predecessor's, so a loss or a spelling change in what it compares reports on the pull request; wait for it as for
Test Workspace. A red fence on a pull request that touched neither the export, the loader, nor the format is inherited frommain: compare with the latestmainrun before reading it as the pull request's. - RustFS S3 integration runs configured engine, server, cluster, CLI, and recovery owners. A configured test that skips is a failure. It also runs on every non-documentation pull request, as a reporting context: the configured S3 owners run nowhere else, so a contract change that updates only the local-FS twin of an object-store test reports on the pull request instead of first appearing on
main; wait for both shards as forTest Workspace. A red shard on a pull request that touched no object-store code, or one that names no test (the 60-minute ceiling, the image pull, RustFS readiness), is inherited frommainor from infrastructure: compare with the latestmainrun before reading it as the pull request's. To reproduce locally, the job'senvblock and itsStart RustFSandCreate RustFS test bucketsteps inci.ymlare the complete recipe. - Azurite Azure integration runs only after merge, on tags, or by manual
dispatch: its 90-minute ceiling would outrun
Test Workspaceon a pull request. It exercises configured storage, admission-lease, recovery, cluster, server, and CLI owners against a digest-pinned Azurite image, then verifies that control objects, Lance data, and the admission object use the declared container. - AWS feature builds and tests
omnigraph-serverwith--features aws; it also runs on every pull request, as a required context.
Azure remains a qualification preview. Emulator coverage and the completed managed-identity smoke proof do not replace the pending adversarial live-Azure matrix, and every mutation-capable Azure server must retain the admission-wrapper boundary.
CI checks OpenAPI drift but never rewrites openapi.json. Regenerate an intentional API change locally as described in testing.md.
Two workflows own the simulator's pinned tests and generated fleets; both set
RUSTFLAGS: --cfg tokio_unstable themselves (the omnigraph-dst crate
compiles empty without it, so the default jobs are unaffected):
dst.yml(per PR and onmainpushes): the pinned deterministic suite — every failure line carries the universe seed, so a red run is reproducible locally from the log alone. The job also lints the shipped engine shape (-p, nodstfeature), which workspace feature unification hides from the default Clippy job. Whether the suite blocks a merge is the branch-protection required-contexts list.dst-nightly.yml(cron 03:00 UTC + manual dispatch): matrix-sharded deterministic and concurrent fleets over date-derived, mutually disjoint seed intervals. Failures are logs with seed rows, not required contexts; the concurrent fleet'swildmode makes no replay claim.
gq-logic-tests.yml separately owns authored GQT execution through DST. Its
configured step runs from crates/omnigraph-gqt to load tokio_unstable.
An unavailable-runtime refusal test does not replace executing the DST cases.
For Rust changes:
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings -W clippy::dbg_macro
cargo clippy --workspace --all-targets --locked \
--features omnigraph-engine/failpoints,omnigraph-cluster/failpoints \
-- -D warnings -W clippy::dbg_macro
cargo test --workspace --exclude omnigraph-gqt --locked \
--features omnigraph-engine/failpoints,omnigraph-cluster/failpoints
cargo test -p omnigraph-gqt --locked --lib --test runner_dispatchFrom crates/omnigraph-gqt, also run the complete configured package:
cargo test -p omnigraph-gqt --locked
cargo clippy -p omnigraph-gqt --all-targets --locked -- -D warnings -W clippy::dbg_macroFor repository metadata and workflow changes:
bash scripts/check-agents-md.sh
python3 scripts/check-docs.py
python3 scripts/check-workflow-action-pins.py
python3 scripts/check-release-vocabulary-gates.py
python3 scripts/check-container-binary-contract.py
python3 scripts/check-azure-admission-boundary.py
typos # from the repository root; a subdirectory run scans only that subtree
actionlint .github/workflows/*.yml
shellcheck scripts/*.shtypos (cargo install typos-cli --locked --version 1.50.1, the version ci.yml pins; the misspelling list grows per release, so a newer local binary can flag words CI accepts), actionlint and shellcheck are developer tools, not workspace dependencies. Run the applicable subset when a change does not touch their surface.
| Workflow | Trigger and output |
|---|---|
release-edge.yml |
Called by a non-documentation main CI run after its vocabulary audit, or manually for an already-audited current main; updates the rolling edge release and platform archives. |
release.yml |
Called by audited v* tag CI or manually for an already-audited tag; builds platform archives, publishes the GitHub release, updates Homebrew when credentials are available, and smoke-tests the Windows installer. |
publish-crates.yml |
Called by audited v* tag CI or manually for an already-audited tag; publication remains paused until the registry-ownership policy changes. |
publish-image.yml |
Called by audited v* tag CI or manually for an already-audited tag; builds the bookworm-compatible public server image for GHCR and, when configured, Docker Hub. Manual backfills do not move latest. |
package.yml / omnigraph-package.yml |
Manual AWS CodeBuild packaging for default and AWS-feature artifacts, with checksums, digests, and attestations. |
refresh-docs-site.yml |
Documentation changes on main or manual dispatch; requests a docs-site redeploy. |
Release archives and containers include the CLI, server, and Azure admission wrapper where their packaging contract requires all three. Keep the reusable package workflow, Dockerfile, and binary-contract check aligned.
- Preserve a reporting path for every branch-protection context on every pull request.
- Keep external Actions and reusable workflows pinned to full commit SHAs.
- Update the documentation classifier when adding a new documentation format; never classify by extension outside the approved docs paths.
- Keep configured object-store jobs fail-closed on accidental skips.
- Keep every automatic artifact publisher transitively behind a successful exact-SHA vocabulary audit; a skipped pull-request context never authorizes publication.
- Update branch-protection.md only when the declared required contexts or policy actually change.