Path filters reference

Reference for the path-filter patterns that decide which Stella Ops CI workflows run for a given set of changed files. It is for CI authors who maintain .gitea/config/path-filters.yml and the workflows that consume it. Read the reality check below before relying on any per-module workflow name — much of the catalog is aspirational, and the active PR lane keys on change detection rather than these patterns.


Overview

Path filters determine which workflows run based on changed files. This ensures:


Configuration Location

Centralized path filter definitions are documented in:

.gitea/config/path-filters.yml

This file is the catalog of intended path filter patterns. Its own header states that each workflow should reference these patterns for consistency — it is a reference document, not a mechanism that workflows import at runtime.

Reality check (verified against .gitea/ on 2025-12-28 config / current workflows). The active .NET PR test lane does not use per-module workflow files keyed on these patterns. Instead, .gitea/workflows/dotnet-pr-tests.yml runs a detect job that calls .gitea/scripts/test/detect-affected-modules.sh, which diffs the PR against its base ref, maps changed files to top-level src/<Module> directories that contain *.Tests.csproj, and sets affects_all=true for shared/global surface (src/__Libraries/**, src/__Analyzers/**, src/__Tests/**, Directory.*.props, global.json, nuget.config, and the test scripts themselves). Broad coverage is the job of the nightly .NET Test Sweep (dotnet-nightly-sweep.yml). The per-module <module>-*.yml workflow names referenced throughout this file and in path-filters.yml are largely aspirational — of the names below, only airgap-*.yml resolves to an active workflow (airgap-federation-e2e.yml). Treat the per-module/categories sections as a roadmap, not the running CI.


Path Filter Categories

1. Infrastructure Files (Trigger FULL CI)

Changes to these files trigger all tests and full build validation:

infrastructure:
  - 'Directory.Build.props'      # Root MSBuild properties
  - 'Directory.Build.rsp'        # MSBuild response file
  - 'Directory.Packages.props'   # Central package versions
  - 'src/Directory.Build.props'  # Source directory properties
  - 'src/Directory.Packages.props'
  - 'nuget.config'               # NuGet feed configuration
  - 'StellaOps.sln'              # Aggregate solution (see note)

NOTE — StellaOps.sln does not resolve to a file. path-filters.yml lists StellaOps.sln, but there is no root (or src/) aggregate solution in the repo; only per-module solutions exist (src/<Module>/StellaOps.<Module>.sln, 30 of them). The change-detection script (detect-affected-modules.sh) does still match src/StellaOps.sln as a broad-change trigger defensively, but no such file is present today. CI build scripts deliberately avoid a monolithic dotnet build src/StellaOps.sln (it OOM-crashed the ~12 GB Gitea host on 2026-05-14) and build one project at a time instead.

The doc previously also listed src/**/StellaOps.*.sln and .gitea/workflows/** here; neither is in path-filters.yml’s infrastructure block. Workflow changes are tracked under the devops.workflows key (.gitea/workflows/**), shown in the DevOps section below.

When to use: All PR-gating and integration workflows should include these paths.

2. Documentation Paths (Skip CI)

These paths should use paths-ignore to skip builds:

docs_ignore:
  - 'docs/**'           # All documentation
  - '*.md'              # Root markdown files
  - '!CLAUDE.md'        # Exception: Agent instructions SHOULD trigger
  - '!AGENTS.md'        # Exception: Module guidance SHOULD trigger
  - 'etc/**'            # Configuration samples
  - 'LICENSE'           # License file
  - '.gitignore'        # Git ignore
  - '.editorconfig'     # Editor configuration

Exceptions: These markdown files SHOULD trigger CI (encoded as negated patterns above, matching path-filters.yml):

3. Shared Library Paths (Trigger Cascading)

Changes to shared libraries trigger tests in dependent modules:

Cryptography (CRITICAL - affects security)

cryptography:
  paths:
    - 'src/__Libraries/StellaOps.Cryptography*/**'
    - 'src/Cryptography/**'
  cascades_to:
    - Scanner tests
    - Attestor tests
    - Authority tests
    - EvidenceLocker tests
    - Signer tests
    - AirGap tests
    - Security test suite
    - Offline E2E tests

Evidence & Provenance

evidence:
  paths:
    - 'src/__Libraries/StellaOps.Evidence*/**'
    - 'src/__Libraries/StellaOps.Provenance/**'
  cascades_to:
    - Scanner tests
    - Attestor tests
    - EvidenceLocker tests
    - ExportCenter tests
    - SbomService tests

Infrastructure & Database

infrastructure:
  paths:
    - 'src/__Libraries/StellaOps.Infrastructure*/**'
    - 'src/__Libraries/StellaOps.DependencyInjection/**'
  cascades_to:
    - ALL integration tests

Replay & Determinism

replay:
  paths:
    - 'src/__Libraries/StellaOps.Replay*/**'
    - 'src/__Libraries/StellaOps.Testing.Determinism/**'
  cascades_to:
    - Scanner determinism tests
    - Determinism gate
    - Replay module tests

Verdict & Policy Primitives

verdict:
  paths:
    - 'src/__Libraries/StellaOps.Verdict/**'
    - 'src/__Libraries/StellaOps.DeltaVerdict/**'
  cascades_to:
    - Policy engine tests
    - RiskEngine tests
    - ReachGraph tests

Plugin Framework

plugin:
  paths:
    - 'src/__Libraries/StellaOps.Plugin/**'
  cascades_to:
    - Authority tests (plugin-based auth)
    - Scanner tests (analyzer plugins)
    - Concelier tests (connector plugins)

Configuration

configuration:
  paths:
    - 'src/__Libraries/StellaOps.Configuration/**'
  cascades_to:
    - ALL modules

src/__Libraries/StellaOps.Configuration/ is wired into nearly every service, so path-filters.yml cascades it to all_modules. (Note: the change-detection script in the active PR lane treats any src/__Libraries/** edit — Configuration included — as affects_all=true and defers to the nightly sweep rather than fanning out a job per module.)


Module-Specific Paths

Each module entry in path-filters.yml has source, tests, optional workflows, and optional dependencies keys. The Workflows column below reproduces the workflows: glob from the config — but, as noted at the top of this page, almost none of those workflow files exist yet (only airgap-*.yml does). Treat that column as the intended naming convention, not a list of live workflows. The active PR lane (dotnet-pr-tests.yml) keys solely on the top-level src/<Module>/** directory via change detection.

Core Platform

ModuleSource PathsTest PathsWorkflows (aspirational)
Authoritysrc/Authority/**src/Authority/__Tests/**authority-*.yml
Router (includes Gateway)src/Router/**src/Router/__Tests/**router-*.yml
IssuerDirectorysrc/Authority/StellaOps.IssuerDirectory/**, src/Authority/__Libraries/StellaOps.IssuerDirectory.*/**src/Authority/__Tests/StellaOps.IssuerDirectory.Persistence.Tests/**, src/Authority/StellaOps.IssuerDirectory/StellaOps.IssuerDirectory.Core.Tests/**

Scanning & Analysis

ModuleSource PathsTest PathsWorkflows (aspirational)
Scannersrc/Scanner/**, src/BinaryIndex/**src/Scanner/__Tests/**, src/BinaryIndex/__Tests/**scanner-*.yml, scanner-analyzers*.yml
BinaryIndex (Symbols merged in, Sprint 202)src/BinaryIndex/**src/BinaryIndex/__Tests/**
AdvisoryAI (advisory_ai in config)src/AdvisoryAI/**src/AdvisoryAI/__Tests/**advisory-*.yml
ReachGraphsrc/ReachGraph/**src/ReachGraph/__Tests/**reachability-*.yml
Graphsrc/Graph/**src/Graph/__Tests/**graph-*.yml

Data Ingestion

ModuleSource PathsTest PathsWorkflows (aspirational)
Concelier (Feedser + Excititor absorbed, Sprint 203)src/Concelier/**src/Concelier/__Tests/**concelier-*.yml, connector-*.yml
VexLenssrc/VexLens/**src/VexLens/__Tests/**
VexHubsrc/VexHub/**src/VexHub/__Tests/**
Mirrorsrc/Mirror/**src/Mirror/__Tests/**mirror-*.yml

Artifacts & Evidence

ModuleSource PathsTest PathsWorkflows (aspirational)
Attestor (includes Signer, Provenance)src/Attestor/**src/Attestor/__Tests/**attestation-*.yml, attestor-*.yml
Signer (sub-module)src/Attestor/StellaOps.Signer/**, src/Attestor/__Libraries/StellaOps.Signer.*/**src/Attestor/StellaOps.Signer/StellaOps.Signer.Tests/**
Provenance (sub-module)src/Attestor/StellaOps.Provenance.*/**src/Attestor/__Tests/StellaOps.Provenance.*/**provenance-*.yml
SbomServicesrc/SbomService/**src/SbomService/__Tests/**
EvidenceLockersrc/EvidenceLocker/**src/EvidenceLocker/__Tests/**evidence-*.yml
ExportCentersrc/ExportCenter/**src/ExportCenter/__Tests/**export-*.yml
Findings (includes RiskEngine, VulnExplorer)src/Findings/**src/Findings/__Tests/**findings-*.yml, ledger-*.yml
RiskEngine (sub-module)src/Findings/StellaOps.RiskEngine.*/**src/Findings/__Tests/StellaOps.RiskEngine.*/**

Policy & Risk

ModuleSource PathsTest PathsWorkflows (aspirational)
Policysrc/Policy/**src/Policy/__Tests/**policy-*.yml
Unknownssrc/Unknowns/**src/Unknowns/__Tests/**

NOTE — Unknowns is not in path-filters.yml. src/Unknowns/ exists in the tree, but the config modules: block has no unknowns entry. It is covered only generically by the change-detection script (any src/Unknowns/** change with a *.Tests.csproj present selects it).

Operations

ModuleSource PathsTest PathsWorkflows (aspirational)
JobEngine (includes Scheduler, TaskRunner, PacksRegistry — Sprint 208)src/JobEngine/**src/JobEngine/** (see note)
Scheduler (sub-module)src/JobEngine/StellaOps.Scheduler.*/**src/JobEngine/StellaOps.Scheduler.__Tests/**
TaskRunner (sub-module)src/JobEngine/StellaOps.TaskRunner*/**src/JobEngine/StellaOps.TaskRunner.__Tests/**
PacksRegistry (sub-module)src/JobEngine/StellaOps.PacksRegistry*/**src/JobEngine/StellaOps.PacksRegistry.__Tests/** (config still points at stale src/Orchestrator/...)packs-*.yml
Notify (config folds Notifier in)src/Notify/**, src/Notifier/**src/Notify/__Tests/**notify-*.yml
Timeline (includes TimelineIndexer)src/Timeline/**src/Timeline/__Tests/**
Replaysrc/Replay/**src/Replay/__Tests/**replay-*.yml

NOTE — JobEngine test path. Neither this doc’s earlier src/JobEngine/__Tests/** nor path-filters.yml’s src/JobEngine/StellaOps.JobEngine/StellaOps.JobEngine.Tests/** resolves to a real directory. JobEngine test projects actually live under per-domain __Tests roots — src/JobEngine/StellaOps.Scheduler.__Tests/**, src/JobEngine/StellaOps.PacksRegistry.__Tests/**, and src/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Tests/**. The resilient pattern is the top-level src/JobEngine/**, which is what change detection uses. (Config also lists Notifier as part of notify; there is a separate src/Notifier/ tree but no standalone notifier config entry, and path-filters.yml does not list Timeline at all.)

Infrastructure

ModuleSource PathsTest PathsWorkflows (aspirational)
Cryptographysrc/Cryptography/**src/__Libraries/__Tests/StellaOps.Cryptography*/**crypto-*.yml
Telemetrysrc/Telemetry/**src/Telemetry/__Tests/**
Signalssrc/Signals/**src/Signals/__Tests/**signals-*.yml
AirGapsrc/AirGap/**src/AirGap/__Tests/**airgap-*.yml, offline-*.yml (only airgap-federation-e2e.yml exists)
AOCsrc/Aoc/**src/Aoc/__Tests/**aoc-*.yml

Integration

ModuleSource PathsTest PathsWorkflows (aspirational)
Integrationssrc/Integrations/**src/Integrations/__Tests/**
CLIsrc/Cli/**src/Cli/__Tests/**cli-*.yml
Websrc/Web/**src/Web/**/*.spec.tslighthouse-*.yml

NOTE — Integrations.__Extensions. path-filters.yml flags that src/Integrations/__Extensions/ holds non-.NET IDE plugins (TypeScript/Kotlin) that do not participate in .NET CI; separate npm/gradle CI would be required. Active FE workflows (fe-web-tests.yml, fe-e2e-tests.yml) gate src/Web/StellaOps.Web/** specs — the src/Web/**/*.spec.ts glob above is the config’s broader convention.


DevOps & CI/CD Paths

These mirror the devops: block in path-filters.yml. The config keeps each entry to a single broad glob; the doc previously listed extra sub-globs that are either redundant or do not match anything — corrected/annotated below.

Docker & Containers

docker:
  - 'devops/docker/**'
  - '**/Dockerfile'

**/Dockerfile does NOT match Dockerfile.<flavour> files (e.g. devops/docker/Dockerfile.console, Dockerfile.ci), and many such files exist. If you want those covered, add '**/Dockerfile.*'path-filters.yml currently omits it.

Compose Profiles

compose:
  - 'devops/compose/**'

The doc previously also listed docker-compose*.yml; there are no compose files at the repo root. They all live under devops/compose/, already covered by the glob above.

Helm Charts

helm:
  - 'devops/helm/**'

The doc previously also listed devops/helm/stellaops/**; no such subchart exists — devops/helm/ currently contains only a README.md. The broad glob is the only helm entry in path-filters.yml.

Database

database:
  - 'devops/database/**'

devops/database/postgres/ exists but is already covered by devops/database/**; path-filters.yml does not list it separately.

CI/CD Scripts

scripts:
  - '.gitea/scripts/**'

path-filters.yml’s devops.scripts lists only .gitea/scripts/**. The doc previously added devops/scripts/** (that directory does exist, but it is not in the config entry).

Workflows

workflows:
  - '.gitea/workflows/**'

Workflow-file changes are tracked under devops.workflows, not the infrastructure block. This is the canonical home for the .gitea/workflows/** pattern.


Test Infrastructure Paths

These mirror the test_infrastructure: block in path-filters.yml. The config keeps each entry to one broad glob; the sub-globs the doc previously listed are real directories but redundant under the parent glob — except StellaOps.TestKit, which does not exist.

Global Test Suites

global_tests:
  - 'src/__Tests/**'

Sub-suites like src/__Tests/Integration/**, .../architecture/**, .../security/**, .../chaos/**, and .../e2e/** all exist on disk but are already covered by src/__Tests/**. Note: any change under src/__Tests/** is classified as affects_all=true by the change-detection script, deferring to the nightly sweep.

Shared Test Libraries

shared_libraries:
  - 'src/__Tests/__Libraries/**'

path-filters.yml lists only the parent glob (key name shared_libraries, not test_libraries). src/__Tests/__Libraries/StellaOps.Infrastructure.Postgres.Testing/ exists and is covered by it, but StellaOps.TestKit does not exist at src/__Tests/__Libraries/ — drop that pattern.

Test Datasets & Benchmarks

datasets:
  - 'src/__Tests/__Datasets/**'
benchmarks:
  - 'src/__Tests/__Benchmarks/**'

In path-filters.yml these are two separate keys (datasets and benchmarks); the doc previously merged __Benchmarks into datasets.


Example Workflow Configurations

PR-Gating Workflow (Skip Docs)

on:
  push:
    branches: [main]
    paths-ignore:
      - 'docs/**'
      - '*.md'
      - 'etc/**'
  pull_request:
    paths-ignore:
      - 'docs/**'
      - '*.md'
      - 'etc/**'

Module-Specific Workflow (With Cascading) — illustrative convention

This block illustrates the intended convention; no scanner-*.yml workflow exists today. The active scanner test coverage comes from the change-detection PR lane (dotnet-pr-tests.yml) and the nightly sweep, not a dedicated per-module workflow. Cascade/dependency globs below match the scanner entry’s dependencies in path-filters.yml.

on:
  push:
    branches: [main]
    paths:
      # Direct module paths
      - 'src/Scanner/**'
      - 'src/BinaryIndex/**'
      # Shared library cascades (path-filters.yml: modules.scanner.dependencies)
      - 'src/__Libraries/StellaOps.Evidence*/**'
      - 'src/__Libraries/StellaOps.Cryptography*/**'
      - 'src/__Libraries/StellaOps.Replay*/**'
      - 'src/__Libraries/StellaOps.Provenance/**'
      # Infrastructure cascades
      - 'Directory.Build.props'
      - 'Directory.Packages.props'
      # Self-reference (would be created alongside the workflow file)
      - '.gitea/workflows/scanner-*.yml'
  pull_request:
    paths:
      - 'src/Scanner/**'
      - 'src/BinaryIndex/**'
      - 'src/__Libraries/StellaOps.Evidence*/**'
      - 'src/__Libraries/StellaOps.Cryptography*/**'

Real example — workflow-renderer.yml (live workflow)

This active workflow is a concrete instance of the path-filter + self-reference convention above:

on:
  pull_request:
    paths:
      - 'src/Workflow/__Libraries/StellaOps.Workflow.Renderer.ElkSharp/**'
      - 'src/Workflow/__Libraries/StellaOps.Workflow.Renderer.ElkJs/**'
      - 'src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Svg/**'
      - 'src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Msagl/**'
      - 'src/Workflow/__Tests/StellaOps.Workflow.Renderer.Tests/**'
      - 'src/__Libraries/StellaOps.ElkSharp/**'
      - 'tools/elk-stress/**'
      - '.gitea/workflows/workflow-renderer.yml'   # self-reference
  workflow_dispatch: {}
  schedule:
    - cron: '0 6 * * *'

Documentation-Only Workflow

on:
  push:
    paths:
      - 'docs/**'
      - '*.md'
  pull_request:
    paths:
      - 'docs/**'
      - '*.md'

The doc previously listed scripts/render_docs.py as an extra trigger; no such script exists in the repo. (The only render script under scripts/ is .gitea/scripts/test/render-test-dashboard.py, which renders the test dashboard, not docs.) There is also no active docs-only CI workflow at present.

Docker/Container Workflow

on:
  push:
    paths:
      - '**/Dockerfile'
      - '**/Dockerfile.*'   # needed to catch Dockerfile.<flavour> files
      - 'devops/docker/**'
  schedule:
    - cron: '0 4 * * *'  # Also run daily for vulnerability updates

Note the **/Dockerfile.* line: it is required to catch the many Dockerfile.<flavour> files in devops/docker/ but is absent from path-filters.yml’s devops.docker entry (which lists only **/Dockerfile).


Validation Checklist

When adding or modifying path filters:


Glob Pattern Reference

PatternMatches
src/**All files under src/ recursively
src/*Direct children of src/ only
**/*.csAll .cs files anywhere
*.mdMarkdown files in root only
src/**/*.csprojAll .csproj files under src/
!src/**/*.mdExclude markdown in src/
**/Dockerfile*Dockerfile, Dockerfile.prod, etc.