Skip to content

docs: repoint docs and code comments at the files the ts split moved #2647

docs: repoint docs and code comments at the files the ts split moved

docs: repoint docs and code comments at the files the ts split moved #2647

Workflow file for this run

name: 🔎 Quality
# ──────────────────────────────────────────────────────────────────────
# Advisory quality + perf (consolidated from quality/rust-quality/benchmark).
# None blocks merge.
# JS/docs (PR): typos · links (lychee) · knip (dead code) · i18n key drift · jsx-a11y
# Rust (PR): cargo-hack (each-feature) · cargo-mutants (--in-diff)
# Perf : criterion export-render benchmark — records the baseline on
# push-to-main (Rust changes only); never runs on PRs — opt in
# per branch via workflow_dispatch.
# The benchmark job is the ONLY one with elevated permissions (contents: write +
# the deploy key + the Pages PAT); per-job `permissions:`/`if:` keep every other
# job least-privilege and PR-scoped. A `changes` gate preserves the Rust-only
# filter for benchmark.
# ──────────────────────────────────────────────────────────────────────
on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
# File-level least privilege; only the benchmark job widens this.
permissions:
contents: read
concurrency:
group: quality-${{ github.ref }}
cancel-in-progress: true
jobs:
# Path gate so the perf benchmark only runs when Rust actually changed
# (mirrors the former benchmark.yml paths filter, now per-job).
changes:
name: 🧭 Detect Changes
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
outputs:
rust: ${{ steps.filter.outputs.rust }}
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🧭 Path filter
id: filter
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
with:
filters: |
rust:
- 'apps/desktop/src-tauri/**'
- '.github/workflows/quality.yml'
# ── JS / docs quality (PR + manual; never on push-to-main) ───────────────────
typos:
name: 🔤 Typos
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🧰 Install typos
id: install-typos
continue-on-error: true
uses: taiki-e/install-action@4cef1412cce204788f482e778a0b9187f9626a29 # v2
with:
tool: typos
# Advisory: run typos ourselves so the findings land in the job Summary
# (the crate-ci action only emits inline annotations). Never fails the job.
- name: 🔤 Spell check (advisory)
continue-on-error: true
run: |
set +e
if ! command -v typos >/dev/null 2>&1; then
{
echo "## 🔤 Typos"
echo "⚠️ typos binary unavailable — check skipped."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
typos --format brief > "$RUNNER_TEMP/typos.txt" 2>&1
code=$?
{
echo "## 🔤 Typos"
if [ "$code" -eq 0 ]; then
echo "✅ No typos found."
else
echo "Advisory — does not block merge. Possible typos:"
echo ""
echo '```'
cat "$RUNNER_TEMP/typos.txt"
echo '```'
fi
} >> "$GITHUB_STEP_SUMMARY"
exit 0
links:
name: 🔗 Link Check
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🔗 Lychee link check (advisory)
uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0
with:
args: "--no-progress 'README.md' 'docs/**/*.md'"
fail: false
knip:
name: 🧹 Knip (dead code)
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 📦 Setup Node + pnpm
uses: ./.github/actions/setup-node-pnpm
# Advisory: knip's markdown reporter writes a readable table to the job
# Summary. Never fails the job.
- name: 🧹 Run knip (advisory)
continue-on-error: true
run: |
set +e
pnpm exec knip --reporter markdown > "$RUNNER_TEMP/knip.md" 2>&1
code=$?
{
echo "## 🧹 Knip (dead code)"
if [ "$code" -eq 0 ]; then
echo "✅ No dead code found."
else
echo "Advisory — does not block merge."
echo ""
cat "$RUNNER_TEMP/knip.md"
fi
} >> "$GITHUB_STEP_SUMMARY"
exit 0
i18n:
name: 🌐 i18n Keys
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 📦 Setup Node + pnpm
uses: ./.github/actions/setup-node-pnpm
# i18next-cli rewrites the locale files from t() usage; a non-empty diff
# means missing keys (or sort drift). Advisory: report, then restore.
- name: 🌐 Check translation keys (advisory)
continue-on-error: true
run: |
pnpm --filter @ajh/translations i18n:extract
if git diff --quiet -- packages/translations/src/locales; then
echo "Locale files are in sync with t() usage."
else
echo "::warning::i18next-cli would change the locale files (missing keys / sort drift):"
git --no-pager diff -- packages/translations/src/locales
git checkout -- packages/translations/src/locales
fi
a11y-lint:
name: ♿ a11y Lint
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 📦 Setup Node + pnpm
uses: ./.github/actions/setup-node-pnpm
# jsx-a11y via a SEPARATE config so the strict main lint is unaffected.
# Advisory: capture the ESLint report into the job Summary. Never fails.
- name: ♿ ESLint jsx-a11y (advisory)
continue-on-error: true
run: |
set +e
pnpm exec eslint --config eslint.a11y.config.mjs 'apps/desktop/src/renderer/**/*.tsx' 'packages/ui/src/**/*.tsx' -f stylish > "$RUNNER_TEMP/a11y.txt" 2>&1
code=$?
{
echo "## ♿ a11y Lint (jsx-a11y)"
if [ "$code" -eq 0 ]; then
echo "✅ No accessibility problems."
else
echo "Advisory — does not block merge."
echo ""
echo '```'
cat "$RUNNER_TEMP/a11y.txt"
echo '```'
fi
} >> "$GITHUB_STEP_SUMMARY"
exit 0
# ── Non-Linux compile checks (PR + manual; never on push-to-main) ───────────
# Every other Rust job runs on ubuntu-latest, and before these jobs `macos-`
# and `windows-` appeared in no workflow but release.yml. So any code behind a
# macOS- or Windows-specific cfg compiled NOWHERE until a release was cut,
# which is the most expensive place to find a break. Cargo.toml already
# carries a comment about being burned by exactly this.
#
# Precisely: `cfg(unix)` / `cfg(not(windows))` branches ARE already compiled by
# the ubuntu jobs — the gap was only ever `#[cfg(target_os = "macos")]`,
# `#[cfg(windows)]` and other platform-exclusive predicates.
#
# Check only — no bundling, no tests — so these stay cheap (the macOS runner
# bills at 10x, Windows at 2x).
cargo-check-macos:
name: 🍎 macOS compile check
if: github.event_name != 'push'
runs-on: macos-latest
timeout-minutes: 30
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 🦀 cargo check (all targets)
working-directory: apps/desktop/src-tauri
run: cargo check --all-targets --locked
cargo-check-windows:
name: 🪟 Windows compile check
if: github.event_name != 'push'
runs-on: windows-latest
timeout-minutes: 30
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 🦀 cargo check (all targets)
working-directory: apps/desktop/src-tauri
run: cargo check --all-targets --locked
# This job is `check` only (see the comment above), so `std::fs::rename`
# over an EXISTING file (postings::InteractionStore::save's write-then-
# rename) was never actually EXECUTED on Windows — rename-over-existing
# has different underlying semantics per platform (MoveFileExW needs
# MOVEFILE_REPLACE_EXISTING; POSIX rename(2) replaces unconditionally),
# and this repo has a recorded history of platform-exclusive code
# compiling nowhere until release. One narrowly-targeted `cargo test`,
# not the whole suite — the point is this ONE rename call, not Windows
# test coverage generally, which would cost real runner minutes (billed
# at 2x) for no new signal on everything else already proven on Linux.
- name: 🪟 Atomic file-replace test (Windows rename-over-existing)
working-directory: apps/desktop/src-tauri
run: cargo test --lib postings::interactions::tests::persistence::save_replaces_the_file_atomically_and_leaves_no_temp_file --locked
# ── MCP stdio smoke (PR + manual; never on push-to-main) ─────────────────────
# ADR-040 named macOS/Linux smoke of `ajh-tauri agent mcp` as its open gap.
# LINUX IS NOT IN THIS MATRIX ON PURPOSE: `tests/mcp_smoke.rs` is an ordinary
# integration test, so the gating ubuntu `tests` job in ci-pipeline.yml
# already runs it. This job adds the two platforms nothing else executes on.
#
# Unlike the two compile-check jobs above, this one LINKS the binary
# (`CARGO_BIN_EXE_ajh-tauri`), which is why it gets its own timeout rather
# than sharing theirs. It needs no frontend dist: a debug build is dev-mode,
# and tauri-codegen embeds empty assets when a devUrl is configured.
#
# A debug binary is console-subsystem, so this does NOT prove the release
# Windows GUI-subsystem stdout path (`platform/windows_console.rs`) — the
# stdio discipline it proves is the console-subsystem one.
#
# ADVISORY, like every other extra-signal step in this workflow: a real
# regression here fails the gating ubuntu `tests` job too (same test file),
# so blocking a PR on the two EXTRA platforms would buy nothing but flake —
# this job spawns a child process and drives it over pipes, on the runners
# with the least predictable process timing.
mcp-smoke:
name: 🔌 MCP stdio smoke (${{ matrix.os }})
if: github.event_name != 'push'
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 🔌 Scripted MCP stdio sessions (advisory)
continue-on-error: true
working-directory: apps/desktop/src-tauri
run: cargo test --test mcp_smoke --locked
# ── Rust test-quality (PR + manual; never on push-to-main) ───────────────────
cargo-hack:
name: 🦀 Feature combinations
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 🧰 Install cargo-hack
uses: taiki-e/install-action@4cef1412cce204788f482e778a0b9187f9626a29 # v2
with:
tool: cargo-hack
- name: 🦀 Check each feature (advisory)
continue-on-error: true
working-directory: apps/desktop/src-tauri
run: cargo hack --each-feature check --all-targets
cargo-mutants:
name: 🦀 Mutation testing (PR diff)
if: github.event_name != 'push'
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: 📥 Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # full history so the merge-base diff for --in-diff resolves
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 🧰 Install cargo-mutants
uses: taiki-e/install-action@4cef1412cce204788f482e778a0b9187f9626a29 # v2
with:
tool: cargo-mutants
- name: 🦀 Mutation testing on the diff (advisory)
continue-on-error: true
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "Manual run — full-crate mutation testing is slow; this job only runs --in-diff on PRs."
exit 0
fi
git -C "$GITHUB_WORKSPACE/apps/desktop/src-tauri" diff --relative "$BASE_SHA...HEAD" > "$RUNNER_TEMP/mutants.diff" || true
if [ ! -s "$RUNNER_TEMP/mutants.diff" ]; then
echo "No Rust changes under apps/desktop/src-tauri in this PR — skipping mutation testing."
exit 0
fi
cd apps/desktop/src-tauri
cargo mutants --in-diff "$RUNNER_TEMP/mutants.diff" --timeout 180 || true
- name: 📊 Upload mutants report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: mutants-${{ github.run_id }}
path: apps/desktop/src-tauri/mutants.out/
retention-days: 7
if-no-files-found: ignore
# ── Perf benchmark — push-to-main baseline; opt-in elsewhere ─────────────────
# The ONLY elevated job: commits the refreshed dashboard back to main (deploy
# key) and kicks the Pages deploy (PAT). Runs on push-to-main when Rust changed
# (the `changes` gate) and on manual dispatch. NEVER on pull_request events —
# to benchmark a PR, run this workflow via workflow_dispatch on the PR branch
# (Actions → Quality → Run workflow).
benchmark:
name: 📈 Export render
needs: changes
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && needs.changes.outputs.rust == 'true')
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write # commit benchmark history to apps/landing/public/benchmarks on main (push runs)
pull-requests: write # comment on a PR when a result regresses
steps:
- name: 🛡️ Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
# Full history + the release deploy key so the push-to-main run can
# commit the refreshed dashboard back to main. PR runs check out WITHOUT
# the key (and without persisted credentials): `cargo bench` executes PR
# code, which must never see a secret that can push to main.
- name: 📥 Checkout Repository (push/dispatch — with deploy key)
if: github.event_name != 'pull_request'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }}
- name: 📥 Checkout Repository (PR — no credentials)
if: github.event_name == 'pull_request'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: 🦀 Setup Rust (Tauri)
uses: ./.github/actions/setup-rust
- name: 📈 Run export benchmarks
working-directory: apps/desktop/src-tauri
run: cargo bench --bench export_render --locked -- --output-format bencher | tee output.txt
- name: 📊 Store result & alert on regression (advisory)
uses: benchmark-action/github-action-benchmark@4322e5726e6334590d251fc4f92bec0efafc45dc # v1.22.2
continue-on-error: true
with:
name: Export render
tool: cargo
output-file-path: apps/desktop/src-tauri/output.txt
github-token: ${{ secrets.GITHUB_TOKEN }}
gh-pages-branch: main
benchmark-data-dir-path: apps/landing/public/benchmarks
skip-fetch-gh-pages: true
auto-push: false
# Bound the series. Without this the action appends forever: 199 entries
# had accumulated over 76 days (~2.6/day at ~7.7 KB each, because every
# entry embeds a full commit object for three benches), making data.js
# 1.5 MB and growing ~20 KB/day — and it is committed to main on every
# run, so the cost compounds in git history rather than replacing.
#
# 100 entries is ~5 weeks at the current cadence, far more resolution
# than the /mission-control sparkline or the regression alert needs,
# and it halves the file on the next run. The alert compares against the
# previous entry, so trimming old points cannot weaken it.
max-items-in-chart: 100
comment-on-alert: true
alert-threshold: '150%'
fail-on-alert: false
alert-comment-cc-users: '@saeedkolivand'
- name: 📤 Commit benchmark data
id: commit
if: github.event_name != 'pull_request'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if [ -z "$(git log origin/main..HEAD --oneline)" ]; then
echo "Benchmark data unchanged — nothing to push."
exit 0
fi
git commit --amend -m "chore: update export benchmark data [skip ci]"
remote="git@github.com:saeedkolivand/ai-job-hunter-app.git"
# origin/main can advance during the ~16-min bench run, so a plain push
# races and is rejected. Rebase the single [skip ci] benchmark commit
# onto the latest origin/main and retry the push a few times.
for attempt in 1 2 3 4 5; do
git fetch origin main
if ! git rebase origin/main; then
git rebase --abort || true
echo "::error::benchmark commit failed to rebase onto origin/main"
exit 1
fi
if git push "$remote" HEAD:main; then
echo "committed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "push rejected (attempt $attempt/5) — origin/main moved, retrying after backoff"
sleep $((attempt * 5))
done
echo "::error::failed to push benchmark data to main after 5 attempts"
exit 1
- name: 🌐 Trigger Pages deploy
if: steps.commit.outputs.committed == 'true'
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: gh workflow run pages.yml --ref main