Working with ReqIF

The ReqIF specification frames its use around two exchange scenarios between exchange partners, typically two different requirement authoring tools, possibly at two different organizations:

  • One-way exchange: partner A exports a specification from their authoring tool, transmits it to partner B, and partner B imports it into their authoring tool.

  • Round-trip exchange: same as one-way, but partner B then modifies the specification, exports it back to partner A, and partner A re-imports it on top of the original.

The Syside ReqIF CLI plays the role of the “ReqIF tool” on the SysML v2 side of either scenario. The current workspace (the directory where the CLI runs) is the SysML v2 authoring tool’s storage. The remote partner is whatever requirements management tool produced or will consume the .reqif / .reqifz exchange document.

The sections below follow a full round-trip, importing a requirements specification, revising it in SysML v2, and exporting it back to ReqIF.

Workspace setup

Before any ReqIF document can be imported or exported, the workspace needs the SysideReqIF.sysml library. This library defines the metadata used to mark SysML v2 elements as ReqIF carriers: the @reqif / @reqif_* tags that pin a stable ReqIF identity onto every imported element, and the #reqif_*_type prefixes used on the four ReqIF *Type templates.

From the directory to be used as the workspace, run:

syside reqif init

This drops lib/SysideReqIF.sysml into the workspace. Pass --lib-dir to put it somewhere else. The library is plain SysML v2 and is intended to be checked in alongside the rest of the model.

init is idempotent: re-running it against a workspace whose library already matches the current release is a safe no-op (Found existing SysideReqIF.sysml library. No action needed.). If the copy in the workspace differs, init rewrites it to the shipped text and says Updated SysideReqIF.sysml with latest SysideReqIF library content., so init doubles as the upgrade command. The other commands produce a clear error pointing back to init when the library is missing or out of date, so it is hard to forget.

ReqIF to SysML v2

This section describes the workflow for importing a ReqIF document into the workspace as SysML v2.

Importing the document

This step covers the ReqIF specification’s UC2: Import New Requirement Specifications and UC3: Update Requirement Specifications. From the SysML v2 side they are the same command: the CLI decides whether to create new elements or merge changes into existing ones based on what the workspace already contains. The import command provides several configuration options; the subsections below cover each one.

Receive a .reqif or .reqifz from the upstream partner and place it anywhere on disk, then run:

syside reqif import path/to/incoming.reqif

The command parses the exchange document, runs the Preconditions against both the incoming document and the workspace, and writes a fixed package layout into the workspace (ReqIF_Import_Enums, ReqIF_Import_Types, ReqIF_Import_Objects, ReqIF_Import_Specifications, ReqIF_Import_Relations). Binary attachments referenced from XHTML attribute values are extracted into attachments_reqif/ (or the directory passed to --attachments-dir). An entry inside a .reqifz whose path would land outside that directory is skipped with a warning rather than extracted, so a malformed or hostile archive cannot write elsewhere in the workspace; the rest of the import proceeds.

The directory must resolve to a subdirectory inside the workspace, and not to the workspace root itself: an attachment link is recognized by its directory prefix, which the root does not provide. The Markdown links written into the model are spelled relative to the .sysml file that holds them (../../attachments_reqif/pic.png in a --split-by member file), so the workspace stays portable across machines and the language server can render the image on hover.

Changed in version 0.11.0: Attachment links used to be spelled relative to the workspace root, which resolved only for files sitting directly in it. The workspace root is now rejected as --attachments-dir.

Tip

Run syside reqif import from a clean Git working tree (or whichever VCS is in use). import rewrites the ReqIF_Import_*.sysml files in place, and on a delta run also rewrites any other files that hold ReqIF elements, so committing first allows reviewing the git diff afterwards to see exactly what the partner changed, or, on a fresh import, sanity-checking the generated layout before building on it.

Imported elements are already locked: each carries an @reqif (or @reqif_*) metadata tag with the original ReqIF identifier and last_change timestamp, so they will round-trip back out without losing their identity.

Note

Re-running syside reqif import against a workspace that already contains a prior import applies only the delta. Elements that had a ReqIF identifier but are no longer present in the incoming document are reported as orphaned and left in place. Review and delete them by hand if they should be removed. See Known limitations for the cases that the delta path does not yet handle and that require a fresh workspace.

Resolving duplicate names

Some upstream requirement tools require every element to carry a LONG_NAME but do not enforce that those names be unique. The resulting .reqif fails the SysML v2 side’s name-uniqueness preconditions (see Preconditions) because two SysML v2 elements in the same effective namespace cannot share a name. Re-run the import with --resolve-duplicate-names to make it pass:

syside reqif import --resolve-duplicate-names path/to/incoming.reqif

The flag appends a short identifier-derived suffix (__<6chars> from each colliding element’s ReqIF id) to every name involved in a collision. A one-line stderr report is printed for each rename so the SysML v2 output can be correlated back to the source. On the way back out, syside reqif export strips the suffix automatically, and the receiving partner sees the original LONG_NAME. The unmangling is keyed off the recorded identifier, so if a mangled element is renamed on the SysML v2 side (drop the suffix, change the base, etc.) the export honors the chosen name instead of trying to restore the original.

The flag also covers names that collide inside a single owner rather than in the top-level namespace: EnumValues within one enumeration DatatypeDef, and AttributeDefs within one SpecType. It additionally resolves an enumeration DatatypeDef that shares its name with an AttributeDef it types. In that last case the attribute is the side that gets the suffix, so the enumeration keeps its original name: that name qualifies every value expression in the model (Priority::high), and is what auto-completion offers while writing them.

Splitting large imports New in v0.10.1

By default, requirements are stored in a single ReqIF_Import_Objects.sysml file. For very large specifications, this file can become huge and hard to work with because VS Code automatically disables LSP for huge files. Therefore, when importing a large specification for the first time, pass the --split-by=<strategy> flag to partition the objects across several files using the specified <strategy>. Currently, Syside supports two strategies:

  1. hierarchy: splits the objects file by following ReqIF specification hierarchy until a subtree can fit within --split-max-loc lines (by default 10 000) or --split-max-depth limit is reached (by default unlimited). The files are named based on the corresponding requirement names by removing spaces and punctuation and converting them to PascalCase and grouped into folders mirroring the hierarchy. Since SysML file names do not affect namespaces, to avoid name collisions, the contents of the files are wrapped in packages whose names are constructed by concatenating the names of the entire path. Syside additionally emits aliases for the contained packages so that the elements can be reached by using qualified name syntax Folder::Subfolder::'requirement name'.

  2. flat-greedy: splits the objects file into chunks where each one is at most --split-max-loc lines long (by default 10 000).

Four further flags tune the result. Each is rejected outside the strategy it applies to, so a typo fails the import rather than being silently ignored:

  • --split-reexports / --no-split-reexports (either strategy): whether the base ReqIF_Import_Objects package re-exports the split member packages as public imports, so references through it resolve identically in every layout. Dropping them trades that for a smaller wildcard-import fan-out. Enabled on a fresh split import; on a delta, the workspace’s current state is kept.

  • --split-min-depth <n> (hierarchy): force the folder tree to unroll at least n levels under ReqIF_Import_Objects/, where the per-Specification folder is level 1, even where a subtree would fit one file. Use it to surface top-level semantic groups as folders. It must not exceed --split-max-depth, nor the shallowest leaf of any hierarchy, since a branch that ends before the floor cannot be unrolled to it. Default 0, meaning no forced unrolling.

  • --split-max-slug-length <n> (hierarchy): truncate the name-derived file and folder slugs to n characters, guarding against overly long paths. Collisions the truncation causes are resolved with a deterministic identifier-derived suffix. Default 64.

  • --rename-files (hierarchy, delta imports): reconcile the name-derived files, folders, packages, and aliases with the elements’ current names. All stale names are fixed, not only the ones this delta renamed. Without it, renamed elements keep their old paths and a warning lists them.

To import ReqIF using the hierarchical strategy, use:

syside reqif import --split-by=hierarchy path/to/incoming.reqif

--split-by is taken into account only during the first import. During the reimport, the split strategy is automatically recognized; specifying a different strategy during the reimport is, currently, an error. To make the file diff minimal and easy to review, reimport never reorganizes the existing elements; instead it adds them into locations that are closest to the ones where the elements would have been put during the regular import.

Namespacing imports New in v0.10.1

By default every import writes the same fixed ReqIF_Import_* package and file names, so importing two different specifications into one workspace would collide in those shared files. Pass --domain <label> to insert a label immediately after the ReqIF_Import_ unit: --domain Powertrain turns ReqIF_Import_Specifications into ReqIF_Import_Powertrain_Specifications (and likewise for the file names):

syside reqif import --domain Powertrain path/to/powertrain.reqif

Characters that are not filename-safe (spaces, the path separators / and \, and : * ? " < > |, etc.) are percent-encoded so the names stay valid paths on Windows and *nix and decode back to the original value; a warning lists any characters that were encoded. A delta re-import into the same workspace must reuse the same --domain value, since it selects which packages to update.

Working in SysML v2

If partner A sent the document for information only, the workflow ends here: the SysML v2 model is ready to be browsed, analyzed, or fed into downstream tooling. This is the one-way scenario from the ReqIF specification.

Otherwise, this is where the model takes shape, whether it arrived by import or is authored directly in SysML v2. Edit it as ordinary SysML v2: rename requirements, change attribute values, attach SysML v2 constructs that ReqIF cannot express (parts, actions, analyses), or add a new requirement under an existing Specification.

Two practical points:

  • Anything written in SysML v2 that is not annotated with an @reqif / @reqif_* metadata tag stays SysML-only and will not appear in any future ReqIF export. This is the intended way to enrich an imported document with model content that has no ReqIF counterpart.

  • Anything that is annotated with such a tag and intended to round-trip back out must end up with a stable identifier. Imported elements already have one; newly authored elements do not until the locking step runs.

See ReqIF and SysML v2 mapping for the table of equivalent constructs to use when authoring on the SysML v2 side.

Authoring from scratch

The workflow above starts from an incoming .reqif, but the SysML v2 side can also be the originator of a one-way exchange. In that case the steps reduce to:

  1. syside reqif init to drop in the library.

  2. Author the model directly in SysML v2 using the constructs listed in ReqIF and SysML v2 mapping. Use a #reqif_*_type stereotype on each abstract *Type template def, and an @reqif metadata block on each instance-side element (a specification, spec object, relation group, or spec relation) that should round-trip through ReqIF.

  3. syside reqif lock to assign identifiers.

  4. syside reqif export --title "<title>" outgoing.reqif to produce the exchange document.

From there the receiving partner imports outgoing.reqif into their tool exactly as in the inbound case above; the format is symmetric.

Organizing the model files

The five ReqIF_Import_*.sysml files written by import are a starting point, not a fixed layout. export and the import delta path key off the @reqif metadata, not the file or package the element happens to live in, so any def-shaped element (DatatypeDefinitionEnumeration, SpecificationType, SpecObjectType, SpecRelationType, RelationGroupType, Specification, SpecObject, and RelationGroup itself) can be moved into a different file or SysML v2 package once the private import lines are fixed up so cross-references still resolve. A subsequent syside reqif export writes out exactly the same ReqIF content as before the move, and a subsequent delta syside reqif import recognizes the moved elements as already-present and reports No changes detected.

The exception is usage-shaped elements that depend on their enclosing context:

  • A SpecRelation (a connection) must stay inside the occurrence def for its RelationGroup. The export resolves a relation’s source and target endpoints through the source_specification / target_specification features of the enclosing group, and the precondition that “every ReqIF tag must be an owned feature of a ReqIF-annotated type” rejects a relation hoisted into a top-level package. Move the whole RelationGroup occurrence def instead; the contained connections travel with it.

  • A nested SpecHierarchy (a requirement usage indexed as '1', '2', …) is owned by the parent SpecObject def and moves with that def; it cannot be hoisted on its own.

A practical layout is to break the imported model up by subsystem or document (for example, one file per Specification plus a shared types file) instead of keeping the four flat ReqIF_Import_Objects / …Specifications / …Relations / …Types files generated by import. Subdirectories are fine: the workspace is scanned recursively, so files under specs/, types/, etc. are picked up just like top-level ones.

One thing does not travel with a moved element: links to extracted attachments are spelled relative to the .sysml file that holds them. After moving text that references an attachment into a file at a different directory depth, correct the ../ prefix of each such link, or re-run syside reqif import to have the whole body regenerated. An uncorrected link resolves to nothing on hover and in rendered Markdown, and one that no longer resolves at all is no longer recognized as an attachment reference, so the attachment silently drops out of the next export. See Known limitations.

Locking newly-authored elements

Run:

syside reqif lock

lock walks every @reqif_*-tagged element in the workspace, fills in a fresh UUID identifier and a last_change timestamp on any element that does not already have them, and leaves elements that were imported (or locked previously) alone. Locking is idempotent: re-running it on an already-locked workspace is a no-op.

Without this step, freshly-authored ReqIF elements would be exported with regenerated identifiers each time, defeating the round-trip identity promise. There is no need to remember to run lock, though: syside reqif export re-runs the preconditions before writing and fails with a clear message if any element is unlocked, and lock is also safe to run as a pre-commit hook.

Note

syside reqif lock does not track when the element was last modified, only gives the initial date. To track which element was modified precisely when, set up a review process where every modified requirement also has its last_change updated before a Git (or equivalent) branch can be merged.

Validating requirements

The SysML v2 checks that export runs automatically can also be invoked on their own, to confirm the workspace is export-ready without producing an output file:

syside reqif check sysml

Its inbound counterpart, syside reqif check reqif path/to/incoming.reqif, screens a .reqif from a new partner before an import. The full list of checks is documented in Preconditions.

Tip

Wire syside reqif check sysml into the CI/CD pipeline as a gating step. It exits non-zero on any precondition failure, so a green build means the workspace is ready for an export or a delta import at any time, with no last-minute surprises about an unlocked element or a stray top-level package when a partner actually asks for the next round-trip.

SysML v2 to ReqIF

This step covers the ReqIF specification’s UC1: Export Requirement Specifications. Run:

syside reqif export --title "Brake System Requirements, rev. 3" outgoing.reqif

The --title flag is required and ends up in the exchange document’s REQ-IF-HEADER as the human-readable name partner A will see. Useful additional flags:

  • --comment "<text>": free-form description that goes into the header alongside the title.

  • --repository-id <id>: identifier (UUID, URL, …) of the repository this export was produced from. Recommended for any round-trip workflow so the receiver can correlate exports. The Git remote URL of the repository where the SysML v2 model lives is usually the best choice: it identifies the workspace unambiguously and gives a recipient looking at a stale .reqif somewhere to look it up.

  • --reqifz: emit a zipped archive that bundles the XML and the attachments_reqif/ files the document references into a single file. Use this whenever the document references binary attachments. The flag controls the format of the bytes written; the output filename is whatever is passed as the positional file argument, unchanged. The extension must agree with the flag: --reqifz outgoing.reqif (or outgoing.reqifz without --reqifz) is rejected before any model work runs, so pick the matching .reqifz / .reqif name to start with.

  • --attachments-dir <dir>: read attachments from a directory other than attachments_reqif/. As on import, it must be a subdirectory inside the workspace.

  • --source <name>: the exchange to export, when the workspace serves more than one partner. See Serving several exchange partners .

A .reqifz carries only the files the exported document actually references from an XHTML attribute value. Anything else sitting under the attachments directory stays out: stale files from an earlier import, scratch notes, and, in a workspace serving several partners, the attachments belonging to the other exchanges. A reference with no file behind it is reported as a warning naming the missing paths, and the export still succeeds.

Changed in version 0.11.0: export used to bundle every file under the attachments directory recursively, whether or not the document referenced it, which made curating that directory by hand the only thing standing between one partner’s files and another partner’s inbox.

Transmit the resulting file to partner A through any channel (email, shared drive, ticket attachment, …). The transmission itself is out of scope of the ReqIF specification and of this CLI.

Note

The body of an exported document (the CORE-CONTENT) is deterministic: running export twice against an unchanged workspace produces byte-identical CORE-CONTENT. The REQ-IF-HEADER, however, gets a fresh IDENTIFIER (a random UUID) and a fresh CREATION-TIME (the current wall-clock time) on every run. Committing exported .reqif files to version control alongside the SysML v2 sources produces a two-line diff in the header on every re-export even when nothing in the model has changed.

Serving several exchange partners New in v0.11.0

A workspace can take part in more than one exchange at a time. Every @reqif annotation carries a source naming the exchange it belongs to, and an element may carry one annotation per partner, each with its own identifier and optional long_name. The pair (source, identifier) is the ReqIF identity: it must select at most one element in the workspace, and one element may hold at most one identity of a given kind per source.

--source <name> selects the exchange for export, lock, and link scaffold. Its default is the unnamed exchange (""), which is what a single-partner workspace uses and what import writes, so nothing needs to change until a second partner appears.

The selection covers attachments too. Exporting one exchange bundles only the files that exchange’s own requirements reference, so a shared attachments_reqif/ can hold every partner’s images without one partner’s export carrying another’s.

Two paths lead into a multi-partner workspace:

  • syside reqif import generates a model from an incoming document, as described above.

  • syside reqif link maps a partner’s document onto elements you authored yourself, which is how one ontology comes to carry several partners’ identities. See Linking an existing ontology.

Progress reporting New in v0.10.1

Both syside reqif import and syside reqif export report progress as they work. When stdout is a TTY, a live rich progress bar is shown. When stdout is not a TTY (for example when redirected to a file or run in CI), progress is instead logged as periodic line-by-line updates, by default one every 1000 items:

syside reqif import --progress-interval 1000 path/to/incoming.reqif

--progress-interval N controls how many items pass between those non-TTY updates. Setting it to 0 disables progress output entirely. The flag has no effect on the live TTY bar, which is rendered regardless of its value.

Example model

A small ready-to-go model ships with these docs, a worked example that exercises every ReqIF concept: an enumeration datatype, all four type templates, five spec objects across two specifications with nested hierarchies, and a relation group of two refine relations.

Download the starter model (ZIP)

Drop it into an empty directory, then run syside reqif init, syside reqif lock, and syside reqif export --title "Brake System Requirements" out.reqif to produce a ready-to-send out.reqif.