Files
2026-06-18 22:21:38 -04:00

5.8 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
15-ci-skip-api-harness-jobs-for-doc-only-prs 02 ci
ci
gitea-actions
paths-filter
gate
doc-only-skip
requires provides affects
15-01
CI / gate commit-status
doc-only PR skip
.gitea/workflows/ci.yml
added patterns
dorny/paths-filter@v4 (Gitea action for PR diff detection)
changes job with paths-filter
always-running gate aggregate job
Gitea-safe individual needs.X.result checks
modified
.gitea/workflows/ci.yml
D-15-02-CHANGES-JOB
dorny/paths-filter@v4 with positive code filter; code==false means doc-only; ambiguous files default to full gate
D-15-02-GATE-INDIVIDUAL
individual needs.X.result checks (not wildcard) due to Gitea 1.26.2 bug
D-15-02-GATE-ALWAYS
if:always() on gate prevents deadlock on skipped upstream jobs (fix in Gitea 1.21.8, instance is 1.26.2)
duration_minutes completed_date tasks_completed tasks_total files_modified
5 2026-06-12 2 2 1

Phase 15 Plan 02: CI doc-only skip + gate aggregate Summary

One-liner: changes job (dorny/paths-filter@v4) classifies each PR; api/harness skip on code==false; always-running gate job aggregates all results using Gitea-safe individual needs.X.result checks, emitting the CI / gate status that Plan 03's branch-protection update requires.

Tasks Completed

Task Name Commit Files
1 Add changes job and gate api/harness on code output 7260438 .gitea/workflows/ci.yml
2 Add always-running gate aggregate job 547b12c .gitea/workflows/ci.yml

What Was Built

Task 1 — changes job + conditional api/harness:

A new changes job using dorny/paths-filter@v4 was inserted before fast-checks in .gitea/workflows/ci.yml. It:

  • Runs on ubuntu-latest with if: github.event_name == 'pull_request'
  • Declares permissions: pull-requests: read (job-scoped, required by paths-filter v4)
  • Emits outputs.code: ${{ steps.filter.outputs.code }}
  • Contains a single uses: dorny/paths-filter@v4 step with id: filter and NO actions/checkout
  • Defines a positive code filter covering **/*.ts, **/*.tsx, **/*.js, **/*.json, **/*.yaml, **/*.yml, apps/**, packages/**, pnpm-lock.yaml, Dockerfile, docker-compose*.yml

The api and harness jobs were modified:

  • Added needs: [changes]
  • Changed if from github.event_name == 'pull_request' to github.event_name == 'pull_request' && needs.changes.outputs.code == 'true'
  • Updated inline comment to say "skipped for doc-only PRs"
  • All services, env, and step bodies left unchanged

Task 2 — gate aggregate job:

A new gate job was appended at the end of ci.yml:

  • runs-on: ubuntu-latest
  • needs: [fast-checks, changes, api, harness]
  • if: always() — ensures the job reports regardless of upstream outcome
  • One step running a bash script that:
    1. Fails (exit 1) if needs.fast-checks.result is not success (fast-checks always runs)
    2. Iterates over needs.api.result and needs.harness.result, failing if either is not success or skipped
    3. Echoes "Gate passed." otherwise
  • Uses individual needs.X.result references (not contains(needs.*.result,...)) due to Gitea 1.26.2 bug #31007

Decisions Made

  • D-15-02-CHANGES-JOB: Positive code filter chosen (not a docs exclusion filter) so any new or ambiguous file type defaults to the full gate. A doc-only PR must have every changed file fall outside the code patterns.
  • D-15-02-GATE-INDIVIDUAL: Individual needs.X.result checks used instead of contains(needs.*.result, ...) wildcard — Gitea 1.26.2 issue #31007 confirms the wildcard returns false even when jobs succeed.
  • D-15-02-GATE-ALWAYS: if: always() on the gate job prevents deadlock when upstream jobs are skipped. The deadlock bug (Gitea #27906) was fixed in 1.21.8; this instance runs 1.26.2.

Deviations from Plan

None — plan executed exactly as written.

Threat Model Coverage

Threat Mitigation Status
T-15-04: gate passes when fast-checks fails gate exits 1 on fast-checks != success; skipped accepted only for api/harness Implemented
T-15-05: code PR misclassified doc-only positive code filter — any ambiguous file matches code and runs full gate Implemented
T-15-06: supply chain via dorny/paths-filter@v4 pinned to @v4 tag; pull-requests:read only; no secrets access Implemented
T-15-07: required check deadlock gate uses if:always(); api/harness NOT added as required checks (Plan 03 scope) Implemented

Known Stubs

None — this plan produces only CI workflow YAML. No runtime state or UI involved.

Threat Flags

None — no new network endpoints, auth paths, file access patterns, or schema changes.

Verification

Static (pre-merge):

  • Python yaml.safe_load parses ci.yml without error
  • changes job present with dorny/paths-filter@v4, id: filter, permissions: pull-requests: read, outputs.code
  • No actions/checkout step in changes job
  • api and harness have needs: [changes] and combined if with needs.changes.outputs.code == 'true'
  • gate job present with if: always() and needs: [fast-checks, changes, api, harness]
  • needs.fast-checks.result, needs.api.result, needs.harness.result individually referenced in gate
  • No contains(needs.*.result wildcard in file

Behavioral (post-merge, per 15-VALIDATION.md):

  • Doc-only PR: changes emits code=false; api/harness show skipped; fast-checks runs; gate passes
  • Code PR: all three run; gate passes when green
  • Gate-fail path: failing fast-checks causes gate to exit 1

Self-Check: PASSED

  • .gitea/workflows/ci.yml modified: EXISTS
  • Commit 7260438 (Task 1): FOUND
  • Commit 547b12c (Task 2): FOUND