Skip to content

Migration path rst to trlc #441

Description

@FScholPer

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
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)

  1. 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.)
  2. Handle [version==N] → emit Ref@N; reject a bare ref if the model requires a version.
  3. 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.rstIndex).

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

  1. 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.
  2. Run trlc --verify + lobster tracing on it as a non-gating CI job.
  3. 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

  1. score_platform bumps score_tooling 1.1.2 → current (rst_to_trlc does not exist in 1.1.2).
  2. 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.
  3. Add the missing stkh_req type to the requirements model (a commented-out StdReq
    stub already anticipates this) — otherwise platform's feat_reqs 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_reqstkh_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)

  1. 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

  1. 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.
  2. 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)

  1. 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.
  2. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions