Basic usage

This little program renders Goal Structuring Notation in a YAML format to a scalable vector graphics (SVG) image.

example

Usage

You can create an SVG like this:

gsn2x <yourgsnfile.yaml> 

The output is an argument view in SVG format and automatically written to <yourgsnfile.svg>. If more than one input file is provided, they are treated as modules.

Options

Usage: gsn2x [OPTIONS] <INPUT>...

Arguments:
  <INPUT>...  Sets the input file(s) to use.

Options:
  -h, --help     Print help
  -V, --version  Print version

CHECKS:
  -c, --check                      Only check the input file(s), but do not output graphs.
  -x, --exclude <EXCLUDED_MODULE>  Exclude this module from reference checks.

OUTPUT:
  -N, --no-arg                         Do not output of argument view for provided input files.
  -f, --full <COMPLETE_VIEW>           Output the complete view to file with name <COMPLETE_VIEW>.    [default: complete.svg]
  -F, --no-full                        Do not output the complete view.
  -a, --arch <ARCHITECTURE_VIEW>       Output the architecture view to file with name <ARCHITECTURE_VIEW>. [default: architecture.svg]
  -A, --no-arch                        Do not output the architecture view.
  -e, --evidence <EVIDENCE>          Output list of all evidence to file with name <EVIDENCE>. [default: evidence.md]
  -E, --no-evidence                   Do not output list of all evidence.
  -o, --output-dir <OUTPUT_DIRECTORY>  Emit all output files to directory <OUTPUT_DIRECTORY>.      [default: .]

OUTPUT MODIFICATION:
  -l, --layer <LAYERS>            Output additional layer. Can be used multiple times.
  -s, --stylesheet <STYLESHEETS>  Links a stylesheet in SVG output. Can be used multiple times.
  -t, --embed-css                 Embed stylesheets instead of linking them.
  -m, --mask <MASKED_MODULE>      Do not show this module in views.
  -G, --no-legend                 Do not output a legend based on module information.
  -g, --full-legend               Output a legend based on all module information.
  -w, --wrap <CHAR_WRAP>          Define the number of characters after which a line of text is wrapped.

Syntax in YAML

Elements

The following Goal Structuring Notation (GSN) core elements are supported:

Element TypePrefix
GoalG
AssumptionA
JustificationJ
SolutionSn
ContextC
StrategyS

Every element is defined by a prefix (as shown in the table above) and an arbitrary identifier then.

Examples

G1:

G-TopLevelGoal:

C_A_certain_context:

Attributes

The only mandatory attribute is text that is the textual contents of the element.

An optional supportedBy gives a list of the supporting arguments. Thus, Goal, Strategy and Solution can be listed here.

An optional inContextOf links Justifications, Contexts or Assumptions.

Every element may have an optional url attribute that creates a navigation link in the resulting SVG. This should support finding information more easily.

Goals and Strategies can be undeveloped i.e., without supporting Goals, Strategies or Solutions. These elements should marked with undeveloped: true, otherwise validation will emit warnings.

Example

G1:
  text: This is a Goal
  supportedBy: [S1]
  inContextOf: [C1]

S1:
  text: This is a Strategy

C1: 
  text: This is a Context

Please see examples/example.gsn.yaml for an example of the used syntax.

Summary

AttributeOptionalNotes
textno
supportedByyes
inContextOfyes
undevelopedyesMutually exclusive to supportedBy.
urlyes
classesyesSee Stylesheets.
nodeTypeyesSee footnote1
rankIncrementyesSee Layout.
horizontalIndexyesSee Layout.
charWrapyesSee Line Breaks.
acpyesSee Confidence Argument Extension.
1

When providing a nodeType you do not need to follow the standard prefix scheme above. Just give Goal, Assumption, Justification, Solution, Context and Strategy to give the type of the element.

Checks

Validation checks

The tool automatically performs the following validation checks on the input YAML.

Validations can be performed on individual input files.

IDMeaning
V01All IDs must either start with a known prefix or a node_type must explicitly set.
V02All Goals and Strategies must be either marked with undeveloped: true or have supporting Goals, Strategies or Solutions.
V03Goals and Strategies marked as undeveloped, must have no supporting arguments.
V04All elements listed under supportedBy and inContextOf must be known elements types and semantically sensible (e.g. a Justification cannot be listed under supportedBy).
V05All referenced elements in supportedBy and inContextOf must be unique i.e., no duplicates in the list.
V06All referenced elements in supportedBy and inContextOf must not refer to the element itself.
V07All elements listed as extending other elements must be known elements of the current module and semantically sensible (see V04).
V08 The IDs start contradicts the type of the element set with node_type.
V09Element has an assurance claim point that references another element, that this is neither its own ID nor any of the connected elements.

The following checks apply to the complete set of input files.

IDMeaning
C01There should be only one but must be at least one top-level element (G,S,C,J,A,Sn) unreferenced.
C02The top-level element must be a Goal. A top-level element is an element that is not referenced by any other element.
C03All referenced elements in supportedBy and inContextOf must exist.
C04There must be no circular supportedBy references.
C06All module names must be unique.
C07All IDs must be unique across all modules.
C08All elements must be reachable from the root elements. This message can e.g. happen if there are multiple independent graphs where one contains circular references only.
C09All extended modules must exist.
C10All extended elements must exist in the named module and must be undeveloped.

Uniqueness of keys (i.e. element IDs) is automatically enforced by the YAML format.

If called with option -c or --check the input file is only checked for validity, but the resulting graph is not written. The checks for references (Cxx) can be skipped for individual files by using the -x option.

Format of messages

Error messages and warnings are printed to stderr.

The following format is used:

(Warning|Error): \((?<module>.+)\) \((?<num>[CV][0-9][0-9])\): (?<msg>.+) 

Advance Use-cases

There are a few extra, advanced use-cases:

Formatting

gsn2x allows for different ways to formatting text. Formatting loosely follows Markdown syntax, even though not the full specification is supported.

Text emphasis

You can use * and _ to emphasize text.

Text enclosed in *'s will be assigned a CSS class "bold".

Text enclosed in _'s will be assigned a CSS class "italic".

A default inline style is also assigned.

Example

This text:

This is a *bold* text. This is an _italic_ text.

will be rendered to:

This is aboldtext. This is anitalictext.

You can add hyperlinks for text: attributes as well as all additional layers. Hyperlinks are automatically detected when they start with "http://", "https://", "file://".

If you like to hide the actual URL, you can assign a text to the link that is rendered instead. The syntax for this follows Markdown syntax. However, a title is not supported, only text and href are. URLs may not contain whitespace characters. If you URL has one, just replace with %20.

Please note that the link created by url: cannot have additional text, since it is anyway invisible and applicable to the complete node.

Example

This link:

[Link Text](https://github.com/jonasthewolf/gsn2x)

will be rendered to:

Link Text

This link:

https://github.com/jonasthewolf/gsn2x

will be rendered to:

https://github.com/jonasthewolf/gsn2x

Text layout within elements

You can control line breaks by YAML means, e.g. following this example:

G1:
  text: |
    This
    is
    shown
    on
    separate
    lines

Alternatively, you can use the -w option and provide a global number of characters after which lines are wrapped.

You can also use the optional charWrap attribute for an element to individually define the number of characters after which line is wrapped. The same attribute can be applied for a complete module at the module section.

Please note that wrapping is done if a whitespace is detected after the given number of characters.

Layout of elements

How does the default layout of the graph work?

At first, the root elements are identified. Root elements are elements that are not referenced as supportedBy or inContextOf.

Each level in the rendered graph is called rank. The elements on each rank are sorted lexicographically before being placed on that rank. Then, starting with the first element on the current rank, the elements of the next rank are identified. An element is only ranked if all elements referencing it are already placed. Finally, the inContextOf elements are placed on that rank.

Placement of elements

Vertical placement

To influence the vertical placement i.e., the rank, of an element in the rendered graph, you can use rankIncrement for a node. A rank increment is a positive number to push an element this amount of ranks downwards. It is not possible to decrease the rank that would be assigned by the above algorithm.

Incrementing the rank is especially useful, if e.g., two goals or strategies are on the same logical level, but have a different "depth" in the argumentation (i.e. a different number of goals or strategies in their path to the root goal).

See the example for usage. The strategy S2 has an incremented rank.

Horizontal placement

The order of the GSN elements on the same rank is in the first place defined by their ID. The elements are sorted lexicographically. Thus, a goal G1 if placed on the same rank is placed left to G2.

You can use horizontalIndex to reorder elements after lexicographical sorting. The index can be modified by giving a relative or absolute index.

In the following example, G1 and G2 are placed on the same rank. However, G2 is placed left of G1 because of the relative horizontal index.

G1:
    text: Goal 1
    undeveloped: true

G2: 
    text: Goal 2
    undeveloped: true
    horizontalIndex:
         relative: -1

Please see Sn2 and Sn4 in example for how to use the absolute index.

Typical use-cases for defining an absolute index are putting elements at the beginning or the end within a rank. Please see the following example for how to use an absolute index.

G1:
    text: Goal 1
    undeveloped: true
    horizontalIndex:
        absolute: last

G2: 
    text: Goal 2
    undeveloped: true
    horizontalIndex:
        absolute: 0

Please note that giving the horizontal index for G1 and G2 is redundant.

The absolute index is zero-based. You can use last to move elements to the very right of the graph.

The horizontal index can also be applied to inContextOf elements. You would typically use an absolute index with either 0 or last to place them either left or right of the element they are referenced from.

horizontalIndex and rankIncrement can also be used for module elements. They will be used for the Architecture View then (see Modular extension).

Troubleshooting

There can be situations (e.g. a n:m relation between goals and solutions) that lead to weird looking graphs. You may even encounter the following message Diagram took too many iterations ({run}). See documentation (https://jonasthewolf.github.io/gsn2x/) for hints how to solve this situation. In such cases, please use the mechanisms described above to support the algorithm in ranking the elements more sensibly.

Moreover, gsn2x also outputs a hint on the list of elements that might cause the problem.

If you have trouble doing so, please feel free to create an issue or start a discussion on the GitHub site. The issue template on GitHub shows you how to remove intellectual property from the files that I would ask for then.

Example

The following small example will yield the above mentioned message:

G1:
  text: First
  supportedBy: [Sn2]

G2:
  text: Second
  supportedBy: [Sn1]

Sn1: 
  text: Solution to Second

Sn2: 
  text: Solution to First

The created SVG is hardly readable:

entangled example

The reason for this is, that G1 is lexicographically ordered before G2, but Sn2 after Sn1. Sn1 thus "pushes" Sn2 to the right in each iteration of the layout algorithm.

The problem is easily solvable in different ways:

  1. Rename G1 and/or G2 to change their lexicographical order.
  2. Rename Sn1 and/or Sn2 to change their lexicographical order.
  3. Apply horizontalIndex: absolute: last to G1 or Sn1.
  4. Apply horizontalIndex: absolute: 0 to G2 or Sn2.
  5. Apply horizontalIndex: relative: +1 to G1 or Sn1.
  6. Apply horizontalIndex: relative: -1 to G2 or Sn2.

The order above can be considered - as a general rule of thumb - as a suggested order when trying to resolve layout issues.

Additional layers

Additional attributes of an element are not output into the rendered diagram.

With the command line option -l or --layers you can enable the output of those additional attributes. By using this feature different views on the GSN can be generated.

Example

G1:
  text: This is a Goal
  supportedBy: [S1]
  inContextOf: [C1]
  layer1: This is additional information for G1.

S1:
  text: This is a Strategy
  layer1: This is additional information for S1.

C1: 
  text: This is a Context
  layer1: This is additional information for C1.

In this example, a call to gsn2x -l layer1 will show the additional information to each element prefixed with LAYER1: .

Of course, using text, inContextOf, supportedBy, url, undeveloped, horizontalRank, rankIncrement, acp or classes are not sensible parameters to pass for the -l option.

Please note that using module and passing it as a layer option will also not work.

It is intentional that information is only added for a view, but not hidden to ensure consistency of the GSN in all variants.

Only additional associative arrays with a string key can be used as additional layers.

Stylesheets for SVG rendering

You can provide (multiple) custom CSS stylesheets for SVG via the -s or --stylesheet options.

The path may be relative to the current working directory, absolute, or an URL (i.e. starting with http://, https:// or file://).

When adding -t or --embed-css on the command line, the CSS stylesheets will be embedded in the SVG.

If an output path is provided (see Basic usage), the stylesheet(s) will be copied there. If a relative path is used, the relative path to the current working directory is preserved. If an absolute path is used, the stylesheet will be copied to the root of the output path.

If a URL (see above for definition) is provided for a stylesheet, it is neither embedded nor copied to an output directory.

Classes and styles

Every element will also be addressable by id. The id is the same as the YAML id.

This table shows the CSS classes assigned to a certain element:

ClassAssigned toSVG Element
gsndiagramThe complete diagramsvg
gsnelemAll elementsg
gsngoalGoalg
gsn_undevelopedUndevelopedg
gsnsltnSolutiong
gsnawaysltnAway Solutiong
gsnstgyStrategyg
gsnasmpAssumptiong
gsnawayasmpAway Assumptiong
gsnjustJustificationg
gsnawayjustAway Justificationg
gsnctxtContextg
gsnawayctxtAway Contextg
gsnmoduleModuleg
gsn_module_moduleModule nameg
gsnedgeAll edgespath
gsnlay_<layer>Layer <layer>path
gsninctxtIn Context Ofpath
gsnspbySupported Bypath
gsncompositeComposite (In Context Of AND Supported By)path
gsn_maskedMasked elementsg  
acp_acp_nameElements or edges with an ACPg or path

You can assign additional classes by adding the classes: attribute. It must be a list of classes you want to assign. Additional layers will be added as CSS classes, too. A layer1 will e.g. be added as gsnlay_layer1.

For more information on how to use CSS with SVGs, see here.

Example

The GSN YAML:


G1:
  text: This is a Goal
  classes: [additionalclass1, additionalclass2]
  undeveloped: true

The corresponding CSS:


.additionalclass1 path { fill: red; fill-opacity: 1;}
.additionalclass1 text,a { fill: white; }

The result looks like this:

Styled Example

Highlighting elements when navigating

The CSS :target pseudo class can be used to highlight the element you clicked on in the previous image.

An example could look like this:

g:target path {
    fill: lightsteelblue;
    fill-opacity: 1;
}

List of evidence

An additional file that lists all the evidence in the input file is output by default in evidence.md.

See examples/evidence.md for an example.

The format can be used in Markdown and reStructuredText files.

If the list of evidence should not be output, use the -E option.

You can use the -e option to rename the standard evidence.md to another file name, but not subdirectory. The -o option also applies to the evidence file.

Markdown from examples/evidence.md

List of Evidence

  1. Sn1: Solution 1

    examples_example_gsn_yaml

    https://github.com/jonasthewolf/gsn2x

  2. Sn2: Solution 2

    examples_example_gsn_yaml

  3. Sn3: Solution 3

    examples_example_gsn_yaml

  4. Sn4: Solution 4

    examples_example_gsn_yaml

  5. Sn5: Solution 5

    examples_example_gsn_yaml

Modular extension

gsn2x partially supports the Modular Extension of the GSN standard (see Standard support).

Each module is a separate file. The name of the module is the file name (incl. the path provided to the gsn2x command line).

If modules are used, all related module files must be provided to the command line of gsn2x. Element IDs must be unique across all modules. Checks will by default be performed across all modules. Check messages for individual modules can be omitted using the -x option.

The argument view of individual modules will show "away" elements if elements from other modules are referenced. All elements are public, meaning they can be referenced from other modules.

Note: There is no "away strategy" in the standard.

In addition to the default argument view for each module, there are two output files generated (if more than one input file is provided):

  1. Complete View (default to: complete.svg)
  2. Architecture View (default to: architecture.svg)

You can only change the file names of these additional views. They are put in the directory that all input files have in common. The -o option can be used for these views, too.

If the argument view should not be updated, use the -N option. If the complete view should not be output, use the -F option. If the architecture view should not be output, use the -A option.

Complete view

The complete view is a similar to an argument view for a single module, but showing all modules within the same diagram. The modules are "unrolled".

example complete

See example here.

Architecture view

The architecture view only shows the selected modules and their dependencies. THe architecture view is navigable to the module argument view.

The architecture view only contains the links to the individual module files, if they actually exist when generating the architecture view.

example architecture

See example here.

Example:

gsn2x -f full.svg -a arch.svg main.yml sub1.yml sub3.yml

This will generate the argument view for each module, the complete view (-f full.svg) of all modules and the architecture view (-a arch.svg).

Developing undeveloped elements from other modules

In a customer supplier relationship it may be helpful to develop otherwise undeveloped elements from other modules. This allows creating distributed assurance cases.

Example for a module with undeveloped elements:

module:
  name: template 
  brief: Template for an assurance case

G1:
  text: A claim somebody else should support
  undeveloped: true

Example for developing those elements in another module:

module:
  name: instance
  brief: Extending instance
  extends: 
    - module: template
      develops:
        G1: [G2]

G2:
  text: This is the argument provided by somebody else.
  supportedBy: [Sn1]

Sn1:
  text: A solution

Optional module information

It is possible to add additional module information in the source YAML. This allows describing the module`s name and an optional brief description. Even arbitrary information can be added.

name and brief are mandatory if a module is added.


module: 
   name: MainModule
   brief: This is a short description of the module
   additionalInformation: 
    v1: Changed line 2
    v2: Added line 4

The module information is printed as part of a legend for the argument view.

To influence the position in the architecture view, you can use the horizontalIndex and rankIncrement as you would for elements in the Argument view (see Layout of elements ).

You can use the -G option to suppress the legend completely, or the -g option to limit it to name, brief and the time and date of generation of the SVG.

Including other modules

Multiple modules, i.e. files, can be provided at the command line:

gsn2x index.gsn.yaml required_module1.gsn.yaml required_module2.gsn.yaml required_module3.gsn.yaml

However, this might requires long and complex calls at the command line for reasonably large arguments.

Another way to use multiple modules is to "use" them from others. You can do so by adding a uses attribute to a module:

module:
   name: MainModule
   brief: This is the index.gsn.yaml file
   uses: [required_module1.yaml, required_module2.gsn.yaml, required_module3.gsn.yaml]

The command line is now simplified to:

gsn2x index.gsn.yaml

Or even (if you name the entry point file: index.gsn.yaml):

gsn2x

Of course, "used" modules can also include other modules on their own. There is a check implemented to prevent circular includes.

Confidence Argument Extension

The Confidence Argument Extension is supported. It defines so called Assurance Claim Points (ACPs).

They can be defined either for an element or for the relation between elements.

The following YAML shows an example of their usage:

G1:
  text: System is acceptably safe to operate
  supportedBy: [S1]
  acp:
    ACP1: S1

S1:
  text: Argument over all hazards
  supportedBy: [G2, G3]
  inContextOf: [C1]
  acp:
    ACP4: C1

C1:
  text: Hazard list
  acp:
    ACP6: C1

G2:
  text: Hazard 1 mitigated
  supportedBy: [Sn1]
  acp:
    ACP2: Sn1

Sn1:
  text: Evidence for Hazard 1

G3:
  text: Hazard 2 mitigated
  supportedBy: [Sn2a, Sn2b]
  acp:
    ACP3: [Sn2a, Sn2b]

Sn2a:
  text: Evidence for Hazard 2

Sn2b:
  text: More evidence for Hazard 2
  acp:
    ACP5: Sn2b

Assurance Claim Points (ACP) are defined with the acp attribute. The name of the ACP is followed by:

  • either a reference to the element itself, or
  • a reference or a list of references to elements that are directly related (supportedBy or inContextOf).

There can be multiple ACPs per element.

Rendered Example

Interfacing

Here is some guidance on interfacing with external sources.

I currently see two use-cases:

  1. Use-case: Check if all configuration management items of a project exist that are mandated by the argumentation (as solutions/evidence).

    The reference list may comprise more items that are required for argumentation, i.e. the list items must be attributed.

    The reference list may be distributed across multiple files.

  2. Use-case: Check if all normative/external requirements are fulfilled.

    The reference list is a list of all normative requirements.

    This use-case is actually the other way around than the first one.

For the example scripts below, yq is required.

Checking evidence

This command lists all Solutions that are in example.gsn.yaml but are not in reference.yaml:

yq ea '[select(file_index == 0)|.Sn*.text] - [select(file_index == 1)|.[]]' examples/example.gsn.yaml reference.yaml   

reference.yaml could look like this:

# Configuration Management Items
- Solution 1
- Solution 2

Checking references

Depending on how the external format is defined, you can swap the part before the -, with the part after.

Standard support

This tool is based on the Goal Structuring Notation Community Standard Version 3.

This table shows the support of gsn2x for the different parts of the standard.

StandardSupport
Core GSN✅ full
Argument Pattern Extension❌ not planned
Modular Extension🟡 partially, see Modular Extension; Module Interfaces (Section 1:4.6) and Inter-Module Contracts (Section 1:4.7) are not supported (this includes public decorators and module interface connectors).
Confidence Argument Extension✅ full, see Confidence Argument Extension
Dialectic Extension❌ not planned

Troubleshooting

YAML error messages

If you encounter an error message like this, please use a YAML editor or online validator to check for the well-formedness of the input file:

Error: Failed to parse YAML from file <filename>

Caused by:
    No valid GSN element can be found starting from line <n>.
    This typically means that the YAML is completely invalid, or 
    the `text:` attribute is missing for an element,
    or `supportedBy` or `inContextOf` are not followed by a list ([]).
    Please see the documentation for details (https://jonasthewolf.github.io/gsn2x/troubleshooting.html).
    Original error message: data did not match any variant of untagged enum GsnDocument.

Please see the YAML Syntax what is expected by the programs.

A good strategy to find out which element is causing the problem is to remove all but the first element from the input YAML file. Then running gsn2x again and incrementally adding the elements one by one again, until you hit the error message again.

Unfortunately, it is currently not possible to improve on the location of the error messages in this case.

Sanitizing files for e.g. support requests

You can sanitize your files from your intellectual property using, e.g. https://mikefarah.gitbook.io/yq/.

This statement replaces all text with xes while keeping the number of characters:

 yq "(.[] | select(. | has(\"text\"))) .text |=sub(\"[a-zA-Z0-9]\",\"x\"), ... comments=\"\""  inputfile.yaml > outputfile.yaml

Please note that element identifiers and additional layers are not sanitizied.

Design goals

I noticed that it might make sense to add some information about the goals I have set for myself for this project:

  • Everything-as-code

    The tool should work in a continuos integration environment. The input format should be diff-able to support common git workflows with pull/merge requests.

  • Simplicity

    I would like to keep things simple. Simple for me and others.

    That means the input format should be simple to learn and edit manually in any editor. I also did not want invent a new DSL (domain-specific language) for that purpose. YAML (input file format) might not be the best format, but it serves as a good tradeoff for my purposes. Moreover, it can be parsed by other programs easily, too.

  • Standard conformance

    I would like the program output to be very close to the GSN standard.

    I don't want to redefine the semantics or add additional ones. The standard was created so that as many people as possible have some common grounds. If I added new fancy stuff, everyone might have a different interpretation of that again.

  • As few dependencies as possible

    Since I understand that this tool might be used in some corporate environment where usage of free and open-source software might be limited, I try to keep the dependencies of this program as few as possible.

    I used a very relaxed license and try to take care that it is compatible with those of the dependencies.

  • Full control by the user how the diagram is layed out

    Creating an optimal rendering for all different use-cases is incredibly difficult. I failed in creating an algorithm that would allow for that. When trying I recognized that it is anyhow preferable to give the user full control over how the diagram should look like.

History

I also noticed that (also for myself) it is good to note down some history of the project:

  • It all started out in 2017 with the need for graphically representing some argumentation at work. I wrote a tiny Python script that used a jinja template to transform the YAML syntax into something Graphviz could understand.

    From there Graphviz could generate different output formats. That's where the x in gsn2x is from.

  • It got obvious that some validation, especially on the uniqueness and reference resolution is needed to handle larger argumentation.

    I did not want to write those validations in Python, but in my favorite programming language Rust. I released the first Rust version in July 2021.

  • I desperately tried adding the modular extension by convincing Graphviz to draw what I want, but I failed. I finally made decided to no longer output DOT, but directly generate SVGs from the program. This required writing a specialized version for rendering the tree on my own which ended up in version 2 finally released in April 2022.

  • I tried hard improving the layout algorithm over time but I failed to come up with a satisfying solution. Moreover, I recognized that it is anyway preferable if the user is in full control of the layout of the diagram. Thus, I decided to redesign the layout algorithm and create version 3. A new major version was needed because of different user input that is required now.

    In addition, I changed the license from CC-BY-4.0 to MIT. MIT is shorter and more applicable to software.

  • Own usage and feedback from various people led to improvements of the layout algorithm. And now also to the implementation of the Confidence Argument Extension of the GSN standard as part of version 3.1.

Any feedback, especially the use-case in your company is very much appreciated.

Migrating from Version 2.x to Version 3.x

Version 2 of gsn2x tried to fully automate the layout of diagrams. Version 3 intentionally changed this approach.

To get similar or even better renderings, please follow the guidelines below.

Replace level with rankIncrement

level was deprecated. Instead, increment the rank of the elements that should be pushed further down.

Please see Layout for more information on how to use rankIncrement.

Use strict lexicographical sorting and horizontal reordering to optimize the graph

If you find situations that are not rendered correctly by default, please check if your IDs are sensibly defined (i.e. lexicographically increasing). Use horizontalIndex as described here to fix e.g., crossing edges.