Qualityfolio is a flexible test authoring framework powered by Spry's Axiom pattern. Author plain, human-friendly Markdown for tests that can be parsed into any structure later. Start simple, scale infinitely.
- Write Naturally - Use Markdown the way you already do
- Schema-Free - No upfront structure required. Apply schemas at query time
- Infinite Flexibility - Scale from 2 to 6 heading levels as your project grows
- Git-First - Version control your tests like code
- Auto-Discovery - Let the parser figure out your structure, or define it yourself
- Built-in Dashboard - Generate test management dashboards with metrics & traceability
Qualityfolio doesn't replace your tools - it upgrades them.
Qualityfolio is built to complement your existing test management, automation frameworks, and documentation systems. Write tests in Markdown, execute with your current tools, and capture evidence in structured SQL for audit-ready traceability.
Instead of:
- Tests scattered across multiple tools and formats
- Evidence lost in screenshots and spreadsheets
- Manual traceability matrices that break on every change
- No version history or accountability trail
You get:
- Human-readable Markdown - Version controlled, reviewable, and Git-native
- Structured SQL evidence - Queryable, immutable, and audit-ready
- Automated traceability - Requirements ↔ Tests ↔ Evidence ↔ Results
- Evidence-grade accountability - Complete history of who tested what, when, and why
Philosophy: Quality is only provable when declared intent (tests) aligns with runtime reality (evidence). Qualityfolio enforces this rigorously.
Simple rules:
- Use headings to suggest structure (none are required)
- Use GFM tasks (
- [ ],- [ ]) for steps and expectations - Add metadata with
@key valueannotations or YAML/JSON blocks
That's it. The parser handles the rest.
- Learn the patterns - See examples from simple to complex
- Start building - Generate your database & dashboard
- See examples - View real test artifacts
- Get help - Common issues & solutions
Explore a fully functional Qualityfolio dashboard with sample test artifacts, metrics, and traceability views.
Before you begin, ensure you have the following:
- Runtime: Deno 2.x or later (required)
- Package Manager: Homebrew (required)
- Tools: Spry, Surveilr (required)
Ensure the following tools and files are available on your system:
- Deno - runtime required by Spry
- Homebrew (brew) - package manager for installing dependencies
- Spry - runbook and SQLPage orchestration engine
- Surveilr - ingestion and transformation engine
- Qualityfolio Extension - Visual Studio Code Extension/Cursor IDE Extension which simplifies editing Evidence Block values
For Linux:
# 1. Install Deno
curl -fsSL https://deno.com/install.sh | sh
# 2. Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 3. Install Spry
brew install programmablemd/packages/spry
# 4. Install Surveilr
wget https://github.com/surveilr/packages/releases/download/3.23.0/surveilr_3.23.0_x86_64-unknown-linux-gnu.tar.gz
tar -xzf surveilr_3.23.0_x86_64-unknown-linux-gnu.tar.gz
sudo mv surveilr /usr/local/bin/
For macOS:
# 1. Install Deno
curl -fsSL https://deno.com/install.sh | sh
# 2. Install Homebrew
curl -fsSL -o install.sh https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh
# 3. Install Spry
brew install programmablemd/packages/spry
# 4. Install Surveilr
brew tap surveilr/tap && brew install surveilr
To install Qualityfolio extension, download and install the pre-packaged extension for Visual Studio Code or Cursor IDE.
Tip: The Qualityfolio extension simplifies editing Evidence Block values directly within your IDE.
Qualityfolio tests follow a structured Markdown hierarchy to ensure full traceability and machine-readable execution. Qualityfolio projects can range from simple to highly complex structures.
An example test artifact file (example-artifact.md) for the project OWASP - GLUE UP (using Qualityfolio's Small pattern) demonstrates key concepts:
- Header-based Classification - Structured metadata for test organization
- Hierarchical Test Design - Nested test suites and cases
- Evidence Block Patterns - Linking test results with execution evidence
View the example: example-artifact.md
- Create test artifact files and store them in a folder (for example,
test-artifacts). - Execute the following command in a terminal:
spry rb run qualityfolio.mdThis command ingests the test artifact files and generates the SQLite database resource-surveillance.sqlite.db, which can be queried using any SQL editor.
- After creating your test artifacts, execute the following commands:
spry rb run qualityfolio.md
spry sp spc --fs dev-src.auto --destroy-first --conf sqlpage/sqlpage.json --md qualityfolio.md
EOH_INSTANCE=1 SQLPAGE_SITE_PREFIX=/netspective-qualityfolio PORT=9227 surveilr web-ui -d ./resource-surveillance.sqlite.db --port 9227 --host 0.0.0.0This will launch the Test Management Dashboard with test metrics, requirement traceability matrix, and test cycle-wise execution views at:
http://localhost:9227/
Once your database and dashboard are running, you can:
- Query
resource-surveillance.sqlite.dbfor custom analytics - Extend SQLPage views by updating
qualityfolio.md - Add new test artifacts and execution evidence
- Integrate Qualityfolio into CI/CD pipelines
All documentation is in the docs/ folder:
| Topic | Description |
|---|---|
| Introduction | Introduction |
| Download Operational Truth™ | Installation |
| Quick Start Guide | Quick Start Guide |
Start simple. Scale infinitely. Teams naturally grow in complexity over time, and Spry's Axiom pattern adapts to your needs without forcing you to rewrite anything.
Key Insight: You write Markdown with headings at any depth (1-6 levels). The parser reads the structure, but role names (like "project", "suite", "case") are only applied at query time based on your chosen schema.
Choose the complexity level that matches your project's current needs:
| Scale | Heading Structure You Write | Query-Time Schema Mapping |
|---|---|---|
| Small Perfect for simple projects or quick test plans |
# Project ## Test Case ### Evidence |
3 Levels H1 → project or plan H2 → case H3 → evidence |
| Medium Growing teams with test suites |
# Project ## Suite ### Test Case #### Evidence |
4 Levels H1 → project H2 → suite H3 → case H4 → evidence |
| Large Enterprise projects with multiple plans |
# Project ## Plan ### Suite #### Test Case ##### Evidence |
5 Levels H1 → project H2 → plan H3 → suite H4 → case H5 → evidence |
| Complex Full organizational test strategies |
# Project ## Strategy ### Plan #### Suite ##### Test Case ###### Evidence |
6 Levels H1 → project H2 → strategy H3 → plan H4 → suite H5 → case H6 → evidence |
The Magic: Start with 3 headings today. Add more levels tomorrow. Your existing Markdown stays valid. No refactoring needed.
Pick one to start, mix and match as you grow. All patterns work seamlessly together.
---
doc-classify:
- select: heading[depth="1"]
role: project
- select: heading[depth="2"]
role: case
- select: heading[depth="3"]
role: evidence
---
# <Your Project or Test Plan Title>
@id <optional-stable-id>
Context One or two sentences that explain scope.
## Reset password works
@id <test-case-id>
```yaml HFM
doc-classify:
requirementID: <requirement-id>
Tags: [tag 1, tag 2]
```
Short narrative of the scenario.
Steps
- [ ] Open "Forgot Password"
- [ ] Submit email
- [ ] Receive reset email
- [ ] Set a new password
Expected
- [ ] Confirmation screen
- [ ] Login with new password succeeds
### Evidence
@id <add an id to refer this evidence>
```yaml HFM
doc-classify:
cycle: <test-cycle-number>
assignee: Sarah Johnson
env: qa
status: passed
```
- [Run log](./evidence/TC-LOGIN-0001/1.1/run.auto.md)
- [Response JSON](./evidence/TC-LOGIN-0001/1.1/result.auto.json)Parse-time: 3 headings. Query-time: map
{ heading[depth="1"]: "project", heading[depth="2"]: "case", heading[depth="3"]: "evidence" }.
---
doc-classify:
- select: heading[depth="1"]
role: project
- select: heading[depth="2"]
role: suite
- select: heading[depth="3"]
role: case
- select: heading[depth="4"]
role: evidence
---
# <Your Project or Test Plan Title>
@id <optional-stable-id>
Context One or two sentences that explain scope.
## Authentication Suite
@id <test-suite-id>
Context One or two sentences that explain the test suite.
### Valid login
@id <test-case-id>
Steps
- [ ] Enter valid credentials
- [ ] Submit
Expected
- [ ] Redirect to dashboard
#### Evidence
- Screenshot
- Test execution result
### Logout vallidation
@id <test-case-id>
Steps
- [ ] Click profile menu
- [ ] Click "Sign out"
Expected
- [ ] Return to sign-in
#### Evidence
- Screenshot
- Test execution resultParse-time: 4 headings. Query-time mapping:
{ heading[depth="1"]: "project", heading[depth="2"]: "suite", heading[depth="3"]: "case", heading[depth="4"]: "evidence" }or{ heading[depth="1"]: "plan", heading[depth="2"]: "suite", heading[depth="3"]: "case", heading[depth="4"]: "evidence" }- your choice.
---
doc-classify:
- select: heading[depth="1"]
role: project
- select: heading[depth="2"]
role: plan
- select: heading[depth="3"]
role: suite
- select: heading[depth="4"]
role: case
- select: heading[depth="5"]
role: evidence
---
# E2E Project Alpha
## Account Creation Plan
### Accounts & Auth Suite
@id acct-create-plan
```yaml
owner: riya@example.org
objective: Sign-up → login → profile bootstrap
```
#### New user can sign up and verify email
@id acct-signup-verify-case
Preconditions
- Mail sandbox configured in QA
Steps
- [ ] Open `/signup`
- [ ] Submit
- [ ] Receive verification email
- [ ] Click verification link
- [ ] Login
Expected
- [ ] User marked verified
- [ ] Login succeeds
##### Evidence
- [Run log](./evidence/TC-LOGIN-0001/1.1/run.auto.md)
- [Verification email JSON](./evidence/TC-LOGIN-0001/1.1/result.auto.json)Parse-time: 5 headings. Query-time mapping commonly used for this depth:
{ heading[depth="1"]: "project", heading[depth="2"]: "plan", heading[depth="3"]: "suite", heading[depth="4"]: "case", heading[depth="5"]: "evidence" }.
---
doc-classify:
- select: heading[depth="1"]
role: project
- select: heading[depth="2"]
role: strategy
- select: heading[depth="3"]
role: plan
- select: heading[depth="4"]
role: suite
- select: heading[depth="5"]
role: case
- select: heading[depth="6"]
role: evidence
---
# E2E Project Alpha
## Project Strategy
### Account Creation Plan
@id acct-create-plan
```yaml
owner: riya@example.org
objective: Sign-up → login → profile bootstrap
```
#### Accounts & Auth Suite
##### New user can sign up and verify email
@id acct-signup-verify-case
Preconditions
- Mail sandbox configured in QA
Steps
- [ ] Open `/signup`
- [ ] Submit
- [ ] Receive verification email
- [ ] Click verification link
- [ ] Login
Expected
- [ ] User marked verified
- [ ] Login succeeds
###### Evidence
- [Run log](./evidence/TC-LOGIN-0001/1.1/run.auto.md)
- [Verification email JSON](./evidence/TC-LOGIN-0001/1.1/result.auto.json)Parse-time: 6 headings. Query-time mapping commonly used for this depth:
{ heading[depth="1"]: "project", heading[depth="2"]: "strategy", heading[depth="3"]: "plan", heading[depth="4"]: "suite", heading[depth="5"]: "case", heading[depth="6"]: "evidence" }.
- Annotations: any line like
@key valuein a heading’s own section (before child headings). - Fenced code blocks: use
yaml,json, orjson5for structured metadata; captured with line numbers.
Examples:
@id acct-lockout-case @severity critical @component auth
```yaml
owner: riya@example.org
env: qa
objective: Lockout policy & reset email
```Annotations do not inherit to children - add where you want them to apply.
Use checkboxes to make steps and expected results machine-readable:
Steps
- [ ] Navigate to `/login`
- [ ] Enter valid credentials
- [ ] Provide MFA code
- [ ] Redirect to `/home`
Expected
- [ ] Session cookie set
- [ ] CSRF token present
- [ ] Home shows display nameSpry's Axiom pattern extracts each item with
checkedstate, the text, and precise line numbers.
If you like, top-of-file frontmatter is parsed:
---
doc-classify:
- select: heading[depth="1"]
role: project
- select: heading[depth="2"]
role: strategy
- select: heading[depth="3"]
role: plan
- select: heading[depth="4"]
role: suite
- select: heading[depth="5"]
role: case
- select: heading[depth="6"]
role: evidence
---Recommended conventions (not required - use what fits your team):
- Use lowercase with hyphens:
account-creation-plan.md,mobile-auth-login.case.md. - Keep evidence near the doc for easy links:
./evidence/.... - Typical repo layout (optional; use what fits your team):
qualifyfolio/
├── support/
│ └── assurance/
│ └── qualityfolio/
│ ├── evidence/
│ │ ├── TC-GLUE-001/
│ │ │ └── 1.1/
│ │ │ ├── result.auto.json
│ │ │ └── run.auto.md
│ │ └── TC-GLUE-002/
│ │ └── 1.1/
│ │ ├── loginButtonClick.png
│ │ ├── result.auto.json
│ │ └── run.auto.md
│ ├── sqlpage/
│ │ └── sqlpage.json # runtime configuration file for SQLPage
│ ├── test-artifacts/
│ │ └── example-artifact.md # example test artifact
│ ├── qualityfolio-json-etl.sql # SQL ETL script for Qualityfolio data
│ ├── qualityfolio.md # SQLPage Markdown page (DB config + queries)
│ └── resource-surveillance.sqlite.db # Database generated
Remember: the parser does not require any folder layout. This is just for DX.
See it in action: View example test artifacts
- Use whatever heading depth you need up to 6th level (none are required).
- Prefer GFM tasks for steps & expected results.
- Add
@id,@severity,@component, etc. where useful. - Use fenced YAML/JSON for richer metadata.
- Link evidence files close to the doc.
- Let schemas or discovery decide roles later.
- “My evidence isn’t detected” → an evidence must be a leaf heading (no deeper headings beneath it).
- “My annotations don’t show up” → ensure
@key valueis not inside a code block and is in the heading’s own section. - “Discovery chose odd roles” → either add minimal content to meaningful ancestors (so they’re “significant”) or apply an explicit schema when querying.
Your docs are yours. Spry's Axiom pattern is designed to read Markdown respectfully and safely.