Files
jellytau/docs/traceability-ci.md
T
dtourolle 0da0a9f16c fix(ci): derive traceability denominators from requirements.md (DR-093)
The coverage gate divided traced counts by hardcoded literals (UR/39,
IR/24, DR/48, JA/3, TOTAL_REQS=114) that had fallen out of date as
requirements grew to 211. It reported 158% coverage — JA alone printed
800% — so the 50% threshold was mathematically unreachable and the job
could not fail. Coverage could have collapsed to 30% and CI would still
have printed a green tick.

Real coverage is 86%. The number was fine; the gate was dead.

extract-traces.ts now owns both sides of the fraction:

- countDefinedRequirements() counts an ID only where it leads a markdown
  table row, ignoring the "Traces To" column and prose. IDs are
  deduplicated because requirements.md lists every UR twice (§1
  definition + §3 matrix), which would otherwise report UR as 121/61.
- computeCoverage() uses the intersection of traced and defined IDs, so
  a TRACES comment naming a deleted or typo'd requirement is reported as
  `orphaned` rather than inflating the ratio past 100%. UT/IT test
  identifiers are excluded as a separate taxonomy.
- CI reads .coverage.percent and fails on <50% or >100%; a >100% reading
  is now a hard error rather than the condition that hid this bug.
- New `bun run traces:coverage` runs the same computation locally.
- scripts/ added to the scan roots — the coverage tool was invisible to
  the matrix it generates.

Tests written first (15, over fixtures so they don't drift as
requirements are added). vitest include widened to scripts/** so build
tooling is covered by the normal suite.

Verified empirically rather than by inspection: forcing the threshold to
99% fails; adding a requirement lowers coverage 86%→85%; a TRACES: DR-999
lands in `orphaned` without changing `covered`.

traceability-ci.md documented the same stale numbers and would have let
the broken arithmetic be reconstructed — replaced with a pointer to the
live command.
2026-07-30 10:30:08 +02:00

8.6 KiB

Requirement Traceability CI/CD Pipeline

This document explains the automated requirement traceability validation system for JellyTau.

Overview

The CI/CD pipeline automatically validates that code changes are properly traced to requirements. This ensures:

  • Requirements are implemented with clear traceability
  • No requirement coverage regressions
  • Code changes are linked to specific requirements
  • Quality metrics are tracked over time

Gitea Actions Workflows

Traceability validation lives in .gitea/workflows/traceability-check.yml:

  • Automatic trace extraction
  • Coverage validation against minimum threshold (50%)
  • Modified file checking
  • Artifact preservation
  • Summary reports

Runs on: Every push and pull request to master/main/develop

A second workflow, traceability.yml, previously duplicated this one as a "GitHub-compatible alternative". It was removed: CI here is Gitea Actions, and its only unique step (PR comments via actions/github-script) depended on the GitHub REST client, which Gitea does not provide. To add PR comments, post to Gitea's /api/v1/repos/{owner}/{repo}/issues/{index}/comments from traceability-check.yml rather than reviving the old file.

What Gets Validated

1. Trace Extraction

bun run traces:json > traces-report.json

Extracts all TRACES comments from:

  • TypeScript files (src/**/*.ts)
  • Svelte components (src/**/*.svelte)
  • Rust code (src-tauri/src/**/*.rs)
  • Test files

2. Coverage Thresholds

The workflow checks:

  • Minimum overall coverage: 50%

Denominators are derived from docs/requirements.md at run time — they are never hardcoded here or in the workflow. Run bun run traces:coverage for the current per-type breakdown; any number written into this document is a snapshot that will drift.

Why this matters. The workflow used to divide by frozen literals (UR/39, IR/24, DR/48, JA/3, total 114) while requirements.md had grown past 200. It reported 158% coverage, so the 50% threshold was unreachable and the job could not fail regardless of how far coverage dropped. See specs/traceability-gate-repair.md.

Coverage is the intersection of traced and defined IDs: an ID that appears in a TRACES: comment but is not defined in requirements.md is reported as orphaned and does not count toward coverage. UT/IT test identifiers are a separate taxonomy and are excluded entirely.

The workflow fails and blocks merge if coverage drops below 50% — or if it computes above 100%, which can only mean the gate is miscounting.

3. Modified File Checking

On pull requests, the workflow:

  1. Detects all changed TypeScript/Svelte/Rust files
  2. Warns if new/modified files lack TRACES comments
  3. Suggests the TRACES format for missing comments

How to Add Traces to New Code

When you add new code or modify existing code, include TRACES comments:

TypeScript/Svelte Example

// TRACES: UR-005, UR-026 | DR-029
export function handlePlayback() {
  // Implementation...
}

Rust Example

/// TRACES: UR-005 | DR-001
pub fn player_state_changed(state: PlayerState) {
    // Implementation...
}

Test Example

// TRACES: UR-005 | DR-001 | UT-026, UT-027
#[cfg(test)]
mod tests {
    // Tests...
}

TRACES Format

TRACES: [UR-###, ...] | [IR-###, ...] | [DR-###, ...] | [JA-###, ...]
  • UR-### - User Requirements (features users see)
  • IR-### - Integration Requirements (API/platform integration)
  • DR-### - Development Requirements (internal architecture)
  • JA-### - Jellyfin API Requirements (Jellyfin API usage)

Examples:

  • // TRACES: UR-005 - Single requirement
  • // TRACES: UR-005, UR-026 - Multiple of same type
  • // TRACES: UR-005 | DR-029 - Multiple types
  • // TRACES: UR-005, UR-026 | DR-001, DR-029 | UT-001 - Complex

Workflow Behavior

On Push to Main Branch

  1. Extracts all traces from code
  2. Validates coverage is >= 50%
  3. Generates full traceability report
  4. Saves report as artifact

On Pull Request

  1. Extracts all traces
  2. Validates coverage >= 50%
  3. Checks modified files for TRACES
  4. Warns if new code lacks TRACES
  5. Suggests proper format
  6. Generates report artifact

Failure Scenarios

The workflow fails (blocks merge) if:

  • Coverage drops below 50%
  • JSON extraction fails
  • Invalid trace format

The workflow warns (but doesn't block) if:

  • New files lack TRACES comments
  • Coverage drops (but still above threshold)

Viewing Reports

In Gitea Actions UI

  1. Go to Actions tab
  2. Click the Traceability Validation workflow run
  3. Download traceability-reports artifact
  4. View:
    • traces-report.json - Raw trace data
    • docs/traceability.md - Formatted report

Locally

# Extract current traces
bun run traces:json | jq '.byType'

# Generate full report
bun run traces:markdown
cat docs/traceability.md

Coverage Goals

Current Status

Run bun run traces:coverage — it prints the live figure and exits non-zero below threshold. Numbers are deliberately not pinned here; the previous snapshot in this section (51%, 56/114) was stale by roughly 100 requirements and was what made the broken CI arithmetic look plausible for so long.

As of July 2026 overall coverage is ~86% (182/212).

Targets

  • Short term (Sprint): Maintain ≥50% overall
  • Medium term (Month): Reach 70% overall coverage
  • Long term (Release): Reach 90% coverage with focus on:
    • IR requirements (API clients)
    • JA requirements (Jellyfin API endpoints)
    • Remaining UR/DR requirements

Improving Coverage

For Missing User Requirements (UR)

  1. Review README.md for unimplemented features
  2. Add TRACES to code that implements them
  3. Focus on high-priority features (High/Medium priority)

For Missing Integration Requirements (IR)

  1. Add TRACES to Jellyfin API client methods
  2. Add TRACES to platform-specific backends (Android/Linux)
  3. Link to corresponding Jellyfin API endpoints

For Missing Development Requirements (DR)

  1. Add TRACES to UI components in src/lib/components/
  2. Add TRACES to composables in src/lib/composables/
  3. Add TRACES to player backend in src-tauri/src/player/

For Jellyfin API Requirements (JA)

  1. Add TRACES to Jellyfin API wrapper methods
  2. Document which endpoints map to which requirements
  3. Link to Jellyfin API documentation

Example PR Checklist

When submitting a pull request:

  • All new code has TRACES comments linking to requirements
  • TRACES format is correct: // TRACES: UR-001 | DR-002
  • Workflow passes (coverage ≥ 50%)
  • No coverage regressions
  • Artifact traceability report was generated

Troubleshooting

"Coverage below minimum threshold"

Problem: Workflow fails with coverage < 50%

Solution:

  1. Run bun run traces:json locally
  2. Check which requirements are traced
  3. Add TRACES to untraced code sections
  4. Re-run extraction to verify

"New files without TRACES"

Problem: Workflow warns about new files lacking TRACES

Solution:

  1. Add TRACES comments to all new code
  2. Format: // TRACES: UR-001 | DR-002
  3. Map code to specific requirements from README.md
  4. Re-push

"Invalid JSON format"

Problem: Trace extraction produces invalid JSON

Solution:

  1. Check for malformed TRACES comments
  2. Run locally: bun run traces:json
  3. Look for parsing errors
  4. Fix and retry

Integration with Development

Before Committing

# Check your traces
bun run traces:json | jq '.byType'

# Regenerate report
bun run traces:markdown

# Verify traces syntax
grep "TRACES:" src/**/*.ts src/**/*.rs

In Your IDE

Add a file watcher to regenerate traces on save:

{
  "fileWatcher.watchPatterns": [
    "src/**/*.ts",
    "src/**/*.svelte",
    "src-tauri/src/**/*.rs"
  ],
  "fileWatcher.command": "bun run traces:markdown"
}

Git Hooks

Add a pre-push hook to validate traces:

#!/bin/bash
# .git/hooks/pre-push
bun run traces:json > /dev/null
if [ $? -ne 0 ]; then
  echo "❌ Invalid TRACES format"
  exit 1
fi

References

Support

For issues or questions:

  1. Check this document
  2. Review example traces in src/lib/stores/
  3. Check existing TRACES comments for format
  4. Review workflow logs in Gitea Actions

Last Updated: 2026-02-13