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.
c5traceability.pyComputes 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:
c5traceability_config.yaml by default) that declares:
document_order — the list of document prefixes shown in the summary table.checks — a list of coverage checks, each specifying a subject document column and one or more linked document columns. An item is considered covered when at least one linked column contains a value in the same CSV row.defect_sources — documents whose .md files are scanned for ?c5-defect-X keywords (levels 0–4).--discover is passed, the script walks the specs directory looking for .doorstop.yml files to infer the document tree automatically, building a config without requiring a hand-written YAML file.MRS-ADBox) are excluded by default..md file in each defect-source document for ?c5-defect-0 … ?c5-defect-4 keywords. Items with level ≥ 3 are reported separately.rich formatting) and, when --html is 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.html
c5browser.pyGenerates 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:
.doorstop.yml file 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 static DOC_TYPES list if discovery finds nothing..md (Markdown-frontmatter format) and .yml (pure YAML format) file from each document directory. For .md files the YAML frontmatter is extracted with a lightweight parser; for .yml files pyyaml is used directly. The title is taken from the first H1 heading (Markdown) or the header / first text line (YAML). Parent UIDs are extracted from the links: field.(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.?c5-defect-X keywords found in item frontmatter fields are replaced with colour-coded Bootstrap badges.<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/
c5fingerprint.pyComputes and stores dependency content fingerprints in Doorstop items, enabling dependency-aware impact analysis when referenced source files change.
Role in the pipeline:
Each Doorstop item may carry a references: list pointing to source files (e.g. a TCS item referencing the implementation files it covers). c5fingerprint.py hashes the content of those files and stores the result as references_content_fingerprint in the item’s YAML frontmatter. When a referenced file changes, the stored fingerprint becomes stale, alerting reviewers that the item may need to be revisited. This creates a lightweight traceability link between specification items and their dependent source artifacts.
How it works:
.doorstop.yml files to identify all Doorstop documents and their item files (same discovery pattern as other SpecEngine scripts).references: frontmatter list and collects every entry that carries a path key. URL-only or path-less entries are skipped."missing" and excluded from the combined hash, so a missing file does not mask changes in files that are present."path:hash" pairs of all present files.references_content_fingerprint value. Items where any file hash has changed are flagged as [STALE].--dry-run or --check is set, stale items are updated in place with the new fingerprint.references_content_fingerprint: {} default is injected into the document’s .doorstop.yml attributes.defaults block (idempotent).Stored fingerprint format (written into each item’s YAML frontmatter):
references_content_fingerprint:
combined: 4a7b9c1d2e3f4a5b
files:
c5dec/core/cpssa/__init__.py: 9c0d1e2f3a4b5c6d
c5dec/core/cpssa/cpssa.py: 1a2b3c4d5e6f7a8b
Usage:
# Standard invocation (from docs/specs/ via publish.sh)
poetry run python ./SpecEngine/c5fingerprint.py
# Dry-run – compute only, no writes (exit 0)
poetry run python ./SpecEngine/c5fingerprint.py --dry-run
# Check mode – exit 1 if stale items found (CI gate)
poetry run python ./SpecEngine/c5fingerprint.py --check
# Verbose – show per-file hash details per item
poetry run python ./SpecEngine/c5fingerprint.py --verbose
# Non-default paths
poetry run python ./SpecEngine/c5fingerprint.py \
--specs-dir /path/to/docs/specs \
--repo-root /path/to/repo/root
Flags:
| Flag | Effect |
|---|---|
--dry-run |
Compute fingerprints but write nothing. Exits with 0. |
--check |
Implies --dry-run. Exits with 1 if any stale items are found. Suitable as a CI gate. |
--verbose |
Print per-item and per-file hash details alongside the [OK] / [STALE] status. |
--specs-dir PATH |
Override the specs root (default: parent of this script’s directory). |
--repo-root PATH |
Override the repository root used to resolve reference paths. |
c5graph.pyGenerates 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:
c5browser.py: reads .doorstop.yml files for document structure, then loads all active .md / .yml items. Items with active: false are excluded.links: field creates a directed edge from the child item to the parent. Node colour indicates coverage:
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/
c5publish.pyPublishes 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.publisher.publish(tree, path, ".html") to render every document in the tree to an HTML file in docs/publish/.--include-cc-db is not given (the default), all .doorstop.yml files under the CC database directory are temporarily renamed to disabled_doorstop.yml before building the Doorstop tree, so Common Criteria reference items are excluded from the published output. They are renamed back after publishing completes.index.html is patched to:
<head> block with local Bootstrap CSS references.table-striped table-condensed classes to all <table> elements.<body> with links to the browser, traceability statistics, and graph reports.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 skips 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/
c5-keyword.pyA 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 .md file in a given folder. For each file, every occurrence of a ?c5-defect-X keyword 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-X keywords 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> undo
doorstop_yml_to_md.pyA 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:
header field is extracted and written as a # Title H1 heading at the top of the Markdown body.text field is appended to the Markdown body.## field_name section.links, active, level, reviewed) are written into the YAML frontmatter block, sorted alphabetically..md file is written alongside the original .yml, which is then deleted..doorstop.yml configuration file is updated to set itemformat: 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/mrs
prune_bad_links.pyRemoves Doorstop links: entries that violate the Doorstop constraint that items may only link to items in their direct parent document.
How it works:
.doorstop.yml files. Each one is read to extract the document prefix and parent prefix, building a full map of the document tree..md and .yml) is read. The YAML frontmatter is parsed to extract the links: list.links: block, it is normalised to links: [].--dry-run reports 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/specs
c5traceability_config.yamlThe 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.
c5traceability_config_example.yamlA 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-only
All 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 |