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.
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.mdhad 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:
- Detects all changed TypeScript/Svelte/Rust files
- Warns if new/modified files lack TRACES comments
- 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
- ✅ Extracts all traces from code
- ✅ Validates coverage is >= 50%
- ✅ Generates full traceability report
- ✅ Saves report as artifact
On Pull Request
- ✅ Extracts all traces
- ✅ Validates coverage >= 50%
- ✅ Checks modified files for TRACES
- ✅ Warns if new code lacks TRACES
- ✅ Suggests proper format
- ✅ 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
- Go to Actions tab
- Click the Traceability Validation workflow run
- Download traceability-reports artifact
- View:
traces-report.json- Raw trace datadocs/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)
- Review README.md for unimplemented features
- Add TRACES to code that implements them
- Focus on high-priority features (High/Medium priority)
For Missing Integration Requirements (IR)
- Add TRACES to Jellyfin API client methods
- Add TRACES to platform-specific backends (Android/Linux)
- Link to corresponding Jellyfin API endpoints
For Missing Development Requirements (DR)
- Add TRACES to UI components in
src/lib/components/ - Add TRACES to composables in
src/lib/composables/ - Add TRACES to player backend in
src-tauri/src/player/
For Jellyfin API Requirements (JA)
- Add TRACES to Jellyfin API wrapper methods
- Document which endpoints map to which requirements
- 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:
- Run
bun run traces:jsonlocally - Check which requirements are traced
- Add TRACES to untraced code sections
- Re-run extraction to verify
"New files without TRACES"
Problem: Workflow warns about new files lacking TRACES
Solution:
- Add TRACES comments to all new code
- Format:
// TRACES: UR-001 | DR-002 - Map code to specific requirements from README.md
- Re-push
"Invalid JSON format"
Problem: Trace extraction produces invalid JSON
Solution:
- Check for malformed TRACES comments
- Run locally:
bun run traces:json - Look for parsing errors
- 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:
- Check this document
- Review example traces in
src/lib/stores/ - Check existing TRACES comments for format
- Review workflow logs in Gitea Actions
Last Updated: 2026-02-13