Skip to content

ci: skip agent-quality's code steps on a docs-only push - #261

Open
napetrov wants to merge 12 commits into
intel:mainfrom
napetrov:ci-docs-skip
Open

napetrov wants to merge 12 commits into
intel:mainfrom
napetrov:ci-docs-skip

Conversation

@napetrov

@napetrov napetrov commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

In agent-quality.yml, a new push to a PR now decides whether the code steps run from the push itself, not from the whole PR. A docs-only push skips the Docker smoke test steps.

What is skipped

The changes step sits in the external-catalog-smoke job, not in its own job. When code=false, the job's steps after the checkout are skipped (Python setup, install, clone of uxlfoundation/skills, catalog scan, task run, pass-rate check, artifact upload). The job reports success. The benchmark job still runs on every event, since it measures the docs. ci.yml (static, mypy, tests, package-smoke, windows-smoke) is unchanged, so a docs-only push still runs those jobs. See Risks.

Scope of the decision:

  • synchronize (a new push): diffs github.event.before..github.event.after. These are the push's own commits. HEAD is not used, because on pull_request events the checkout is the merge commit.
  • Force push (before is not an ancestor of after), missing commits, an all-zero before, or an empty diff: runs everything.
  • opened, reopened, push to main, workflow_dispatch: unchanged (whole PR, or always on).
  • Docs set (one regex, DOCS in the step env): root Markdown, dokimi/*.md, dokimi/docs/, dokimi/changelog.d/, .github/**.md. Not counted as docs: dokimi/data/ (read at run time), dokimi/tests/, .agent-eval/.
  • --no-renames on both diffs, so a code file renamed to Markdown still counts as code. This also makes the whole-PR decision stricter, only for renames.

Evidence

bash -e run of the extracted step script on a scratch repo with real git (sim/simulate.sh): 19 of 19 pass.

PASS docs-only push on PR with code earlier       want=false got=false
PASS docs-only push, several doc paths            want=false got=false
PASS code push                                    want=true  got=true
PASS mixed docs+code push                         want=true  got=true
PASS force push (before not ancestor)             want=true  got=true
PASS empty push (before == after)                 want=true  got=true
PASS before is all zeros                          want=true  got=true
PASS before commit missing                        want=true  got=true
PASS rename code -> md                            want=true  got=true
PASS data/ markdown (runtime package data)        want=true  got=true
PASS tests/ markdown fixture                      want=true  got=true
PASS .agent-eval markdown                         want=true  got=true
PASS root guide only (AGENTS.md)                  want=false got=false
PASS PR opened, docs-only PR vs main              want=false got=false
PASS PR opened, code commits vs main              want=true  got=true
PASS whole PR of docs-only push (old rule)        want=true  got=true
PASS push to main (not a PR)                      want=true  got=true
PASS workflow_dispatch                            want=true  got=true

Static: zizmor 1.31.1 (the pinned version) --offline --min-severity medium: no findings. actionlint 1.7.7.23: clean.

Self-review (pr-review local mode, lane L1): no confirmed blocking findings. Run by hand in one pass; no subagent tool was available.

Risks

  • Branch protection: the branch is not protected, and the rulesets have no required status checks, so a skipped job blocks no merge.
  • The job event is not verified on a live run. github.event.before and after on a synchronize event are assumed from the GitHub payload. If either is missing, the fallback runs the full job, so this fails safe.
  • A docs-only push after main has moved is not re-tested by the smoke job against the new base. The push to main still runs the job on the merged result.
  • ci.yml's code jobs (test matrix, mypy, package-smoke, windows-smoke) still run on every push. Gating them is a separate change.

🤖 Generated with Claude Code

@codecov-commenter

codecov-commenter commented Oct 28, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.65%. Comparing base (e64b192) to head (25d3953).
⚠️ Report is 4 commits behind head on main.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #261      +/-   ##
==========================================
+ Coverage   93.59%   93.65%   +0.05%     
==========================================
  Files         277      277              
  Lines       35878    35942      +64     
  Branches     6085     6091       +6     
==========================================
+ Hits        33580    33661      +81     
+ Misses       1484     1469      -15     
+ Partials      814      812       -2     

see 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@napetrov
napetrov marked this pull request as ready for review October 29, 2026 06:15

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The push-scoped decision is sound, and every later step is still gated on the output. One thing is worth fixing before merge: a docs-only push can cancel the smoke run of the code push before it and then skip it (inline).

What I ran, on a Linux review host at d01b50a:

  • make ci-local: 27 passed, 1 failed, 7 skipped; 9,298 tests passed. The one FAIL is pip-audit, which lists filelock advisories in the tool lock files; this PR touches only agent-quality.yml. The Docker smoke job is skipped locally.
  • zizmor 1.31.1 on the file: no findings, same as at the base.
  • I did not execute the changes script itself; the shell behaviour below comes from reading it.

Waits for the author: the commit has no Signed-off-by line, and ci-local does not run the DCO check. @napetrov needs to sign off d01b50a (git commit -s --amend) before merge.

Checked, no issues

  • Gating: every step after changes carries if: steps.changes.outputs.code == 'true'; the upload step keeps always() && with it. benchmark has no needs on this job.
  • Injection: EVENT, ACTION, BASE_REF, BEFORE, AFTER and DOCS all reach the script through env; nothing is interpolated into run:.
  • Fail-safe paths: an empty or missing before/after, an unfetched commit, a force push and an empty diff all end in code=true. Under bash -e, the if ! and || {...} forms do not abort the script.

Comment thread .github/workflows/agent-quality.yml Resolved
@vchamarthi

vchamarthi commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 1 issue(s) in this PR.

By severity: 1 medium

Severity Category Location Finding
medium bug agent-quality.yml:94 Must-fix: per-push scoping can leave code unchecked.
Review details
  • Commit reviewed: 0b6686d against merge base f0f95a2
  • Coverage: 1 files in 1 groups
  • Engine: OpenCodeReview v1.13.3, us.anthropic.claude-sonnet-5-5
  • Usage: 25k tokens (7k from cache), 26s
  • Tool calls: 2 (1 code_comment, 1 file_read)
  • .github/workflows/agent-quality.yml: 1 file

Comment thread .github/workflows/agent-quality.yml Outdated
&& git merge-base --is-ancestor "$BEFORE" "$AFTER"; } 2>/dev/null; then
set_code true "force push, or the commits are not fetched"; exit 0
fi
scope=push

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · high
Must-fix: per-push scoping combined with cancel-in-progress: true can leave code untested. Push A (code) starts the smoke test. Push B (docs only) cancels A's run (same PR concurrency group). B's run sees only .md changes in A..B, skips every step and reports success. The PR head then has a green External catalog smoke test although the code change from A was never verified. The job doesn't run again unless something else triggers it. Decide the scope from the whole PR (base...HEAD) for every PR event. Alternatively, keep the per-push scope but set cancel-in-progress to false for PRs. Either way, a docs-only push must not skip when an earlier push's code run was cancelled or failed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting on a decision before I change this: how to stop a docs-only push from masking the smoke run of an earlier code push. Context: with scope=push and cancel-in-progress: true for pull_request, docs push B cancels code run A and then skips every code step, so the head shows green with the code never verified (and a red A is replaced by a green B). Options: (1) decide from the whole PR (base...HEAD) on every PR event: closes it, but a docs push after a code push runs the smoke test again, which is what this PR set out to avoid. (2) cancel-in-progress false for PRs: B no longer cancels A, but a skipped green B still replaces a red A on the head. (3) keep the push scope, but skip only when everything since the last commit whose smoke run passed its code steps (Verify pass rate succeeded, read with the Actions API, actions: read) is docs-only; a cancelled or failed A then forces B to run. My recommendation: (3), because it is the only one that closes both cases and keeps the savings; it costs a few API reads and a longer changes step. The thread stays open until then. (Same finding as the other thread on this line; one decision answers both.)

The changes step diffed the whole PR (base...HEAD), so one code commit
anywhere in a PR kept every later docs-only push running the Docker smoke
test. A new push (synchronize) now decides from the push itself
(before..after). Every other PR run still decides from the whole PR, and a
push that cannot be diffed (force push, missing commits, empty diff) runs
everything. Renames are diffed with --no-renames so a code file renamed to
Markdown still counts as code.

Documentation is Markdown in the root guides, dokimi/*.md, dokimi/docs/,
dokimi/changelog.d/ and .github/. dokimi/data/ (read at run time), the
tests and .agent-eval/ count as code.

Thread: PRR_kwDOVBg7g88AAAABU5mMWA.b
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov

Copy link
Copy Markdown
Contributor Author

(a) Waiting on a decision before I change this: the cancel-and-skip finding is held, see the ask on PRRT_kwDOVBg7g86xi50L; the three options and my recommendation are there. (b) Sign-off: Fixed in 54b6efb: the PR commit now carries Signed-off-by: Nikolay Petrov. I rewrote that one commit (its content is unchanged) because this repo's DCO app has no .github/dco.yml, so a remediation commit would not clear it; pushed with a lease on the old head. The DCO check pins it.

@napetrov

Copy link
Copy Markdown
Contributor Author

This review has no claim of its own (only the OpenCodeReview run marker); its one finding is the inline thread PRRT_kwDOVBg7g86xlzGd, answered there.

@napetrov

Copy link
Copy Markdown
Contributor Author

Nothing to change here: the report says all modified lines are covered; installing the Codecov app is an organization setting.

@napetrov

Copy link
Copy Markdown
Contributor Author

The one issue counted here is the inline thread PRRT_kwDOVBg7g86xlzGd (cancel then skip); it is held for a decision there. No separate change.

set_code() { echo "code=$1" >> "$GITHUB_OUTPUT"; echo "changes: code=$1 ($2)"; }
if [ "$EVENT" != pull_request ]; then set_code true "not a pull request"; exit 0; fi
# --no-renames: a rename from code to Markdown must count as the code it removes.
if [ "$ACTION" = synchronize ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Must-fix: per-push scoping can leave code unchecked. Push A changes code and starts the smoke test. Push B, docs-only, arrives. The concurrency group has cancel-in-progress for PRs, so it cancels A's run. B's run sees only docs, skips every step and reports success. The head commit is green and the code from A never went through the Docker smoke test. If A's run had already failed, B's green run hides that failure in the same way. Fix: decide from the whole PR (base...HEAD) for every PR event, which is the old behaviour. If you want the per-push skip, apply it only when the previous run for BEFORE succeeded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c1ed98d: option 3. A docs-only push now skips the code steps only when everything since the newest commit whose smoke run passed its code steps is documentation.

What changed in .github/workflows/agent-quality.yml:

  • The changes step diffs from that passed commit, not from before. It finds the commit through the Actions API (actions: read on the smoke job): the newest completed run of this branch whose Verify pass rate step succeeded, reachable from the push.
  • A run that was cancelled by the next push, or that failed, does not count, so the code it carried runs again.
  • With no passed run, or an unreadable run list, everything runs.

Pinned by dokimi/tests/test_agent_quality_changes_scope.py. It runs the step as the workflow has it, with gh stubbed. Before the change, the cancelled-run, failed-run, no-passed-run and unreadable-API cases returned code=false. After it, all six scope cases and the permission check pass, including docs after a passed run (still skips).

The cancellation itself is GitHub's concurrency behaviour; I have not run this on Actions.

The changes step decided a synchronize run from before..after alone. A
docs-only push B cancels an in-flight code run A (same PR concurrency group,
cancel-in-progress), so A's smoke test never finished and B skipped every code
step: the head went green with A's code unverified, and a red A was replaced by
a green B.

Diff from the newest commit whose smoke run passed its code steps instead
(Verify pass rate succeeded, read with the Actions API; the smoke job gets
actions: read). A cancelled or failed run does not count, so its code runs
again. With no passed run, or an unreadable run list, everything runs.

tests/test_agent_quality_changes_scope.py runs the changes step as the workflow
has it, with gh stubbed. Red before this change: the cancelled-run, failed-run,
no-passed-run and unreadable-API cases return code=false, and the permission
check fails. Green after: all six scope cases and the permission check pass.

Review thread on PR 261: .github/workflows/agent-quality.yml:94.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@exai-bot

exai-bot commented Oct 30, 2026 •

Copy link
Copy Markdown

🔍 OpenCodeReview found 2 issue(s) in this PR.

By severity: 1 medium, 1 low

Severity Category Location Finding
medium test test_agent_quality_changes_scope.py:222 Must-fix: the harness enables pipefail, but the workflow has no explicit shell or shell default, so GitHub runs this step with `bash -e...
low maintainability agent-quality.yml:97 Nit: declare shell: bash on the changes step.
Review details
  • Commit reviewed: 25d3953 against merge base e64b192
  • Coverage: 2 files in 2 groups
  • Engine: OpenCodeReview v1.13.3, us.openai.gpt-6.1-sol
  • Usage: 243k tokens (134k from cache), 45s
  • Tool calls: 18 (7 file_read, 6 code_search, 2 code_comment, 2 file_read_diff, 1 file_find)
  • .github/workflows/agent-quality.yml: 1 file
  • dokimi/tests/test_agent_quality_changes_scope.py: 1 file

Comment thread .github/workflows/agent-quality.yml Outdated
# steps below (the Docker smoke test cannot be affected by it); the benchmark
# job measures the docs, so it always runs. The job itself still takes a
# runner, as its steps are skipped, not the job.
# Whether this run changes code (anything but documentation). Code-free runs skip

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style · low
This comment block is about 15 lines. The repo rule (scripts/check_comment_blocks.py) allows blocks of at most two # lines. It fails once a PR adds a block of three or more lines. Condense it to the constraint or reason. Move the scope and docs-set detail to the PR description or dokimi/docs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined: the 21-line block at lines 28-48 is already on intel/main and this PR does not touch it (its hunks are at 51 and 66+). check_comment_blocks.py --base intel/main --head HEAD passes; trimming that block belongs in a separate cleanup.

set_code() { echo "code=$1" >> "$GITHUB_OUTPUT"; echo "changes: code=$1 ($2)"; }
if [ "$EVENT" != pull_request ]; then set_code true "not a pull request"; exit 0; fi
# --no-renames: a rename from code to Markdown must count as the code it removes.
if [ "$ACTION" = synchronize ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Per-push scoping can turn the check green without the code ever being smoke-tested. The concurrency group cancels the in-flight run on each new push. So code push A starts the smoke test, a docs-only push B cancels it, and B's run skips every step and reports success. The head commit then shows a green check although the Docker smoke test never passed on that code. The same happens if A's run had already failed and B is docs-only. Required checks are not enforced today, so this is not blocking, but the result is misleading. Options: keep the whole-PR scope for the smoke job (base...HEAD), or only use push scope when the previous run for BEFORE succeeded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c1ed98 (already on this branch): a docs-only push skips the code steps only after the newest passed smoke run, and cancelled or failed runs do not count. The cancelled, failed, no-passed-run and unreadable-API scenarios in test_agent_quality_changes_scope.py pin it (18 passed on 7d654b3).

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Under bash's pipefail, `printf | grep -qvE` fails when grep exits on the
first code path before printf has written the rest of a long list, so a
code push with many changed paths was read as documentation and skipped
the smoke test. Feed the list to grep as a here-string instead.

A new scope test pins a push with 2000 changed paths (red before the fix).

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov
napetrov requested a review from a team as a code owner October 30, 2026 05:28
Comment thread .github/workflows/agent-quality.yml Outdated
# Newest commit reachable from AFTER whose smoke run passed (Verify pass rate succeeded); prints nothing if none.
# An unreadable run list also prints nothing; the caller then runs everything.
last_verified() {
runs=$(gh api "repos/$REPO/actions/workflows/agent-quality.yml/runs?branch=$HEAD_REF&event=pull_request&status=completed&per_page=20" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · low
HEAD_REF goes into the URL query without encoding. Git allows branch names with &, # or +, and these would change or cut off the query. For example, a&per_page=1 or a#b would filter on the wrong branch, or on no branch. The result is a wrong or empty run list. An empty list falls back to code=true, so this is not unsafe, but it disables the optimization for those branches. Passing the parameters with -f lets gh encode them (on a GET request they are sent as query parameters).

Suggestion:

Suggested change
runs=$(gh api "repos/$REPO/actions/workflows/agent-quality.yml/runs?branch=$HEAD_REF&event=pull_request&status=completed&per_page=20" \
runs=$(gh api "repos/$REPO/actions/workflows/agent-quality.yml/runs" \
-f branch="$HEAD_REF" -f event=pull_request -f status=completed -f per_page=20 \

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5d22ccb, unpushed: the runs query sends the branch as a GET field, so gh encodes &, # and +. The new test uses a&per_page=1#b+c, and the old URL form fails it (1 failed).

Comment thread .github/workflows/agent-quality.yml Outdated
|| { set_code true "PR diff failed"; exit 0; }
fi
if [ -z "$files" ]; then set_code true "$scope diff is empty"; exit 0; fi
# A here-string, not a pipe: under pipefail, grep -q exiting early on a long list

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maintainability · low
This step has no shell: key, so the runner uses bash -e {0}, without pipefail. The comment gives a reason that does not apply here, although the here-string is still harmless. Separately, git diff --name-only quotes non-ASCII paths (core.quotepath), so a doc file such as "docs/é.md" ends in " and does not match DOCS. It is then counted as code. That fails safe, but you can avoid it with git -c core.quotepath=false diff ... in both diffs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 384ea5e and f5e352a, unpushed: core.quotepath=false makes non-ASCII doc names match DOCS, and the here-string comment now states the real reason (this step runs without pipefail). Dropping the quotepath option fails 2 tests.

The changes step put HEAD_REF into the URL query unencoded, so a branch
name with & # or + changed or cut off the query and listed the wrong runs.
Pass the fields with -f and --method GET: gh turns -f fields into a POST
unless the method is GET. The test fails on the old URL and passes now.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…step

git quotes a non-ASCII path in --name-only output unless core.quotepath is
off. A quoted "docs/é.md" does not match the DOCS pattern, so a docs-only
change was read as code. It failed safe, but ran the smoke test for nothing.
Both diffs now run with core.quotepath=false. The tests fail on the old
diffs and pass now.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The step has no shell key, so it runs bash -e without pipefail. The comment
gave a pipefail reason that does not apply to it. The here-string is still
harmless, so only the comment changes.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 9c88ee4. The push-scoped decision now diffs from the last commit whose smoke run passed Verify pass rate, which closes the cancel-then-skip case from my earlier thread. One thing is worth fixing before merge: the new tests do not pin the docs set or the rename handling, and those decide whether the smoke test is skipped.

What I ran, on a Linux review host at 9c88ee4:

  • make ci-local: 29 passed, 0 failed, 7 skipped (the Docker smoke job is among the skips). The 9 new tests pass, and the comment-block check passes.
  • The changes step script, executed against a scratch git repo with a stubbed gh, under bash -e and bash -eo pipefail: cancelled, failed, skipped and unreadable-run cases all end in code=true. A passed run on a commit that is not an ancestor of the push is ignored. Output is the same under both shells.
  • A mutation run on the step: 39 mutants, 30 of which pass the new test file.

Checked, no issues

  • Masking: only a Verify pass rate conclusion of success counts as verified. A skipped docs-only run does not, so it cannot hide a failed or cancelled one.
  • Fail-safe paths: an unreadable run list, an unreadable jobs call, no passed run, a force push and an empty diff all give code=true.
  • Long file lists: the here-string handles 2000 changed paths under pipefail.
  • Permissions: actions: read is added to the smoke job only, and a test pins it.
  • Commits: every non-merge commit carries Signed-off-by.


# (history after base, runs newest first as (commit, Verify pass rate conclusion) or None
# for an unreadable API, before, after, the code output expected)
SCENARIOS = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests do not pin the docs set or the rename handling, so loosening either silently skips the smoke test. Every scenario uses README.md as the only docs path and src/app.py as the only code path, on a linear history with no renames. Mutation checks on the changes step, each of which passes all 9 tests:

  • DOCS ending in \.[a-z]+$ instead of \.md$ (any extension counts as docs). Unanchoring the end, allowing nested paths in dokimi/changelog.d/, and dropping dokimi/docs/ or .github/ also pass.
  • Removing --no-renames from both diffs. A src/app.py to README.md rename then lists only README.md, so code is read as docs.
  • Dropping the git merge-base --is-ancestor "$sha" "$AFTER" check in last_verified. After a rebase, an older passed run on a non-ancestor commit is then accepted as the diff base.

Each of these ends in code=false for a push that should run the smoke test.

Suggestion: add scenarios for (1) a push touching dokimi/data/x.md, dokimi/tests/x.md and a .py file next to a .md, expecting true; (2) a push touching dokimi/docs/sub/a.md and dokimi/changelog.d/1.md, expecting false; (3) a git mv src/app.py README.md commit, expecting true; (4) a passed run on a commit outside the history of after, expecting true.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7d654b3: scenarios now cover a non-Markdown file under docs/, Markdown under data/ and tests/, docs/ at depth, a flat and a nested changelog.d entry, a code-to-docs rename and a passed run off the push's history. Each of the six mutants in your list now fails at least one test.

…nges step

The scope tests used README.md as the only docs path and a linear history
with no renames, so loosening the DOCS pattern, dropping --no-renames or the
ancestor check in last_verified left them green. Add scenarios for each:
a non-Markdown file under docs/, Markdown under data/ and tests/, docs/ at
depth, a changelog.d entry one level down, a code-to-docs rename, and a passed
run on a commit outside the push's history.

Each of the six mutants fails at least one test; the workflow is unchanged.

Thread: PRRT_kwDOVBg7g86x68oj
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
from=$(last_verified)
if [ -z "$from" ]; then set_code true "push: no passed smoke run to diff from"; exit 0; fi
scope="push since ${from:0:7}"
files=$(git -c core.quotepath=false diff --no-renames --name-only "$from" "$AFTER") \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
Must-fix: this diff ignores changes to the PR's base branch since the successful smoke run. Checkout uses the default PR merge commit, so if V passes against main=M1, main then receives a code change M2, and the PR pushes only README.md, V..AFTER contains only docs and this job skips testing the new merge with M2. Head ancestry alone does not establish that the checked-out code was verified. Record/validate the base revision (or compare the previously tested merge tree with the current checkout) before allowing the skip, and add a scenario where the base changes between the successful run and the docs-only push.

def _runs_request(fake: Path) -> tuple[str, dict[str, list[str]]]:
"""The method and the query of the runs request: the URL's own query, plus the -f fields."""
args = (fake / "runs-request").read_text(encoding="utf-8").splitlines()
method = args[args.index("--method") + 1] if "--method" in args else "GET"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test · medium
Must-fix: this fallback does not match gh api, which defaults to POST when -f/-F fields are supplied. Removing --method GET from the workflow therefore still passes the branch-query test: the stub serves the fixture regardless of method, and this helper reports GET. Infer POST when field arguments are present (unless an explicit method overrides it), so the test catches that broken runs-list request.

Suggestion:

Suggested change
method = args[args.index("--method") + 1] if "--method" in args else "GET"
field_flags = ("-f", "-F", "--raw-field", "--field")
default_method = "POST" if any(flag in args for flag in field_flags) else "GET"
method = args[args.index("--method") + 1] if "--method" in args else default_method

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
_history had one if/elif chain over the change kinds, complexity 13 over
the 10 cap with no baseline entry. Each kind is now a writer function in a
dispatch table, the plain edit is _write_change, and the commit step is
_commit. Same files, same order, same commits; the 18 tests are unchanged.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
verdict=$(gh api "repos/$REPO/actions/runs/$id/jobs" \
--jq '.jobs[] | select(.name | startswith("External catalog smoke test")) | .steps[] | select(.name == "Verify pass rate") | .conclusion') || continue
if [ "$verdict" = success ]; then echo "$sha"; return 0; fi
done <<< "$runs"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maintainability · low
Nit: declare shell: bash on the changes step. The new here-strings and ${from:0:7} expansion require Bash, but this step also runs on the self-hosted dokimi-ec2 runner without an explicit shell. GitHub's unspecified Linux shell can fall back to sh, which cannot parse these constructs. The new tests explicitly invoke Bash, so they do not cover that fallback.

"PATH": f"{fake}{os.pathsep}{os.environ['PATH']}",
}
proc = subprocess.run(
["bash", "--noprofile", "--norc", "-eo", "pipefail", "-c", step["run"]],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test · medium
Must-fix: the harness enables pipefail, but the workflow has no explicit shell or shell default, so GitHub runs this step with bash -e without pipefail. This makes the bulk-path scenario reject a valid printf '%s\n' "$files" | grep -qvE "$DOCS" implementation: an early grep exit can give printf SIGPIPE, making the pipeline false only in the test. Run with the workflow's actual shell flags (or explicitly enable the same flags in the workflow) so these tests exercise CI's error handling.

Suggestion:

Suggested change
["bash", "--noprofile", "--norc", "-eo", "pipefail", "-c", step["run"]],
["bash", "--noprofile", "--norc", "-e", "-c", step["run"]],

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants