A pulseengine.eu playground exploring how rivet — a typed-traceability tool — handles the Eclipse S-CORE sphinx-needs corpus.
Not affiliated with the Eclipse Foundation. Eclipse S-CORE is a substantial body of safety engineering work; this workspace converts a snapshot of that work into a second representation so we can study how the two toolchains relate. Upstream eclipse-score repositories are pinned by exact SHA in
rivet.yaml+rivet.lockand materialised on demand bymake sync— no upstream code is vendored here.
Pulseengine builds rivet as part of a wider verification-and-attestation
toolchain. We needed a real, non-toy corpus to test rivet's typed
schemas, its cross-repo machinery, its variant subsystem, and the
ergonomics of rivet validate at scale. Eclipse S-CORE is exactly
that kind of corpus — open, well-documented, ISO 26262 / ASPICE-shaped,
actively maintained by a working group with strong automotive
expertise. Working through it gives us:
- A concrete answer to "does rivet's schema cover real ASIL-rated artifact shapes?" — measured against 2985 actual sphinx-needs artifacts, not bespoke examples.
- Direct feedback on rivet's developer experience when ingesting an upstream we don't control.
- A test surface for proposed rivet features (variant models, per-field embeds, cross-repo externals) under realistic load.
It's a learning exercise for pulseengine. Anything that looks polished about it owes that to upstream eclipse-score — the metamodel, the process documentation, and the artifact discipline are theirs.
playground-eclipse-score/
├── upstream/ # symlinks → .rivet/repos/<short>/
│ ├── eclipse-score-score/
│ ├── eclipse-score-process-description/
│ └── ... (58 total, all pinned)
├── rivet/ # generated rivet projects
│ ├── eclipse-score-score/
│ │ ├── rivet.yaml
│ │ ├── artifacts/imported.yaml # generated by `make convert`
│ │ ├── docs/<path>/*.md # generated markdown alongside
│ │ └── coverage.md # per-repo conversion report
│ └── ... (one per upstream)
├── tools/
│ ├── score_import.py # RST → rivet YAML converter
│ └── falsification-journey.md # log of every gap surfaced + closed
├── vendor/
│ └── rivet-schemas/ # pinned snapshot of rivet schemas
├── variants/
│ ├── feature-model.yaml # starter variant model
│ └── bindings.yaml # 4 named worked variants
├── rivet.yaml # combined corpus + externals declaration
├── rivet.lock # SHA pins (cargo-like)
├── Makefile # sync / convert / validate / variant-check
└── README.md # you are here
Each upstream/<dir> and rivet/<dir> is named
eclipse-score-<upstream-repo-name> so a single eclipse-score-*
glob filters the entire surface in git, ripgrep, IDE pickers, etc.
The doubled-name case (eclipse-score-score, where the upstream repo
is literally called score) preserves the verbatim repo name so the
rebase target is unambiguous.
The fork uses rivet's externals: machinery (see
rivet docs cross-repo) to pin all 58 upstream eclipse-score repos
by exact commit SHA. The authoritative pin source is the externals:
block in rivet.yaml plus the generated rivet.lock. Anyone with a
checkout and internet access can materialise the exact corpus state
this workspace was validated against:
git clone https://github.com/pulseengine/playground-eclipse-score
cd playground-eclipse-score
make sync # rivet sync upstreams + create upstream/ symlinks
make convert # run the RST → YAML converter
make validate # rivet validate + variant check-allUpstream clones land in .rivet/repos/<short-name>/ (gitignored,
rivet-managed). The Makefile creates upstream/eclipse-score-<X>/
symlinks so the converter's paths stay readable.
The CI workflow at .github/workflows/validate.yml runs the same
three targets on every push, pull request, and nightly cron — so the
numbers in the table below stay live, not snapshot.
$ make validate
Result: PASS (2507 warnings)
4/4 variants passed (0 failed)
| Stat | Value |
|---|---|
| Upstream eclipse-score repos pinned | 58 |
| RST files scanned | 919 |
| Sphinx-needs artifacts converted | 2985 |
| Repos with at least one need | 14 |
| Repos with zero needs (Doxygen-only / toolchain / website) | 44 |
| Unknown eclipse need types encountered | 0 |
| Unknown option keys encountered | 7 |
needextend rules resolved |
114 → 2504 tag mutations |
| Markdown documents emitted | 634 |
Variant axes in variants/feature-model.yaml |
6 |
| Named worked variants | 4 |
rivet variant check-all result |
4/4 pass |
rivet validate errors |
0 |
rivet validate warnings |
2507 (all lifecycle gaps in eclipse data, not schema issues) |
Per-repo (top 14 by need count, remaining 44 report 0 needs by design):
| Repo | RST files | Needs converted |
|---|---|---|
| eclipse-score-score | 404 | 1373 |
| eclipse-score-process-description | 299 | 1257 |
| eclipse-score-baselibs_rust | 21 | 76 |
| eclipse-score-module_template | 39 | 65 |
| eclipse-score-persistency | 30 | 61 |
| eclipse-score-docs-as-code | 24 | 61 |
| eclipse-score-inc_security_crypto | 10 | 38 |
| eclipse-score-lifecycle | 19 | 28 |
| eclipse-score-inc_someip_gateway | 14 | 12 |
| eclipse-score-baselibs | 5 | 9 |
| eclipse-score-reference_integration | 3 | 2 |
| eclipse-score-score-crates | 1 | 1 |
| eclipse-score-logging | 2 | 1 |
| eclipse-score-inc_os_autosd | 1 | 1 |
Coverage reports per repo at rivet/<repo>/coverage.md list files
scanned, needs converted, anything skipped, and unknown option keys
encountered. They're the surface where converter limitations show up.
The conversion path (and every fix the corpus surfaced) is logged in
tools/falsification-journey.md —
useful if you want to understand the trade-offs the converter makes.
When upstream eclipse-score moves and the pinned SHAs lag:
# 1. Pull each upstream to its remote HEAD.
make rebase
# 2. Write the new HEADs back into the pin file.
make update-pins # rivet lock --update
# 3. Re-run the converter against the new RST.
make convert
# 4. Re-run the oracle. If it does NOT pass, the corpus has surfaced
# a real gap — either:
# • eclipse added a new need type / link / option we don't cover
# • eclipse changed an existing predicate's semantic / target type
# • eclipse fixed something we were working around
# Each of those is useful data — fix the converter or schema,
# re-run, commit the new snapshot.
make validateThe nightly CI cron does the same flow against upstream HEADs with
continue-on-error: true — the rebased oracle is informational, not
gating, so it tells us when eclipse moved without breaking the main
green badge.
For each sphinx-needs feature eclipse uses heavily, the converter either captures it natively, transforms it, or routes to a rivet equivalent. The interesting cases:
Every eclipse need type (~30 of them: stkh_req, feat_req,
comp_req, feat, feat_arc_sta/dyn, comp_arc_sta/dyn,
logic_arc_int, mod, dd_sta/dyn, sw_unit, tsf, tenet,
assertion, workflow, role, workproduct, gd_*, std_*,
feat_saf_fmea, comp_saf_fmea, *_saf_dfa, dec_rec, doc_*,
testcase, tool_req, aou_req) maps to a corresponding rivet
artifact type via TYPE_MAP in tools/score_import.py. Options
(:safety:, :security:, :reqtype:, :rationale:, etc.) become
typed fields:. Links (:satisfies:, :implements:, :fulfils:,
:violates:, :mitigated_by:, etc.) become typed links:.
Sphinx-needs .. needextend:: directives let authors mutate
already-declared needs by filter expression (e.g. "every need under
process_areas/safety_analysis/ gets +tags: safety_analysis"). The
converter pre-scans the RST tree for needextend blocks, evaluates
the filter expression at conversion time, and bakes the resulting
attribute mutations into the static YAML artifacts. 114 rules across
the corpus produce 2504 mutations.
Supported filter shapes (covers all 114 occurrences):
docname is not None and "<path>" in docname"<sub>" in idc.this_doc()- combinations with
and type == "<X>"/and not status
Mutation keys: +tags, +belongs_to (the only two eclipse uses).
For every RST file containing at least one need directive, the
converter emits a parallel markdown file at
rivet/<repo>/docs/<same-path>.md. Section structure is mapped to
markdown headings, need declarations become ### {title} followed by
an {{artifact:ID:status,safety-level,security}} embed, and inline
sphinx-needs :need:X`` roles are rewritten to rivet's [[X]]
syntax. Currently 634 documents.
Eclipse uses .. needtable:: / .. needpie:: / .. needarch::
directives to embed filtered tables and charts directly in their RST
docs. Rivet ships equivalents that consume the same artifact graph:
{{coverage}}/{{coverage:rule-name}}— coverage bars per rule{{matrix}}/{{matrix:source:target}}— traceability matrices{{table:TYPE:FIELDS}}— filtered artifact tables{{stats}}/{{stats:types}}/{{stats:status}}— metrics/api/v1/{stats,coverage,artifacts,diagnostics}— JSON API with permissive CORS, intended for Grafana embeddingrivet export --format html— static compliance bundle
Authors who want eclipse-style "tables and pies in the doc" use these embeds inside the emitted markdown. The converter doesn't translate the in-doc widgets directly — that's an open item, see the falsification journey doc for the current shape.
Eclipse-score's stakeholder requirements include a placeholder for
variant management (stkh_req__overall_goals__variant_management,
status: valid, rationale: tbd). The upstream metamodel does not yet
have a variant need type or a binding format.
This playground includes a worked starter feature model under
variants/ that captures the variant axes already implicit in
eclipse's data:
feature-model.yaml— 6 axes (safety-classification, security, requirement-category, process-area, standards-compliance, components), with an attribute-schema for typed attributes and constraints encoding rules like "ASIL-B implies ISO 26262 compliance"bindings.yaml— 4 named worked variants (persistency-qm,persistency-asil-b-secure,platform-only,aspice-assessment)
The model is offered as a starting point an integrator could adopt and
extend, not as a proposal for upstream eclipse-score to adopt. Run
make variant-check to validate the model + named variants, or
make variant-matrix to emit a GitHub Actions CI matrix from the
named variants. Both use rivet variant — see
rivet docs schemas-overview.
vendor/rivet-schemas/ is a pinned copy of rivet/schemas/. It's
vendored so this workspace is self-contained — make validate works
without a local pulseengine/rivet checkout. The pinning also protects
the corpus oracle from being silently invalidated by upstream rivet
schema changes.
To refresh from a newer rivet:
cp /path/to/rivet/schemas/*.yaml vendor/rivet-schemas/
make validateTo set expectations:
- Not an alternative to eclipse-score. The upstream project is the
source of truth for everything in
upstream/. This workspace produces a derived representation; it does not modify, replace, or compete with anything eclipse-score ships. - Not a fork in the GitHub sense. No PRs flow from here to eclipse-score; we don't claim to maintain a divergent copy. The word "fork" in the directory name reflects that we hold pinned references to upstream, nothing more.
- Not endorsed by the Eclipse Foundation or any eclipse-score maintainer. Pulseengine is an independent project at pulseengine.eu. Any issues you find in this workspace's converter or schemas should be filed here, not upstream.
- Not a guarantee of certification readiness. rivet itself is
pre-1.0; this workspace exercises rivet against real data but
neither rivet nor this playground has been independently assessed
for use as ISO 26262 / DO-178C / IEC 61508 evidence. See rivet's
SAFETY.mdfor its current self-assessed scope.
16 eclipse-score repositories marked archived: true on GitHub are
excluded from this workspace per the original fork-setup decision:
examples, inc_abi_compatible_datatypes, inc_ai_platform, inc_config_management, inc_feo, inc_gen_ai, inc_json, inc_mw_com, inc_mw_log, inc_process_test_management, inc_process_variant_management, inc_score_codegen, operating_system, test_integration, test_module_a, test_module_b
Three more repos are external forks (nlohmann_json,
bazel_registry_ui, rules_rust) and are skipped because they
mirror upstream projects with their own metamodel conventions.
PRs welcome for: converter improvements (tools/score_import.py),
variant model extensions (variants/), documentation, additional
named variants, fixes to the falsification-journey log.
For schema changes that touch vendor/rivet-schemas/, please also
file an issue on pulseengine/rivet
so the change propagates upstream — the vendored copy is a snapshot,
not a fork of the schema itself.
For anything that needs to change in eclipse-score's RST or metamodel, please contribute upstream at the relevant github.com/eclipse-score repository.
Apache-2.0 — matches both eclipse-score and rivet. See LICENSE.
The eclipse-score upstream content materialised under
.rivet/repos/<X>/ retains its original licensing. The converter
output under rivet/<X>/ is a derivative work and inherits the
upstream's Apache-2.0 license, plus this workspace's Apache-2.0 for
the overlay (converter, schema vendoring, variant model, this README).