eclipse-score / eclipse-score/tooling

Migration path rst to trlc

Open
#441 0 comments 0 reactions 1 assignee Claimed by @antonkri View on GitHub
Dominant language
Rust
Stars
8
Forks
37
Avg merge
2d 13h
Merged PRs (30d)
22

Description

### Epic Description

# Sums up the steps to get a working sphinx to trlc path with low maintainance effort

# Migration path: Sphinx-needs RST → TRLC, ending in one metamodel source of truth

Context: `rst_to_trlc` (eclipse-score/tooling, `bazel/rules/rules_score/src/rst_to_trlc.py`,
exposed as `//bazel/rules/rules_score:rst_to_trlc`, wrapped by the rule in
`private/rst_to_trlc.bzl`) converts Sphinx-needs requirement directives to TRLC.
RST stays the authoring format (community decision). The end state must be a single
metamodel definition, because today the metamodel exists three times and already diverges.

## Current divergence (evidence, measured 2026-08-24)

| Aspect | docs-as-code `metamodel.yaml` | `score_requirements_model.rsl` | `rst_to_trlc.py` |
|---|---|---|---|
| requirement types | 6 (`gd_req`, `stkh_req`, `feat_req`, `comp_req`, `tool_req`, `aou_req`) | 4 usable (`AssumedSystemReq`, `FeatReq`, `CompReq`, `AoU`) | 4 (`DIRECTIVE_TO_TRLC`) |
| `safety` value domain | `^(QM|ASIL_B)$` | `Asil` enum QM, A, B, C, D | maps QM, ASIL_A..D (`SAFETY_MAP`) |
| `satisfied_by` (mandatory link on `comp_req`/`feat_req`) | mandatory | not represented | dropped (not in `_ALLOWED_RST_ATTRS`) |
| `covers` (→ AoU) | optional link | `derived_from` carries AoU refs | dropped |
| `status`, `security`, `reqtype`, `valid_from` | mandatory options | not represented | dropped |
| version selector `[version==N]` | Sphinx-needs syntax | `Ref@N` | copied verbatim → invalid TRLC |
| `stkh_req` | defined, top of trace chain | no type | not convertible |

Consequences today: a `:status: invalid` requirement becomes an indistinguishable valid TRLC
record; a mandatory `satisfied_by` link is lost without a diagnostic; every `derived_from`
with a version selector produces syntactically invalid TRLC.

## Phase 0 — Measure, do not change anything (low-hanging, no process impact)

1. **Drift report tool** in tooling: reads `metamodel.yaml`, `score_requirements_model.rsl`
and the converter's tables, prints the table above. Run as a non-gating CI job.
Value: makes the problem factual and reviewable, and becomes the regression test for
every later phase.
2. **Conversion dry-run job**: run `rst_to_trlc` over all requirement RST in
score / persistency, report per file: records converted, directives skipped,
fields dropped, refs that cannot resolve. No build wiring, no gating.

Deliverable: a numbers-based problem statement for the community discussion.
Risk: none. Effort: ~1 session.

## Phase 1 — Make the converter honest (still no source or process change)

3. **Fail loud instead of skipping silently**: unknown directive with a `requirement` tag,
unknown/dropped mandatory option, or unresolvable ref ⇒ error, not silence.
(Silent data loss is the single property that would sink any ISO 26262 tool argument.)
4. **Handle `[version==N]`** → emit `Ref@N`; reject a bare ref if the model requires a version.
5. **Expose `package =`** through `score_requirements_rule` / `rst_srcs_to_trlc`
(today only the raw `rst_to_trlc` rule accepts it, so the package name silently
defaults to the CamelCased file stem, e.g. `index.rst` → `Index`).

Deliverable: converter that either produces correct TRLC or fails.
Risk: low, self-contained in tooling. Effort: ~1 session.

## Phase 2 — Pilot on one component, shadow mode

6. Wire **one** component in-build, e.g. persistency KVS:
`component_requirements(name = "comp_req", srcs = ["index.rst"], ref_package = ..., deps = [...])`.
35 `comp_req` records exist there today.
7. Run `trlc --verify` + lobster tracing on it as a **non-gating** CI job.
8. Accept that the chain is incomplete: `feat_req__persistency__*` live in
eclipse-score/score, referenced via `@score_platform//:needs_json` — see Phase 3.

Deliverable: proof the pipeline works end to end on real data, with a known-gap list.
Risk: low (non-gating). Effort: ~1 session.

## Phase 3 — Close the cross-repo chain

9. **score_platform bumps `score_tooling` 1.1.2 → current** (`rst_to_trlc` does not exist in 1.1.2).
10. Add `feature_requirements(...)` targets with public visibility per feature directory in
score_platform (no BUILD files exist under `docs/features/**` today), so the TRLC records
are exported via `TrlcProviderInfo` and consumers put them in `deps`.
11. **Add the missing `stkh_req` type to the requirements model** (a commented-out `StdReq`
stub already anticipates this) — otherwise platform's `feat_req`s derive from records
that cannot exist, and dropping those refs would silently cut off the top of the chain.

Deliverable: persistency `comp_req` → platform `feat_req` → `stkh_req` resolvable in TRLC.
Risk: medium — cross-repo version bump and a model extension needing community agreement.
Effort: ~2 sessions plus review latency.

## Phase 4 — Metamodel-driven converter (drift halved)

12. `rst_to_trlc` **reads `metamodel.yaml`** instead of its hard-coded `DIRECTIVE_TO_TRLC`,
`SAFETY_MAP`, `_ALLOWED_RST_ATTRS`, `_REF_FIELDS`. Adding a need type or tightening a
regex in docs-as-code then cannot leave the converter behind: it either flows through
or fails generation.

Blocker to resolve here: `metamodel.yaml` lives in docs-as-code, the converter and `.rsl`
in tooling, and tooling has **no** `bazel_dep` on `score_docs_as_code` (only the reverse).
Options: (a) tooling takes the dep, (b) the metamodel moves to a small shared module both
depend on. (b) is cleaner and is a prerequisite for Phase 5.

Effort: ~1 session after the dependency direction is decided.

## Phase 5 — Single point of truth: generate the `.rsl`

13. **Generate `score_requirements_model.rsl` from `metamodel.yaml`.** The YAML is already
machine-readable, schema-validated (`metamodel-schema.json`) and already drives generated
RST (`generate_metamodel_rst.py`), so it is the de-facto metamodel source.
Types, mandatory/optional options, value-domain regexes and link targets all map onto
TRLC types, fields and enums.
14. Hand-maintained `.rsl` becomes a generated artifact; the Phase 0 drift report becomes a
gating conformance test that must report zero divergence.

End state: **one** metamodel definition (`metamodel.yaml`), two generated representations
(Sphinx-needs config, TRLC `.rsl`), one generated instance representation (`.trlc` from RST).
Someone adding a need type or changing a parser rule can no longer create divergence.

## Phase 6 — Qualification argument (ISO 26262)

15. The converter sits in the safety-relevant path, so it needs tool-error detection.
Cheapest credible measure: a **diverse second extraction** from the Sphinx-needs
`needs.json` (already plumbed as `SphinxNeedsInfo.needs_json_file`) compared against the
generated TRLC — IDs, safety, version, links — failing the build on any diff.
Sphinx then sits only in the *checker*: its failure can cause a false alarm, never a
silent wrong output, so Sphinx itself does not need qualification.
16. Qualification scope is then: `rst_to_trlc` (~240 lines, stdlib only, unit-testable to
full branch coverage) + the `.rsl` generator, both with an independent checker.

## Open decisions for the community

- Dependency direction for the metamodel (tooling → docs-as-code, or a shared module).
- Whether `stkh_req` (and `gd_req` / `tool_req`) get TRLC types, or are explicitly out of scope
with a documented traceability break.
- Whether attributes the TRLC model does not represent (`status`, `security`, `reqtype`,
`valid_from`) are added to the model or explicitly declared non-safety-relevant.
- Whether the `safety` domain is `QM|ASIL_B` (metamodel) or QM..ASIL_D (`.rsl`) — one of the
two is wrong today.

### Acceptance Criteria (DoD)

Phases done

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.