A collection of Python utilities for managing, publishing, and analysing C5-DEC Doorstop specification trees. The scripts are designed to work together as a post-processing toolchain on top of the Doorstop requirements management tool.
Computes traceability coverage statistics from a Doorstop-generated traceability.csv file and renders the results to the console and/or to a self-contained Bootstrap HTML report (traceability_stats.html).
How it works:
- Config loading — reads a YAML config file (
c5traceability_config.yamlby default) that declares:document_order— the list of document prefixes shown in the summary table.checks— a list of coverage checks, each specifying asubjectdocument column and one or morelinkeddocument columns. An item is considered covered when at least one linked column contains a value in the same CSV row.defect_sources— documents whose.mdfiles are scanned for?c5-defect-Xkeywords (levels 0–4).
- Auto-discover mode — if
--discoveris passed, the script walks the specs directory looking for.doorstop.ymlfiles to infer the document tree automatically, building a config without requiring a hand-written YAML file. - CSV parsing — reads the Doorstop traceability CSV, grouping values by column. Named/group items (e.g.
MRS-ADBox) are excluded by default. - Coverage computation — for every check, each unique subject UID is looked up and all linked UIDs in the same rows are collected. Covered = at least one linked value found.
- Defect scanning — scans the body text (or a configured YAML frontmatter field) of each
.mdfile in each defect-source document for?c5-defect-0…?c5-defect-4keywords. Items with level ≥ 3 are reported separately. - Health score — aggregates all coverage percentages across all checks into a single overall score.
- Output — results are printed to the console (with optional
richformatting) and, when--htmlis given, written to an HTML file with a Bootstrap table layout, progress bars, and a navigation pill row.
Optional dependency: rich (for formatted console output); falls back to plain text if not installed.
Usage:
# Console output with default config
python c5traceability.py
# Console + HTML report
python c5traceability.py --html
# Custom config file
python c5traceability.py --config my_config.yaml
# Print auto-discovered config (does not run analysis)
python c5traceability.py --discover
# Write auto-discovered config to file, then run analysis
python c5traceability.py --discover --discover-write
# Custom CSV source and HTML output path
python c5traceability.py --csv path/to/traceability.csv --html --output report.htmlGenerates a standalone HTML specification browser (items_browser.html) containing one sortable, filterable table per Doorstop document type. The page uses Bootstrap 5 and DataTables and requires no server — it can be opened directly in a browser.
How it works:
- Document discovery — reads every
.doorstop.ymlfile found under the specs directory to determine document prefixes, subdirectories, and parent relationships. The list is topologically sorted (parents before children). Falls back to a staticDOC_TYPESlist if discovery finds nothing. - Item parsing — loads every
.md(Markdown-frontmatter format) and.yml(pure YAML format) file from each document directory. For.mdfiles the YAML frontmatter is extracted with a lightweight parser; for.ymlfilespyyamlis used directly. Thetitleis taken from the first H1 heading (Markdown) or theheader/ firsttextline (YAML). Parent UIDs are extracted from thelinks:field. - Column mapping — each document type has a predefined list of
(field_key, display_label, sort_type)columns. Numeric fields (urgency, importance, risk, etc.) are cast to integers for correct DataTables sorting. Unknown document types (discovered but not in the column map) receive a minimal default set of columns. - Defect badge rendering —
?c5-defect-Xkeywords found in item frontmatter fields are replaced with colour-coded Bootstrap badges. - HTML generation — a single, self-contained HTML string is assembled with all CDN links (Bootstrap, jQuery, DataTables), one
<section>per document type, and a sticky navigation bar to jump between them.
Usage:
# Default output (docs/publish/items_browser.html)
poetry run python c5browser.py
# Custom output path
poetry run python c5browser.py --output path/to/out.html
# Custom specs directory
poetry run python c5browser.py --specs-dir path/to/specs/Generates a self-contained, interactive HTML graph (specs-graph.html) visualising the Doorstop item dependency tree using Cytoscape.js with the Dagre hierarchical layout.
How it works:
- Item loading — same discovery and parsing pipeline as
c5browser.py: reads.doorstop.ymlfiles for document structure, then loads all active.md/.ymlitems. Items withactive: falseare excluded. - Graph data building — each item becomes a Cytoscape node. Each entry in an item's
links:field creates a directed edge from the child item to the parent. Node colour indicates coverage:- Green — item has at least one upward link (covered).
- Yellow — item has no upward links (root / uncovered).
- CDN inlining — the Cytoscape.js, Dagre, and cytoscape-dagre libraries are fetched at generation time from their CDN URLs and embedded directly in the HTML output so the file is fully self-contained. If a CDN fetch fails, the script falls back to empty strings (the graph will not render without the libraries).
- Initial view — on load only the top-level MRS nodes and their direct children are visible. Clicking a node expands its subtree; clicking again collapses it.
- Sidebar — a collapsible sidebar lists all nodes with a filter input. Clicking a node in the sidebar centres and highlights it in the graph.
Usage:
# Default output (docs/publish/specs-graph.html)
poetry run python c5graph.py
# Custom output path
poetry run python c5graph.py --output path/to/out.html
# Custom specs directory
poetry run python c5graph.py --specs-dir path/to/specs/Publishes the full Doorstop specification tree to HTML using the doorstop library, then post-processes the output to improve styling, linkify item IDs, and inject links to the SpecEngine tooling reports.
How it works:
- Doorstop publishing — calls
doorstop.publisher.publish(tree, path, ".html")to render every document in the tree to an HTML file indocs/publish/. - CC database exclusion — when
--include-cc-dbis not given (the default), all.doorstop.ymlfiles under the CC database directory are temporarily renamed todisabled_doorstop.ymlbefore building the Doorstop tree, so Common Criteria reference items are excluded from the published output. They are renamed back after publishing completes. - HTML post-processing — the generated
index.htmlis patched to:- Replace the default
<head>block with local Bootstrap CSS references. - Add
table-striped table-condensedclasses to all<table>elements. - Inject a navigation section at the top of
<body>with links to the browser, traceability statistics, and graph reports.
- Replace the default
- CSS patch — appends a responsive column-width fix to the Doorstop sidebar CSS.
- Linkification — scans every published HTML file for bare Doorstop item IDs (e.g.
SRS-001) and replaces them with relative hyperlinks (<a href="SRS.html#SRS-001">SRS-001</a>), skipping IDs already inside anchor tags. - Linkify-only mode —
--linkify-onlyskips publishing entirely and re-runs only the linkification pass, useful after the other SpecEngine tools have generated their report files.
Usage:
# Publish without CC database (default)
python c5publish.py
# Publish including CC database items
python c5publish.py --include-cc-db
# Re-linkify an already-published folder without re-publishing
python c5publish.py --linkify-only
# Re-linkify a non-default folder
python c5publish.py --linkify-only --publish-folder /path/to/publish/A two-way keyword substitution utility that replaces ?c5-defect-X shorthand keywords in Markdown files with coloured HTML <span> elements, or reverts them back.
How it works:
The script maintains a keyword_map dictionary that associates each keyword (?c5-defect-0 … ?c5-defect-4) with an HTML tag name, a CSS colour, and a human-readable label. The two operations are:
replace— iterates over every.mdfile in a given folder. For each file, every occurrence of a?c5-defect-Xkeyword is replaced with a<span style="color:COLOR">LABEL</span>element, making the defect level visible when the Markdown is rendered to HTML.undo— the reverse pass: scans for the generated<span>elements and substitutes the original?c5-defect-Xkeywords back in.
The colour coding is:
| Keyword | Colour | Label |
|---|---|---|
?c5-defect-0 |
green | 0 = flawless |
?c5-defect-1 |
SeaGreen | 1 = insignificant defect |
?c5-defect-2 |
orange | 2 = minor defect |
?c5-defect-3 |
DarkOrange | 3 = major defect |
?c5-defect-4 |
red | 4 = critical defect |
Usage:
# Replace keywords in all .md files in a folder
python c5-keyword.py <folder_path> replace
# Undo replacements in all .md files in a folder
python c5-keyword.py <folder_path> undoA one-time migration script that converts Doorstop item files from the legacy pure-YAML (.yml) format to the Markdown-with-YAML-frontmatter (.md) format used by the C5-DEC project.
How it works:
For each .yml item file found in the target folders:
- The file is loaded as a YAML mapping.
- The
headerfield is extracted and written as a# TitleH1 heading at the top of the Markdown body. - The
textfield is appended to the Markdown body. - Any other field whose value contains embedded newlines (i.e. multi-paragraph prose) is moved to the Markdown body as a
## field_namesection. - All remaining scalar and list fields (including structural fields like
links,active,level,reviewed) are written into the YAML frontmatter block, sorted alphabetically. - The resulting
.mdfile is written alongside the original.yml, which is then deleted. - The document's
.doorstop.ymlconfiguration file is updated to setitemformat: markdown.
Items that already have a corresponding .md file are skipped. Files that do not parse as YAML mappings are flagged as invalid.
--dry-run prints what would happen without writing any files.
Default target folders (relative to the repo root):
docs/specs/arc, docs/specs/mrs, docs/specs/srs, docs/specs/swd, docs/specs/tcs, docs/specs/trp
Usage:
# Convert all default folders
python doorstop_yml_to_md.py
# Preview without writing
python doorstop_yml_to_md.py --dry-run
# Convert specific folders only
python doorstop_yml_to_md.py docs/specs/srs docs/specs/mrsRemoves Doorstop links: entries that violate the Doorstop constraint that items may only link to items in their direct parent document.
How it works:
- Discovery — walks the specs directory for
.doorstop.ymlfiles. Each one is read to extract the documentprefixandparentprefix, building a full map of the document tree. - Item scanning — for each document, every item file (
.mdand.yml) is read. The YAML frontmatter is parsed to extract thelinks:list. - Bad link detection — a link is bad in three cases:
- The item is in a root document (no parent) and has any link at all.
- The link points to an item in the same document (self-link).
- The link target's prefix does not match the document's declared parent prefix.
- Removal — bad link lines are removed from the file's raw text using targeted regular expressions. If removing all links leaves an empty
links:block, it is normalised tolinks: []. - Dry-run mode —
--dry-runreports what would be changed without modifying any file.
Usage:
# Preview what would be removed (from the project root)
python docs/specs/SpecEngine/prune_bad_links.py --dry-run
# Apply removals
python docs/specs/SpecEngine/prune_bad_links.py
# Target a non-default specs directory
python docs/specs/SpecEngine/prune_bad_links.py --specs-dir /path/to/specsThe active configuration used by c5traceability.py for the current project. Contains the document_order list, the checks array defining which cross-document coverage relationships to evaluate, and the defect_sources list.
A heavily commented reference configuration showing all available options for c5traceability_config.yaml, including examples of multi-source checks, defect source configuration with frontmatter_field and guide_strip_heading, and inline documentation for every field.
| Path | Purpose |
|---|---|
assets/css/c5graph.css |
Stylesheet for the graph page generated by c5graph.py |
assets/js/c5graph.js |
JavaScript for the interactive graph generated by c5graph.py |
# 1. Publish the Doorstop specification tree to HTML
python docs/specs/SpecEngine/c5publish.py
# 2. Generate the interactive specification browser
poetry run python docs/specs/SpecEngine/c5browser.py
# 3. Generate the traceability statistics report
python docs/specs/SpecEngine/c5traceability.py --html
# 4. Generate the interactive traceability graph
poetry run python docs/specs/SpecEngine/c5graph.py
# 5. Re-linkify all HTML files now that the tooling reports exist
python docs/specs/SpecEngine/c5publish.py --linkify-onlyAll HTML outputs land in docs/publish/ and are linked from the index.html sidebar injected by c5publish.py.
| Package | Required by | Notes |
|---|---|---|
doorstop |
c5publish.py |
Core Doorstop library |
pyyaml |
all scripts | YAML parsing; most scripts include a minimal fallback parser |
rich |
c5traceability.py |
Optional; improves console output formatting |