Skip to content

feat(timeouts): report how long jobs really took in their latest runs (--suggest-timeouts) - #275

Merged
jdx merged 5 commits into
mainfrom
feat/timeout-history
Oct 14, 2026
Merged

jdx merged 5 commits into
mainfrom
feat/timeout-history

Conversation

@jdx

@jdx jdx commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Closes #121 (PR 4 of 4, so this is the one that closes it once the earlier ones are merged: #269, #272, #274). It is independent of the others, except that all four edit docs/usage.md, man/jactionlint.1.ronn and command_options.go, so expect trivial merge conflicts there.

This is the "Later" item "historical timeout recommendations, report-only" (ghatm is the precedent). The issue asks for: sample count, date range, job/matrix matching limitations and a suggested buffer; review before applying a value; historical maxima are not guarantees. All of them are in the output.

What it does

jactionlint --suggest-timeouts (implies --online; --timeout-history-runs N, default 20, 1 to 100; --timeout-buffer FACTOR, default 2, at least 1) reads, for each workflow, the latest successful runs and the jobs of each run, and prints per job: declared timeout-minutes, samples, min, median, max (minutes, from the start on a runner to the end, so the wait for a runner is not in it) and a suggestion. --format json prints the same as a versioned document; other formats are refused.

  • Report only. It writes no file and no rule or fix uses its numbers: missing-timeout still never chooses a value (the CONTRIBUTING rule), and default-minutes stays the person's decision. The output ends by saying so, and tells the person the largest suggestion, which is the least that one default-minutes can be without cancelling the slowest job on record.
  • The number is ceil(max x buffer) for a job with at least 3 samples. Below that, no number, and the note says why.
  • What it states it cannot know: how many runs and which dates (per workflow) and how many samples (per job); runs that failed, were cancelled or were skipped are left out; a matrix is one job whose legs share the timeout (the suggestion is for the slowest leg, the legs are counted); a call of a reusable workflow has no numbers (the timeouts belong to the jobs of the called workflow); job names of the runs that fit no job of the file, or fit two, are listed as "not matched" and are in no number; runs with more than 100 jobs are read in part and the report says so; a lookup that failed (rate limit, 403) makes the report complete: false with the count, and --online=strict fails on it like the other online checks; "runs may predate the current file".
  • Matching is by name: the name: (an expression in it matches any text), or the ID, with the matrix values in parentheses that GitHub adds (Unit tests (macos-latest) for name: Unit tests with a matrix; I found that rule by running it against the real API, the first version missed it).

Plumbing

A new optional GitHubRunsClient interface (WorkflowRuns, RunJobs) on the HTTP client (so the cache, ETags, rate limit handling, redaction and the token rules apply as for every other lookup), the fixture client (workflow_runs in the fixtures JSON), and the GitHub Enterprise fallback wrapper (which asks the Server only, like environments: the repository of the workflow is on the Server). The recording client does not record runs. Names are validated before they are put in a path (validWorkflowFile). The repository is GITHUB_REPOSITORY or the origin remote on github.com; githubRepositoryOf now also asks git config when .git is a file (linked work trees and submodules, where it returned nothing; the environment rule uses the same function).

Testing

go vet, go test -race ./..., hk check --all pass locally; the WebAssembly build still compiles. Tests: the name matching (matrix, named, expression names, reusable calls), a fake API with the exact paths and query parameters (status=success, per_page), the numbers (min/median/max, the 8-sample matrix, a failed and a skipped execution that are not samples, 2 samples that get no suggestion, a job that never ran, a call of a workflow, an unmatched name, the buffer), the text, the JSON, an unknown workflow (404), an incomplete run (403 for one run, and --online=strict), the option errors, the fixture client, and that the workflow file is not touched.

Real API: I ran it against the history of jdx/jactionlint itself (the 13 workflows of the repository, 6 runs each and then 20 for two of them, about 100 requests with a token) and read the output: the numbers are plausible, a workflow that never succeeded recently says "no successful run on record", the reusable-workflow call has no numbers, and the matrix of ci.yaml is one job with 3 legs and 60 samples.

Not done / not measured

  • It does not follow a reusable workflow into the workflow it calls (it would need the callee's runs, which GitHub lists under the caller's jobs as caller / job); the note says so.
  • Pull-request runs from forks, re-runs and runs of other branches are not told apart; "successful runs of any event and branch" is what is read.
  • No rate-limit planning beyond what the client has: 1 + N requests per workflow, cached for the hour. Without a token GitHub allows 60 an hour (the docs say to give one).
  • The JSON document has no JSON Schema file (the online report has none either); its fields are described in docs/usage.md.

What the series did for #121 (so it can close)

Read against the issue body and the maintainer's comment ("items 1 to 4 done; open: item 5 and the Later list"):

Item State
1 caller-aware facts #113 to #118 done earlier; the facts are now exported in one place (#272) with the audit of where they are computed more than once; I did not merge the four token-permission readers (see #272)
2 no-fix reasons JSON/SARIF done earlier; #269 closes the gap (11 rules had findings with neither a fix nor a reason), adds --show-fix-status and the prerequisites in --explain
3 workflow-secret-scope done (#136)
4 source invariants done (#137, #146, #157); #272 and #269 add their commands to the zero-write test
5 online coverage done by #166, merged after the maintainer's comment: --online-report (any format) and SARIF carry status, skipped lookups by kind, excluded repositories, and the freshness of cached answers (oldestFetched, cacheTtlSeconds, counts of network, cache, stale and immutable answers) with the timestamp source documented
Later: actions.lock compatibility done (#237, #228)
Later: incremental reporting #274
Later: installed-version explanations and generated config schema done (--explain #204, --print-config-schema #195)
Later: versioned semantic export #272
Later: historical timeout recommendations this PR

Not done: per-finding freshness (which finding used a cached answer). The cache layer does not know the findings and the rules share memoized answers, so it would be a new tracking path through every online rule for a number the run-level report already bounds; the run-level answers and oldestFetched are what consumers get.

🤖 Generated with Claude Code


Note

Medium Risk
New GitHub Actions API traffic and token permissions (Actions read), plus non-trivial job-name matching logic; behavior is report-only and does not modify workflows or lint results.

Overview
Adds --suggest-timeouts, a read-only report mode that turns on online checks, pulls each workflow’s latest successful runs from GitHub, and prints per-job duration stats (min/median/max) plus optional timeout-minutes suggestions. Suggestions use ceil(max × --timeout-buffer) with at least three samples; --timeout-history-runs (1–100, default 20) controls how many runs are fetched. Output is text or json only; nothing is written to workflow files and lint rules are unchanged.

New GitHubRunsClient (WorkflowRuns, RunJobs) extends the existing HTTP client, fixtures, and GHE fallback with the same caching and rate-limit behavior as other online lookups. Linter.SuggestTimeouts() builds the TimeoutReport, matches run job names to workflow jobs (matrix legs, expression names, reusable-workflow calls), and documents gaps (unmatched names, truncated large matrices, incomplete API failures). githubRepositoryOf now resolves origin via git config when .git is a file (worktrees/submodules).

CLI guards reject combining this mode with --fix, --export-facts, --no-online, --changed-since, stdin, and incompatible formats. Docs, man page, and tests cover matching, API queries, JSON/text output, and error cases.

Reviewed by Cursor Bugbot for commit fd8189c. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Added --suggest-timeouts to report job durations from recent successful GitHub Actions runs and suggest timeout-minutes values for jobs with enough samples.
    • Added options to choose the number of runs reviewed and set the suggestion buffer. Reports are available in text or JSON and do not modify workflow files.
    • Improved GitHub repository detection for Git worktrees.
  • Documentation
    • Documented timeout suggestions, options, report details, and limitations.

… (--suggest-timeouts)

Closes #121.

The "historical timeout recommendations, report-only" item. For each workflow
it reads the latest successful runs and the jobs of each run from GitHub
(implies --online) and reports samples, min, median and max per job, the dates
of the runs, and a timeout-minutes to consider: the longest duration times
--timeout-buffer (default 2), rounded up, for a job with at least 3 samples.

Report only: it writes nothing and no rule or fix uses a number from it. It
says what it cannot know: a matrix is one job whose legs share the timeout, a
call of a reusable workflow has no numbers, jobs it cannot match to the file
are listed, a failed lookup makes it incomplete, and a longest run on record
is not a bound on the next one.

Also: the GitHub repository of a linked work tree is read with git config
when .git is a file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Central YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 39577122-f368-42d0-8942-50b62fcb6f11
📥 Commits

Reviewing files that changed from the base of the PR and between 0190b4c and fd8189c.

📒 Files selected for processing: 5
  • command.go
  • command_options.go
  • docs/usage.md
  • man/jactionlint.1.ronn
  • timeout_history_test.go

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Walkthrough

The change adds GitHub workflow-run and job lookups, a timeout-history report API, and CLI options to display duration samples and timeout suggestions. Reports support text and JSON output. The tool does not write suggested timeout values to workflow files.

Changes: Timeout Suggestions

Layer / File(s) Summary
GitHub run-history access
github_runs.go, github_runs_http.go, github_session.go, github_fixture.go
Adds run and job lookup types and client methods. HTTP, fixture, fallback, and online-session implementations provide lookup data, with session caching.
Timeout report generation
timeout_history.go, project.go, timeout_history_test.go, docs/api.md, docs/checks.md
Adds SuggestTimeouts to match workflow jobs with successful run data and report duration statistics. Suggestions require at least three successful samples and use the longest duration with the configured buffer. Repository resolution can read a GitHub URL from Git when .git/config cannot be read. Tests cover matching, output, validation, and incomplete lookups.
CLI options and report output
command.go, command_options.go, docs/usage.md, man/jactionlint.1.ronn
Adds --suggest-timeouts, --timeout-history-runs, and --timeout-buffer. The CLI validates option combinations, enables online mode for suggestions, and writes text or JSON output.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant Linter
  participant OnlineSession
  participant GitHubRunsClient
  User->>CLI: request timeout suggestions
  CLI->>Linter: call SuggestTimeouts
  Linter->>OnlineSession: request workflow runs and job details
  OnlineSession->>GitHubRunsClient: retrieve successful runs and job data
  GitHubRunsClient-->>OnlineSession: return run and job data
  OnlineSession-->>Linter: return lookup results
  Linter-->>CLI: return timeout report
  CLI-->>User: write text or JSON report
Loading
Merge Risk: ⚪ Minimal · up to fd818

The supplied evidence identifies no issue that needs to be resolved before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 9 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the main change: adding --suggest-timeouts to report actual job durations from recent runs.
Linked Issues check Passed Issue #121 is closed and completed, so it provides historical context only. No active directly linked coding requirements apply. The reported implementation covers the historical timeout-reporting ite…
Out of Scope Changes check Passed The changes support the timeout-history report. CLI options, GitHub run lookups, fixture and fallback clients, repository detection, documentation, and tests are connected to that report. The report d…
Full details: Docstring Coverage

Explanation

Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 9 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3e73cb9. Configure here.

Comment thread timeout_history.go Resolved
jdx and others added 4 commits October 13, 2026 23:54
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jdx
jdx merged commit 033f615 into main Oct 14, 2026
17 checks passed
@jdx
jdx deleted the feat/timeout-history branch October 14, 2026 01:28
@jdx jdx mentioned this pull request Oct 14, 2026
jdx added a commit that referenced this pull request Oct 14, 2026
🤖 I have created a release *beep* *boop*
---


## [2.2.0](v2.1.0...v2.2.0)
(2026-10-14)


### Features

* **action-input-dependencies:** report inputs of well-known actions
that need each other
([#207](#207))
([ef17270](ef17270))
* **action-release-cooldown:** report an action at a version younger
than N days (online, opt-in)
([#235](#235))
([7dbd838](7dbd838))
* **action:** check the inputs of any remote action against its
action.yml with --online
([#231](#231))
([ac30cbe](ac30cbe))
* **actions-lock:** lint GitHub's Actions lock file against the
workflows ([#237](#237))
([c9d8944](c9d8944))
* **argument-injection:** report free text in the program or the options
of sed, awk, find and tar
([#221](#221))
([ecf46dc](ecf46dc))
* **aube:** understand aube in the rules that know npm, pnpm, yarn and
bun ([#254](#254))
([f6d9319](f6d9319))
* **cache-poisoning:** add pnpm/setup, graalvm and composer, and report
caches of credential files
([#216](#216))
([e3c29f0](e3c29f0))
* **cache-poisoning:** report more zizmor findings and add a pedantic
tier ([#160](#160))
([c725459](c725459))
* **cli:** add --ruff, --show-doc-urls, --finding-context, platform
forgejo and jujutsu roots
([#215](#215))
([8e31c5c](8e31c5c))
* **cli:** add shell completions and --explain RULE
([#204](#204))
([b3b09d3](b3b09d3))
* **cli:** add the options select, no-ignores and list-rules
([#200](#200))
([dca5092](dca5092))
* **cli:** show only the findings on what changed since a revision
(--changed-since)
([#274](#274))
([ea0ac90](ea0ac90))
* **command-injection:** report input that a script runs as code after
it moved to env ([#202](#202))
([ebeeb6f](ebeeb6f))
* **comment-trigger-authorization:** report comment-triggered jobs that
do not check who commented (pedantic)
([#217](#217))
([60fa75e](60fa75e))
* **concurrency-deadlock:** report a group that a workflow shares with
the workflow it calls
([#210](#210))
([c41e2ad](c41e2ad))
* **concurrency-limits:** report schedule-only and manual-only workflows
(pedantic) ([#266](#266))
([c1ec820](c1ec820))
* **config:** JSON Schemas for the config file and for --format json
([#195](#195))
([5f83de6](5f83de6))
* **dangerous-triggers:** say what the token and the secrets can do, and
make a workflow without privileges pedantic
([#189](#189))
([97e79e7](97e79e7))
* **deprecated-node-runtime:** report a Node.js runtime that GitHub
removed in the metadata of an action (pedantic)
([#225](#225))
([84dd922](84dd922))
* **deprecated-node-runtime:** report popular actions at majors that
declare node20 (pedantic)
([#230](#230))
([8a9fc95](8a9fc95))
* **deprecated-node-runtime:** report the environment variables that
chose the Node.js runtime (pedantic)
([#234](#234))
([6531d87](6531d87))
* **environment-file-redirect:** report writes to GITHUB_OUTPUT that
lose the data ([#184](#184))
([79cc5ef](79cc5ef))
* **event-name-never-received:** report github.event_name compared with
an event the workflow never receives
([#214](#214))
([b3bfcc4](b3bfcc4))
* **external-tools:** say when shellcheck or pyflakes did not run
([#188](#188))
([f7280e5](f7280e5))
* **facts:** export the events, permissions, concurrency and calls of a
repository as versioned JSON (--export-facts)
([#272](#272))
([881dde9](881dde9))
* **fail-fast-action-inputs:** report actions that pass when nothing was
produced ([#196](#196))
([f856144](f856144))
* **fix:** say why more findings have no fix, add --show-fix-status and
the fix prerequisites to --explain
([#269](#269))
([7aa3187](7aa3187))
* **gh-without-token:** report the gh command without GH_TOKEN or
GITHUB_TOKEN ([#177](#177))
([1d997da](1d997da))
* **github-app:** check two more token actions and cover local composite
actions in dependabot-missing-actions-update
([#229](#229))
([e5b620e](e5b620e))
* **github-env:** follow text that a script reads with gh and git
([#218](#218))
([9d0a4bb](9d0a4bb))
* **github-script-syntax:** check the JavaScript of
actions/github-script with node --check
([#242](#242))
([f106562](f106562))
* **hardcoded-registry-credentials:** report credentials in plain text
in the registries of dependabot.yml
([#265](#265))
([765521c](765521c))
* **macos-gnu-flags:** report GNU-only options in run scripts of macOS
jobs (pedantic) ([#241](#241))
([871c07d](871c07d))
* **malformed-expression-delimiters:** report ${ github.ref }, $ {{ x }}
and {{ github.sha }}
([#180](#180))
([e452449](e452449))
* **missing-version-comment:** report a hash pin without a version
comment and add it with --online --fix
([#232](#232))
([97767b4](97767b4))
* **multiline-plain-run:** report a run folded into one line
([#205](#205))
([f4e76c4](f4e76c4))
* **online:** look up the actions of a GitHub Enterprise Server on
github.com ([#250](#250))
([d8a1280](d8a1280))
* **online:** report coverage and cache freshness outside SARIF
([#166](#166))
([5fd779e](5fd779e))
* **online:** report the jobs whose environment has no required
reviewers ([#247](#247))
([6c13781](6c13781))
* **output-clobbering:** report input with line breaks written to
GITHUB_OUTPUT as a record
([#212](#212))
([c992eef](c992eef))
* **permissions:** report a job whose permissions are too narrow for its
actions ([#192](#192))
([07d78b7](07d78b7))
* **ref-version-mismatch:** fix the version comment (unsafe) and read
ratchet comments ([#227](#227))
([60842f7](60842f7))
* **release:** publish wheels to PyPI with go-to-wheel
([#277](#277))
([0440a3a](0440a3a))
* **repojacking:** report an action whose owner name anyone can register
([#248](#248))
([3b62077](3b62077))
* **runner-label:** accept the labels of hosted-runner providers
([#199](#199))
([27e453c](27e453c))
* **runner-label:** check runner labels built from a matrix value and
text ([#233](#233))
([b67bbd4](b67bbd4))
* **runner-label:** warn ahead of the retirement of a runner label and
name it afterwards
([#182](#182))
([0c3756b](0c3756b))
* **sarif:** say why a finding with a fix has none in the log
([#164](#164))
([7a2a509](7a2a509))
* **self-hosted-runner:** count the configured labels and runner groups,
and say when a pull request reaches the runner
([#194](#194))
([80c43f8](80c43f8))
* **shell-syntax:** report syntax errors in run scripts without
shellcheck ([#273](#273))
([31ee66e](31ee66e))
* **shellcheck:** read a shellcheckrc, extra arguments and optional
checks from the config
([#191](#191))
([4d02a46](4d02a46))
* **superfluous-actions:** resolve runs-on expressions the repository
tells ([#271](#271))
([22a3f8e](22a3f8e))
* **template-injection:** report more free text of outsiders in the
default profile ([#173](#173))
([08afe4a](08afe4a))
* **timeouts:** report how long jobs really took in their latest runs
(--suggest-timeouts)
([#275](#275))
([033f615](033f615))
* **ubuntu-slim:** report what the single-CPU runner cannot run
([#190](#190))
([51a5e7f](51a5e7f))
* **undefined-property:** check the properties of github.event against
the webhook payloads
([#249](#249))
([81a1038](81a1038))
* **unlocked-install:** model the lifecycle scripts and the release age
of aube (pedantic)
([#255](#255))
([f5dfab7](f5dfab7))
* **unlocked-install:** report installs without a release-age cooldown
(pedantic) ([#201](#201))
([13779f6](13779f6))
* **unlocked-install:** report npm, yarn and bun installs that run
lifecycle scripts (pedantic)
([#187](#187))
([ee74c67](ee74c67))
* **unmatched-paths-filter:** report paths filters that match no file of
the repository (pedantic)
([#238](#238))
([2baf2cb](2baf2cb))
* **unpinnable-action:** report a hash-pinned action whose own
dependencies are not pinned
([#240](#240))
([3cc0ddb](3cc0ddb))
* **unpinned-images:** check docker run images in scripts, add per-image
policies and image allow/deny lists
([#211](#211))
([376b065](376b065))
* **unpinned-images:** pin container images to a digest with --online
--fix=unsafe ([#239](#239))
([2f63474](2f63474))
* **unpinned-uses:** accept a ref which the Actions lock file pins to a
commit ([#228](#228))
([c702b7c](c702b7c))
* **unpinned-uses:** accept the tag of an immutable release under
--online ([#251](#251))
([c112760](c112760))
* **unpinned-uses:** name the most specific version tag in the comment
of --online --fix
([#224](#224))
([84dd5fc](84dd5fc))
* **unredacted-secrets:** report values cut from a secret and printed by
a script (pedantic)
([#222](#222))
([c45a29f](c45a29f))
* **untrusted-artifact:** know more download actions and report a
download of every artifact with the option pedantic
([#219](#219))
([bc216f9](bc216f9))
* **untrusted-checkout:** count the actions that run the workspace, with
the input that makes them do it
([#213](#213))
([29fea2c](29fea2c))
* **untrusted-checkout:** cover issue_comment workflows that build a
pull request ([#206](#206))
([6b73b42](6b73b42))
* **untrusted-checkout:** follow a pull request revision through
variables and script files
([#159](#159))
([78813f4](78813f4))
* **untrusted-checkout:** follow the head of a pull request into local
reusable workflows and composite actions
([#208](#208))
([29843f2](29843f2))
* **untrusted-checkout:** honor the fork pull request protection of
actions/checkout ([#203](#203))
([1acb258](1acb258))
* **untrusted-checkout:** report gates that hold at one moment in front
of code that can change, and labels that last
([#220](#220))
([09fc91e](09fc91e))
* **use-trusted-publishing:** report jobs that build, lack an
environment or drop attestations (pedantic)
([#209](#209))
([1c26e5f](1c26e5f))
* **workflow-call:** check the calls of reusable workflows of other
repositories with --online
([#236](#236))
([89f2c75](89f2c75))
* **workflow-secret-scope:** job-level secrets, secrets passed to
reusable workflows, and unused workflow_call secrets
([#223](#223))
([e7a53ef](e7a53ef))


### Bug Fixes

* **action:** check popular actions pinned to a full version or to a
commit with a version comment
([#174](#174))
([d324b7c](d324b7c))
* **api:** accept nil LinterOptions, print the rule ID in Error and
PrettyPrint, correct API comments
([#283](#283))
([e2d3304](e2d3304))
* **changed-since:** count shared findings once, keep ..foo inside the
repository, refuse --export-facts
([#279](#279))
([341ccb5](341ccb5))
* **config:** a missing file in "extends" of a discovered config file is
an error ([#285](#285))
([1a848ff](1a848ff))
* **download-script:** report a missing release, accept amd64, fix the
help text ([#282](#282))
([3165939](3165939))
* **if-always-true:** do not report the newlines at the end of a block
scalar ([#226](#226))
([871eb35](871eb35))
* **ignore:** read own-line ignore directives from real YAML comments
only ([#284](#284))
([3f9e648](3f9e648))
* **missing-timeout:** add accept-step-timeouts for jobs whose steps all
set timeout-minutes
([#252](#252))
([10cfa99](10cfa99))
* **multiline-plain-run:** rename commandWords so the package builds
again ([#256](#256))
([24d8900](24d8900))
* **parser:** accept a whole mapping or list written as one expression
where the schema allows it
([#178](#178))
([5689451](5689451))
* **permissions:** report the pedantic persona's permission findings
([#176](#176))
([016f588](016f588))
* **runner-label:** follow the images behind the -latest labels
([#171](#171))
([8e6159e](8e6159e))
* **syntax:** accept keys and values the workflow schema allows but the
docs do not list ([#170](#170))
([66df2e0](66df2e0))
* **syntax:** report what the runner rejects in schedules, merge_group,
self-repository uses and action.yml
([#186](#186))
([f498457](f498457))
* **untrusted-checkout:** follow pull/N/head fetches, skip merged pull
requests, count sed -f, awk -f, tar -P and unzip -:
([#175](#175))
([5fd975f](5fd975f))


### Performance Improvements

* analyze each run script once and parse the callers of an action once
([#270](#270))
([ac37b69](ac37b69))
* bound concurrent workflow checks to cut peak memory on huge
repositories ([#158](#158))
([bbe49da](bbe49da))
* **online:** read the advisories of all actions with one request
([#268](#268))
([a04bcd2](a04bcd2))
* share the parsed expressions of a string with the rules that still
parsed them again
([#165](#165))
([9bcdbb0](9bcdbb0))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Mechanical version and documentation updates only; no runtime or rule
logic changes in the diff.
> 
> **Overview**
> **Release Please** bumps the published version from **2.1.0** to
**2.2.0** and keeps every `x-release-please-version` marker aligned:
`.release-please-manifest.json`, the GitHub Action’s default `version`
input in `action.yml`, the `jactionlint-docker` pre-commit image tag,
docs site version (`docs/.vitepress/version.ts`), and install examples
in `docs/ci.md` and `docs/hooks.md`.
> 
> `CHANGELOG.md` gains the **2.2.0** section (2026-10-14) summarizing
what ships in this release—many new lint rules and CLI options
(`--changed-since`, `--export-facts`, `--explain`, online
action/workflow validation, Actions lock file linting), security-focused
checks (untrusted checkout, cache poisoning, repojacking, etc.), bug
fixes, and performance work. This PR does not change linter
implementation; it is the version and changelog cut for code already on
`main`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e0e7f9d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

tracking: caller-aware precision, fix explanations and policy interoperability

1 participant