Workflow Triggers & Dependencies
Reference for Stella Ops CI/CD workflow triggering rules and dependency chains. Audience: engineers who need to know which workflow runs for a given event, and how the job graphs depend on one another.
STATUS — the “Active Workflows” table is generated from
.gitea/workflows/on every change; the archived-design sections below were reconciled 2026-05-30. The active CI tree (.gitea/workflows/) does not match most of the tables and dependency diagrams below. On 2026-02-01 all 118 original workflow files were moved to.gitea/workflows-archived/(see.gitea/README.md), and active CI has since been rebuilt as a different, granular per-concern set of workflows with different file names; the generated table states the current count. As a result:
- The “Complete Trigger Matrix”, “Dependency Chains”, “Master Trigger Configuration”, “Trigger Decision Tree”, and “Smart Dependency Cascading” sections describe the archived/superseded monolithic CI design (
build-test-deploy.yml,test-matrix.yml,release-suite.yml,release.yml,module-publish.yml,cli-build.yml,rollback.yml,promote.yml, etc.). Those files live under.gitea/workflows-archived/and are not wired to any active trigger — they only run viaactor thelocal-ci-verify.ymldry-run harness. Treat those sections as historical reference for the archived catalog, not as a description of what runs today.- The “Active Workflows (current
.gitea/workflows/)” table immediately below is generated bytools/scripts/generate/workflow-triggers.pyand is the list of what actually triggers today. The repo-hygiene lane runs it with--checkon every change.- The generic syntax sections (Trigger Types, Conditional Execution Patterns, Path Filters Reference, Tag Patterns Reference, Workflow Inputs Reference, Best Practices, Troubleshooting) remain valid as Gitea/GitHub Actions syntax references and are not workflow-specific.
Source of truth:
.gitea/workflows/*.yml(active) and.gitea/workflows-archived/*.yml(archived) in the main repo. This doc carries no auth-scope claims, so the scope catalog (StellaOpsScopes.cs) does not apply here.
Active Workflows (current .gitea/workflows/)
The table between the generated-block markers is written by tools/scripts/generate/workflow-triggers.py from every .gitea/workflows/*.yml file and from tools/scripts/validate/ci-lane-input-sets.json. Do not edit it by hand: change the workflow, run python3 tools/scripts/generate/workflow-triggers.py, and commit both. repo-hygiene-verify.yml runs the generator with --check on every pull request and every push to main, and fails when the committed table differs from the workflows.
How to read the cells:
- PR and Main push:
❌means no trigger for that event.✅means the trigger is unfiltered.✅*means apathsallow-list gates it.✅‡means apaths-ignorelist gates it. A push trigger on branches other thanmain, or on tags, names them in the cell. - Every
✅*cell, PR or Main push, is an allow-list that is generated, not hand-kept.tools/scripts/generate/ci-lane-allowlists.pyderives it from the lane’s ProjectReference closure, including the MSBuild props and NuGet configuration each project reads, and from the Dockerfile, solution orgo.modthe lane builds. A lane that has both triggers carries the same derived list under each. The hygiene gate re-derives every list on every push and fails when a committed list differs, or when apull_requestallow-list has no derivation. - A
✅‡cell ignores only trees the lane’s scripts never read, for example sprint boards underdocs/implplan/. A change touching nothing else skips the lane. - A lane that pushes to
mainis never filtered more narrowly on PR than on Main push. It runs the same scripts on either event, so its PR trigger is unfiltered, ignores only trees its push list ignores, or carries the same derived allow-list. - Schedule (UTC) lists every cron. Manual is
workflow_dispatch, with its input names. - Input set is the lane’s
filterintools/scripts/validate/ci-lane-input-sets.json. That file records, for every push-to-mainlane, what its scripts read, which trees it ignores and why, and why a lane stays unfiltered. A lane that filters its PR trigger but does not push tomainhas a record underpullRequestLanes, shown with(PR).tools/scripts/validate/check-ci-paths-ignore.shchecks both on every push. The generator refuses to write a row whose recorded filter disagrees with the workflow, and a filtered lane that has no record. - Workflow
name:is the display name the forge shows for the run.
61 active workflow files; 38 push to main. Generated from .gitea/workflows/*.yml and tools/scripts/validate/ci-lane-input-sets.json.
| Workflow file | PR | Main push | Schedule (UTC) | Manual | Input set | Workflow name: |
|---|---|---|---|---|---|---|
agent-hooks-verify.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Agent hook guards self-test |
airgap-federation-e2e.yml | ✅‡ | ❌ | 0 3 * * * | ✅ | paths-ignore (PR) | Air-gap Vulnerability Federation Acceptance |
auto-sbom-readiness-e2e.yml | ✅‡ | ❌ | 30 4 * * * | ✅ | paths-ignore (PR) | Auto-SBOM Readiness E2E |
chaos-router.yml | ✅* | ❌ | 30 2 * * * | ✅ | paths (PR) | Router Chaos Tests |
cli-plugin-integration.yml | ✅* | ✅* | ❌ | ✅ | paths | CLI Plug-in Integration |
compliance-golden-path.yml | ❌ | ❌ | 0 7 * * * | ✅ (inputs: require_soa_run) | — | Compliance Golden Path |
compliance-script-tests.yml | ✅ | ✅‡ | ❌ | ✅ | paths-ignore | Compliance script tests |
compose-healthchecks-verify.yml | ✅ | ✅‡ | ❌ | ✅ | paths-ignore | Compose healthcheck exemptions |
compose-render-verify.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Compose profile render |
console-layout-audit.yml | ❌ | ❌ | 0 8 * * * | ✅ (inputs: limit) | — | Console Layout Guard |
container-name-prefix-verify.yml | ✅ | ✅‡ | ❌ | ✅ | paths-ignore | Container name estate prefix |
cra-self-update-verify.yml | ✅* | ✅* | ❌ | ✅ | paths | CRA Self-Update Verify |
customer-agreement-verify.yml | ✅ | ✅‡ | 50 4 * * 1 | ✅ | paths-ignore | Customer Agreement gate |
dependency-license-verify.yml | ✅ | ✅‡ | 20 4 * * 1 | ✅ | paths-ignore | Dependency licence gate |
docs-cli-parity.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Docs / CLI parity (doctor-crypto) |
docs-links.yml | ✅ | ✅ | ❌ | ✅ | none | Docs / relative links |
docs-timestamp-articles.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Docs / timestamp-assurance articles (doctor-evidence) |
dotnet-benchmarks.yml | ❌ | ❌ | ❌ | ✅ | — | .NET Benchmarks |
dotnet-nightly-sweep.yml | ❌ | ❌ | ❌ | ✅ (inputs: max_parallel, build_parallelism, cores_per_process, gc_heap_limit, project_timeout, scope_glob, shard) | — | .NET Test Sweep |
dotnet-pr-tests.yml | ❌ | ❌ | ❌ | ✅ | — | .NET PR Tests |
e2e-analyzer-coverage.yml | ✅‡ | ❌ | 30 3 * * * | ✅ (inputs: target, phases, corpus_count) | paths-ignore (PR) | E2E Analyzer Coverage |
e2e-notify-coverage.yml | ✅* | ❌ | 0 3 * * * | ✅ (inputs: phases) | paths (PR) | E2E Notify-channel Coverage |
fe-e2e-tests.yml | ❌ | ❌ | ❌ | ✅ | — | FE E2E Tests |
fe-web-tests.yml | ✅‡ | ❌ | ❌ | ✅ | paths-ignore (PR) | FE Web - Spec Drift Gate |
go-tests.yml | ✅* | ✅* | 30 4 * * * | ✅ | paths | Go Tests |
host-runner-timeout-probe.yml | ❌ | ❌ | ❌ | ✅ | — | Host runner timeout probe |
key-material-verify.yml | ✅ | ✅ | 40 3 * * * | ✅ | none | Committed key-material guard |
knowledge-base-verify.yml | ✅ | ✅ | ❌ | ✅ | none | Agent knowledge-base drift guard |
licence-publications-verify.yml | ✅ | ✅‡ | 35 4 * * 1 | ✅ | paths-ignore | Outbound licence gate |
local-ci-verify.yml | ❌ | ❌ | ❌ | ✅ (inputs: workflow, dry_run) | — | Local CI Verification |
local-runtime-verify.yml | ✅ | ✅‡ | 55 3 * * * | ✅ | paths-ignore | RAR runtime manifest source review |
local-scn-runtime-verify.yml | ✅ | ✅‡ | 45 3 * * * | ✅ | paths-ignore | SCN runtime manifest source review |
no-github-dir-verify.yml | ✅ | ✅‡ | 15 4 * * * | ✅ | paths-ignore | No .github directory guard |
oci-distribution-conformance.yml | ✅‡ | ❌ | 15 3 * * * | ✅ | paths-ignore (PR) | OCI Distribution Conformance |
offline-kit-assembly.yml | ✅‡ | ❌ | 0 4 * * 1 | ✅ (inputs: version, enable_signing) | paths-ignore (PR) | Offline Kit Assembly |
openapi-conformance-skips-verify.yml | ✅ | ✅‡ | ❌ | ✅ | paths-ignore | OpenAPI conformance skip register |
openapi-conformance.yml | ✅‡ | ❌ | 0 5 * * * | ✅ | paths-ignore (PR) | OpenAPI Conformance |
openapi-truthfulness.yml | ✅* | ✅* | ❌ | ✅ | paths | OpenAPI Truthfulness |
plugin-linux-os-confinement.yml | ✅* | ✅* | ❌ | ✅ | paths | Plugin Linux OS Confinement (AppArmor) |
plugin-linux-overlay-sandbox.yml | ✅* | ✅* | ❌ | ✅ | paths | Plugin Linux Overlay Sandbox |
plugin-windows-filesystem-sandbox.yml | ✅* | ✅* | ❌ | ✅ | paths | Plugin Windows Filesystem Sandbox |
plugin-windows-os-confinement.yml | ✅* | ✅* | ❌ | ✅ | paths | Plugin Windows OS Confinement (Job Object) |
python-runtime-compat-verify.yml | ✅ | ✅‡ | 20 5 * * 2 | ✅ | paths-ignore | python runtime compatibility |
qa-feature-check-integrity.yml | ✅ | ✅ | ❌ | ✅ | none | QA feature-check integrity (merge-blocking) |
reachability-negative-class.yml | ✅* | ✅* | ❌ | ✅ | paths | Reachability negative-class gate |
release-bundle-drift-verify.yml | ✅ | ✅ | 50 4 * * * | ✅ | none | release bundle drift |
release-bundle-installer-windows-selftest.yml | ❌ | ❌ | 45 0 * * * | ✅ | — | release bundle installer self-test (Windows) |
release-manifest-verify-publish.yml | ✅* | ✅* | ❌ | ✅ (inputs: release_dir, manifest, trust_root, publish_referrers) | paths | Release Manifest Verify And Publish |
release-orchestrator-live-e2e.yml | ✅* | ❌ | ❌ | ✅ | paths (PR) | Release Orchestrator Live E2E |
repo-hygiene-verify.yml | ✅ | ✅ | ❌ | ✅ | none | Repo hygiene |
runtime-instrumentation-host-agents.yml | ✅* | ✅* | ❌ | ✅ | paths | RuntimeInstrumentation Host Agents |
sprint-status-verify.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Sprint status census |
stella-cli-publish.yml | ❌ | ❌ | ❌ | ✅ (inputs: tag, also_tag_commit_sha, acknowledge_overwrite) | — | Publish stella CLI image |
stella-vs-trivy-benchmark.yml | ❌ | ❌ | 0 20 * * 5 | ✅ (inputs: suite, shard, shard_count, max_projects, max_parallel_workers, cohort, trivy_image, grype_image) | — | stella-vs-trivy benchmark |
supply-chain-hardening.yml | ✅ | ✅ | 15 3 * * * | ✅ (inputs: profile) | none | Supply Chain Hardening |
test-architecture.yml | ✅‡ | ✅‡ | 40 0 * * * | ✅ | paths-ignore | Architecture Conformance |
test-manifest-execution.yml | ✅ | ✅‡ | 17 1 * * *, 43 3 * * 0 | ✅ (inputs: mode, execute, full_sweep, runtime_cost, suite_shard_total, suite_shard_index) | paths-ignore | Test manifest execution |
test-manifest-plan.yml | ✅‡ | ✅‡ | ❌ | ✅ | paths-ignore | Test manifest and planner |
unified-search-quality.yml | ✅* | ✅* | 30 2 * * * | ✅ | paths | Unified Search Quality |
workflow-oracle-tests.yml | ✅* | ❌ | ❌ | ✅ | paths (PR) | Workflow Oracle/Redis Tests |
workflow-renderer.yml | ✅* | ❌ | 0 6 * * * | ✅ | paths (PR) | Workflow Renderer |
Present but never scheduled by Gitea (.disabled suffix): _deprecated-concelier-ci.yml.disabled, _deprecated-concelier-tests.yml.disabled.
Notes:
.gitea/workflow-templates/holds copy-me templates that are not executed.- The active set is curated for the resource-constrained Gitea host; several lanes (
dotnet-*,fe-e2e-tests) are deliberatelyworkflow_dispatch-only after the 2026-05-14 OOM incident — see the comments at the top of those files.
Trigger Types Overview
1. Push Triggers
Workflows triggered by commits pushed to branches.
on:
push:
branches: [main, develop] # Branch filter
paths: # Path filter (optional)
- 'src/**'
paths-ignore: # Exclude paths (optional)
- 'docs/**'
tags: # Tag filter (for releases)
- 'v*'
2. Pull Request Triggers
Workflows triggered by PR events.
on:
pull_request:
branches: [main, develop] # Target branch filter
types: [opened, synchronize, reopened] # Event types
paths:
- 'src/**'
3. Schedule Triggers
Cron-based scheduled execution.
on:
schedule:
- cron: '0 5 * * *' # Daily at 5 AM UTC
- cron: '0 2 * * 0' # Weekly Sunday at 2 AM UTC
4. Manual Triggers
On-demand workflow execution with inputs.
on:
workflow_dispatch:
inputs:
environment:
type: choice
options: [staging, production]
dry_run:
type: boolean
default: false
5. Workflow Call (Reusable)
Called by other workflows.
on:
workflow_call:
inputs:
category:
type: string
required: true
Complete Trigger Matrix
ARCHIVED-DESIGN REFERENCE. Every workflow named in this section now lives in
.gitea/workflows-archived/and does not trigger in active CI (see the status banner and the “Active Workflows” table above). Specific corrections to the original claims are inlined below; the tables are retained as a record of the archived design. Verified against the archived files on 2026-05-30: the trigger details (branches, path filters, cron, tag patterns) below are broadly accurate for the archived workflow, except where flagged “[CORRECTION]”.
PR-Gating Workflows (Always Run on PR — when active)
| Workflow | Branches | Path Filters | Purpose |
|---|---|---|---|
test-matrix.yml | main | !docs/**, !*.md | Unit, Architecture, Contract, Integration, Security, Golden tests |
build-test-deploy.yml | main, develop | src/**, docs/**, scripts/** | Build verification |
policy-lint.yml | main | docs/policy/**, src/Cli/** | Policy file validation |
sast-scan.yml | main, develop | src/**, *.cs, *.ts, Dockerfile* | Static security analysis |
docs.yml | - | docs/**, scripts/render_docs.py | Documentation validation |
integration-tests-gate.yml | main, develop | src/**, src/__Tests/** | Extended integration |
Main Branch Only Workflows
| Workflow | Trigger Condition | Purpose |
|---|---|---|
build-test-deploy.yml → deploy | github.ref == 'refs/heads/main' | Deploy to staging |
integration-tests-gate.yml → corpus-validation | github.ref == 'refs/heads/main' | Ground truth validation |
coverage-report | After integration tests on main | Full coverage analysis |
Tag-Triggered Workflows
| Workflow | Tag Pattern | Example | Purpose |
|---|---|---|---|
release-suite.yml | suite-* | suite-2026.04 | Ubuntu-style suite release |
release.yml | v* | v2025.12.1, v2025.12.0-edge | Version bundle release |
module-publish.yml | module-*-v* | module-authority-v1.2.3 | Per-module publishing |
Scheduled Workflows
| Workflow | Schedule (UTC) | Frequency | Purpose |
|---|---|---|---|
nightly-regression.yml | 0 2 * * * | Daily 2 AM | Full regression suite |
dependency-security-scan.yml | 0 2 * * 0 | Sunday 2 AM | Vulnerability audit |
renovate.yml | 0 3,15 * * * | Daily 3 AM & 3 PM | Dependency updates |
sast-scan.yml | 30 3 * * 1 | Monday 3:30 AM | Weekly deep scan |
migration-test.yml | 30 4 * * * | Daily 4:30 AM | Migration validation |
build-test-deploy.yml | 0 5 * * * | Daily 5 AM | Extended build tests |
test-matrix.yml | 0 5 * * * | Daily 5 AM | Extended test categories |
Manual-Only Workflows
| Workflow | Inputs | Purpose |
|---|---|---|
cli-build.yml | rids, config, sign | Multi-platform CLI builds |
scanner-determinism.yml | - | Verify scanner reproducibility |
cross-platform-determinism.yml | - | Cross-OS build verification |
rollback.yml | environment, service, version | Emergency rollback |
promote.yml | from_env, to_env, version | Environment promotion |
Dependency Chains
ARCHIVED-DESIGN REFERENCE.
build-test-deploy.yml,test-matrix.yml, andrelease-suite.ymlare all in.gitea/workflows-archived/. The job graphs below reflect those archived workflows and do not represent any active pipeline. The active CI has no monolithic build→test→deploy or suite-release pipeline (see the status banner and “Active Workflows” table above).
Build → Test → Deploy Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ build-test-deploy.yml │
├─────────────────────────────────────────────────────────────────┤
│ │
│ profile-validation ─────────────────────────────────┐ │
│ │ │ │
│ ▼ │ │
│ build-test ─────────────────────────────────────────┤ │
│ │ (CLI, Concelier, Authority, Scanner, etc.) │ │
│ │ │ │
│ ▼ │ │
│ quality-gates ──────────────────────────────────────┤ │
│ │ (Reachability, TTFS, Performance SLOs) │ │
│ │ │ │
│ ▼ │ │
│ security-testing (PR label or schedule) ────────────┤ │
│ │ │ │
│ ▼ │ │
│ sealed-mode-ci ─────────────────────────────────────┤ │
│ │ │ │
│ ▼ │ │
│ docs ───────────────────────────────────────────────┤ │
│ │ │ │
│ ▼ │ │
│ scanner-perf ───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ deploy (main branch only OR workflow_dispatch) │
│ │ │
│ ▼ │
│ summary │
│ │
└─────────────────────────────────────────────────────────────────┘
Test Matrix Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ test-matrix.yml │
├─────────────────────────────────────────────────────────────────┤
│ │
│ discover ─────────────────────────────────────────────────┐ │
│ │ (Find all *.Tests.csproj files) │ │
│ │ │ │
│ ├───▶ pr-gating-tests (Matrix: 5 categories) │ │
│ │ ├── Unit │ │
│ │ ├── Architecture │ │
│ │ ├── Contract │ │
│ │ ├── Security │ │
│ │ └── Golden │ │
│ │ │ │
│ ├───▶ integration (PostgreSQL service) │ │
│ │ │ │
│ └───▶ extended-tests (schedule or manual) │ │
│ ├── Performance │ │
│ ├── Benchmark │ │
│ ├── AirGap │ │
│ ├── Chaos │ │
│ ├── Determinism │ │
│ ├── Resilience │ │
│ └── Observability │ │
│ │ │ │
│ ◀────────────────────────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ summary ◀─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Suite Release Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ release-suite.yml │
│ (suite-* tag OR manual) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ parse-tag (if push event) ────────────────────────────────┐ │
│ │ │ │
│ ▼ │ │
│ validate ─────────────────────────────────────────────────┤ │
│ │ │ │
│ ▼ │ │
│ test-gate (optional, skip with skip_tests=true) ──────────┤ │
│ │ │ │
│ ├───▶ build-modules (Matrix: 9 modules) │ │
│ │ ├── Authority │ │
│ │ ├── Scanner │ │
│ │ ├── Concelier │ │
│ │ ├── Excititor │ │
│ │ ├── SbomService │ │
│ │ ├── EvidenceLocker │ │
│ │ ├── Policy │ │
│ │ ├── Attestor │ │
│ │ └── VexLens │ │
│ │ │ │
│ ├───▶ build-containers (Matrix: 9 images) │ │
│ │ │ │
│ ├───▶ build-cli (Matrix: 5 runtimes) │ │
│ │ ├── linux-x64 │ │
│ │ ├── linux-arm64 │ │
│ │ ├── win-x64 │ │
│ │ ├── osx-x64 │ │
│ │ └── osx-arm64 │ │
│ │ │ │
│ └───▶ build-helm │ │
│ │ │
│ ◀────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ release-manifest ─────────────────────────────────────────┐ │
│ │ │ │
│ ├───▶ generate-changelog │ │
│ ├───▶ generate-suite-docs │ │
│ └───▶ generate-compose │ │
│ │ │
│ ◀────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ create-release ───────────────────────────────────────────┐ │
│ │ │ │
│ ▼ │ │
│ commit-docs ──────────────────────────────────────────────┤ │
│ │ │ │
│ ▼ │ │
│ summary ◀─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Conditional Execution Patterns
Branch-Based Conditions
# Deploy only on main branch
deploy:
if: github.ref == 'refs/heads/main'
# Run on main or develop
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
# Skip on release branches
if: "!startsWith(github.ref, 'refs/heads/release/')"
Event-Based Conditions
# Different behavior based on trigger
steps:
- name: Full scan (schedule)
if: github.event_name == 'schedule'
run: ./scan.sh --full
- name: Quick scan (PR)
if: github.event_name == 'pull_request'
run: ./scan.sh --quick
Input-Based Conditions
# Run extended tests if requested
extended-tests:
if: >-
github.event_name == 'schedule' ||
github.event.inputs.include_performance == 'true' ||
github.event.inputs.include_benchmark == 'true'
Failure-Based Conditions
# Run cleanup on failure
cleanup:
if: failure()
# Run notification always
notify:
if: always()
# Run only on success
deploy:
if: success()
Complex Conditions
# Deploy gate: multiple conditions
deploy:
if: >-
needs.build-test.result == 'success' &&
needs.docs.result == 'success' &&
needs.scanner-perf.result == 'success' &&
((github.event_name == 'push' && github.ref == 'refs/heads/main') ||
github.event_name == 'workflow_dispatch')
Path Filters Reference
Common Path Patterns
| Pattern | Matches | Example Files |
|---|---|---|
src/** | All source code | src/Scanner/Program.cs |
docs/** | All documentation | docs/api/openapi.yaml |
*.md | Root markdown | README.md, CHANGELOG.md |
**/*.csproj | All project files | src/Cli/StellaOps.Cli.csproj |
devops/** | DevOps config | devops/helm/values.yaml |
.gitea/workflows/** | Workflow files | .gitea/workflows/test-matrix.yml |
Path Filter Examples
# Source code changes only
paths:
- 'src/**'
- '!src/**/*.md' # Exclude markdown in src
# Documentation only
paths:
- 'docs/**'
- '*.md'
- 'scripts/render_docs.py'
# Security-relevant files
paths:
- 'src/**/*.cs'
- 'src/**/*.csproj'
- '**/Dockerfile*'
- '.gitea/workflows/sast-scan.yml'
# Dependency files
paths:
- 'src/Directory.Packages.props'
- '**/package.json'
- '**/package-lock.json'
- '**/*.csproj'
Tag Patterns Reference
Semantic Version Tags
# Standard releases
tags:
- 'v*' # v1.0.0, v2025.12.1, v2025.12.0-edge
# Channel-specific
tags:
- 'v*-edge' # v2025.12.0-edge
- 'v*-stable' # v2025.12.0-stable
- 'v*-lts' # v2025.12.0-lts
Suite Tags
tags:
- 'suite-*' # suite-2026.04, suite-2026.10
Module Tags
tags:
- 'module-*-v*' # module-authority-v1.2.3
# module-scanner-v2.0.0
# module-cli-v3.1.0
Workflow Inputs Reference
Common Input Types
workflow_dispatch:
inputs:
# String input
version:
description: 'Version to release'
required: true
type: string
# Choice input
environment:
description: 'Target environment'
type: choice
options:
- staging
- production
default: staging
# Boolean input
dry_run:
description: 'Run without making changes'
type: boolean
default: false
# Multi-select (via string)
rids:
description: 'Runtime identifiers (comma-separated)'
type: string
default: 'linux-x64,linux-arm64,win-x64'
Accessing Inputs
steps:
- name: Use input
run: |
echo "Version: ${{ github.event.inputs.version }}"
echo "Environment: ${{ inputs.environment }}"
if [[ "${{ inputs.dry_run }}" == "true" ]]; then
echo "Dry run mode"
fi
Best Practices
1. Minimize PR Workflow Duration
# Use path filters to skip irrelevant runs
paths-ignore:
- 'docs/**'
- '*.md'
- 'LICENSE'
# Use concurrency to cancel outdated runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
2. Fail Fast for Critical Issues
strategy:
fail-fast: true # Stop all jobs if one fails
matrix:
category: [Unit, Integration, Security]
3. Use Matrix for Parallel Execution
strategy:
matrix:
include:
- category: Unit
timeout: 20
- category: Integration
timeout: 45
4. Preserve Artifacts
- uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.category }}
path: ./TestResults
retention-days: 14 # PR artifacts
# retention-days: 90 # Release artifacts
5. Use Conditional Steps
- name: Deploy (main only)
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
- name: Notify on failure
if: failure()
run: ./notify-slack.sh
Troubleshooting Triggers
Workflow Not Running
- Check branch protection rules - Ensure workflow runs are allowed
- Verify path filters - File changes must match
pathspatterns - Check
ifconditions - Job may be skipped by condition - Review concurrency settings - May be cancelled by concurrent run
Workflow Running Unexpectedly
- Check
paths-ignore- May need to exclude more paths - Review schedule - Cron schedule may overlap with events
- Check tag patterns - Tag may match unexpected pattern
Schedule Not Triggering
- Verify cron syntax - Use crontab.guru
- Check workflow file location - Must be on default branch
- Review repository activity - Inactive repos may have schedules disabled
Trigger Decision Tree
ARCHIVED-DESIGN REFERENCE. This tree maps events to the archived workflow catalog (Category A/B/C labels,
release-suite.yml,module-publish.yml,service-release.yml,containers-multiarch.yml,cli-build.yml). None of those tag/branch routes are wired in active CI. [CORRECTION] In the archived catalog,cli-build.ymlandcontainers-multiarch.ymlareworkflow_dispatch-only — they do not trigger onv*.*.*tags as theOn TAG pushbranch below implies;release.ymlis the workflow that consumesv*tags. For live behaviour, consult the “Active Workflows” table at the top of this page.
Use this decision tree to determine which workflows run for each event:
On PUSH to branch:
│
├── Is branch main/develop?
│ ├── YES → Run Category A (PR-Gating) + B (Main-Only) + affected C (Module)
│ └── NO (feature branch) → Skip CI (rely on PR workflow)
│
On PULL REQUEST:
│
├── Check changed paths
│ ├── docs/** only → Skip all (or run docs.yml only)
│ ├── src/** changed → Run Category A + affected C modules
│ └── *.csproj or *.props changed → Run Category A + B (full infrastructure)
│
On TAG push:
│
├── Match tag pattern
│ ├── suite-* → release-suite.yml
│ ├── module-*-v* → module-publish.yml
│ ├── service-*-v* → service-release.yml
│ ├── v*.*.* → containers-multiarch.yml + cli-build.yml
│ └── Other → Ignore
│
On SCHEDULE:
│
└── Run Category E pipelines per cron schedule
Smart Dependency Cascading
ARCHIVED-DESIGN REFERENCE. The
*-ci.ymlper-module workflows this section’s cascade graph assumes (e.g.scanner-ci.yml) are archived. Active workflows do scope their ownpathsfilters (see each file’son.pull_request.pathsblock), but the centralised cascade matrix below was designed for the archived catalog and is not a guaranteed contract in the current.gitea/workflows/set — verify per active file.
When shared libraries change, dependent module tests must also run:
Dependency Graph
SHARED LIBRARY TRIGGERS TESTS FOR
─────────────────────────────────────────────────────────────────
StellaOps.Cryptography* → ALL modules (security-critical)
- Scanner, Attestor, Authority
- EvidenceLocker, Signer
- AirGap, Offline tests
- Security test suite
StellaOps.Evidence* → Scanner, Attestor, EvidenceLocker
StellaOps.Provenance → ExportCenter, SbomService
StellaOps.Infrastructure.* → ALL integration tests
StellaOps.Postgres* (database-dependent modules)
StellaOps.Replay* → Scanner, Determinism tests
StellaOps.Determinism Replay module tests
StellaOps.Verdict → Policy, RiskEngine, ReachGraph
StellaOps.DeltaVerdict
StellaOps.Plugin → Authority, Scanner, Concelier
(plugin-based modules)
Directory.Build.props → ALL modules (build config)
Directory.Packages.props ALL tests
nuget.config
Cascade Implementation
Each workflow includes paths from its dependencies:
# Example: scanner-ci.yml with cascading
name: Scanner CI
on:
push:
branches: [main]
paths:
# Direct module paths
- 'src/Scanner/**'
- 'src/BinaryIndex/**'
# Shared library dependencies (cascading)
- 'src/__Libraries/StellaOps.Evidence*/**'
- 'src/__Libraries/StellaOps.Cryptography*/**'
- 'src/__Libraries/StellaOps.Replay*/**'
- 'src/__Libraries/StellaOps.Provenance/**'
# Infrastructure (triggers full test)
- 'Directory.Build.props'
- 'Directory.Packages.props'
Cascade Matrix Quick Reference
| When This Changes | Run These Tests |
|---|---|
src/__Libraries/StellaOps.Cryptography*/** | Scanner, Attestor, Authority, Evidence, Signer, AirGap, Security |
src/__Libraries/StellaOps.Evidence*/** | Scanner, Attestor, EvidenceLocker, Export |
src/__Libraries/StellaOps.Infrastructure*/** | ALL integration tests |
src/__Libraries/StellaOps.Replay*/** | Scanner, Determinism, Replay |
src/__Libraries/StellaOps.Verdict/** | Policy, RiskEngine, ReachGraph |
src/__Libraries/StellaOps.Plugin/** | Authority, Scanner, Concelier |
Directory.Build.props | ALL modules |
Master Trigger Configuration
ARCHIVED-DESIGN REFERENCE. Every workflow file named in the table below (
build-test-deploy.yml,test-matrix.yml,determinism-gate.yml,policy-lint.yml,sast-scan.yml,secrets-scan.yml,container-scan.yml,release-suite.yml,module-publish.yml,service-release.yml,release.yml,cli-build.yml,containers-multiarch.yml,rollback.yml,promote.yml,nightly-regression.yml,renovate.yml,migration-test.yml, thescanner-*/concelier-*/authority-*modules, etc.) is in.gitea/workflows-archived/and triggers on nothing in active CI. For the live trigger configuration, use the “Active Workflows” table near the top of this page. The table below is retained as a record of the archived design.[CORRECTION] Some rows never matched even the archived catalog:
service-release.ymlis documented elsewhere but the corresponding row pattern referencedservice-*-v*;determinism-gate.yml’s archivedpull_requesttrigger istypes: [closed](not a standard PR gate), andsecrets-scan.ymlruns on all pushes (nopathsfilter). Re-verify any specific cell against the archived.ymlbefore quoting it.
Complete Workflow Trigger Table
| Workflow | Feature Branch | PR | Main Push | Tag | Schedule |
|---|---|---|---|---|---|
| Category A: PR-Gating | |||||
| build-test-deploy.yml | ❌ | ✅ | ✅ | ❌ | ✅ Daily |
| test-matrix.yml | ❌ | ✅ | ✅ | ❌ | ✅ Daily |
| determinism-gate.yml | ❌ | ✅ | ✅ | ❌ | ❌ |
| policy-lint.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| sast-scan.yml | ❌ | ✅ | ✅ | ❌ | ✅ Weekly |
| secrets-scan.yml | ❌ | ✅ | ✅ | ❌ | ❌ |
| dependency-license-gate.yml | ❌ | ✅* | ❌ | ❌ | ❌ |
| Category B: Main-Only | |||||
| container-scan.yml | ❌ | ❌ | ✅* | ❌ | ✅ Daily |
| integration-tests-gate.yml | ❌ | ❌ | ✅ | ❌ | ❌ |
| api-governance.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| aoc-guard.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| provenance-check.yml | ❌ | ❌ | ✅ | ❌ | ❌ |
| Category C: Module-Specific | |||||
| scanner-*.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| concelier-*.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| authority-*.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| findings-ledger-ci.yml | ❌ | ✅* | ✅* | ❌ | ❌ |
| evidence-locker.yml | ❌ | ❌ | ❌ | ❌ | ❌ (manual) |
| [all module-*.yml] | ❌ | ✅* | ✅* | ❌ | ❌ |
| Category D: Release | |||||
| release-suite.yml | ❌ | ❌ | ❌ | ✅ suite-* | ❌ |
| module-publish.yml | ❌ | ❌ | ❌ | ✅ module--v | ❌ |
| service-release.yml | ❌ | ❌ | ❌ | ✅ service--v | ❌ |
| release.yml | ❌ | ❌ | ❌ | ✅ v* | ❌ |
| cli-build.yml | ❌ | ❌ | ❌ | ❌ | ❌ (manual) |
| containers-multiarch.yml | ❌ | ❌ | ❌ | ❌ | ❌ (manual) |
| rollback.yml | ❌ | ❌ | ❌ | ❌ | ❌ (manual) |
| promote.yml | ❌ | ❌ | ❌ | ❌ | ❌ (manual) |
| Category E: Scheduled | |||||
| nightly-regression.yml | ❌ | ❌ | ❌ | ❌ | ✅ 2AM |
| dependency-security-scan.yml | ❌ | ✅* | ❌ | ❌ | ✅ Sun 2AM |
| container-scan.yml | ❌ | ❌ | ✅* | ❌ | ✅ 4AM |
| renovate.yml | ❌ | ❌ | ❌ | ❌ | ✅ 3AM/3PM |
| migration-test.yml | ❌ | ❌ | ❌ | ❌ | ✅ 4:30AM |
Legend: ✅ = with path filter, ✅ = always, ❌ = never*
Related Documentation
- README - CI/CD Overview
- Release Pipelines
- Test Strategy
- Path Filters Reference
- Active workflow definitions:
.gitea/workflows/*.yml(main repo) — source of truth for live triggers - Archived workflow catalog:
.gitea/workflows-archived/*.ymland.gitea/README.md(main repo) - CI/CD troubleshooting:
.gitea/docs/troubleshooting.md(main repo)
