diff --git a/lib/rules/workflow_audit.ex b/lib/rules/workflow_audit.ex
index 525d3aaa..a77e6309 100644
--- a/lib/rules/workflow_audit.ex
+++ b/lib/rules/workflow_audit.ex
@@ -146,35 +146,42 @@ defmodule Hypatia.Rules.WorkflowAudit do
       # Naïve YAML scan: any `^  <key>:$` under top-level `jobs:`
       # without a subsequent `^    timeout-minutes:` line before the
       # next sibling block.
+      # Reusable-workflow caller jobs (a job-level `^    uses:` calling
+      # `org/repo/.github/workflows/x.yml@ref`) are EXEMPT: a workflow_call
+      # job cannot carry a job-level `timeout-minutes:` — the timeout lives
+      # in the called workflow's own jobs. Flagging them is a false positive.
       lines = String.split(content, "\n")
-      in_jobs = false
-      jobs_with_timeout = MapSet.new()
-      jobs_seen = MapSet.new()
-      current_job = nil
+      acc0 = {MapSet.new(), MapSet.new(), MapSet.new(), false, nil}
 
-      {jobs_seen, jobs_with_timeout} =
-        Enum.reduce(lines, {jobs_seen, jobs_with_timeout, false, nil}, fn line, {seen, with_to, in_j, curj} ->
+      {jobs_seen, jobs_with_timeout, reusable_jobs} =
+        Enum.reduce(lines, acc0, fn line, {seen, with_to, reusable, in_j, curj} ->
           cond do
             String.match?(line, ~r/^jobs:\s*$/) ->
-              {seen, with_to, true, nil}
+              {seen, with_to, reusable, true, nil}
 
             in_j and String.match?(line, ~r/^  [A-Za-z0-9_-]+:\s*$/) ->
               [_, name] = Regex.run(~r/^  ([A-Za-z0-9_-]+):/, line)
-              {MapSet.put(seen, name), with_to, in_j, name}
+              {MapSet.put(seen, name), with_to, reusable, in_j, name}
 
             in_j and curj && String.match?(line, ~r/^    timeout-minutes:\s*\d+/) ->
-              {seen, MapSet.put(with_to, curj), in_j, curj}
+              {seen, MapSet.put(with_to, curj), reusable, in_j, curj}
+
+            in_j and curj && String.match?(line, ~r/^    uses:\s*\S/) ->
+              {seen, with_to, MapSet.put(reusable, curj), in_j, curj}
 
             String.match?(line, ~r/^[A-Za-z]/) ->
-              {seen, with_to, false, curj}
+              {seen, with_to, reusable, false, curj}
 
             true ->
-              {seen, with_to, in_j, curj}
+              {seen, with_to, reusable, in_j, curj}
           end
         end)
-        |> then(fn {s, w, _, _} -> {s, w} end)
+        |> then(fn {s, w, r, _, _} -> {s, w, r} end)
 
-      missing = MapSet.difference(jobs_seen, jobs_with_timeout)
+      missing =
+        jobs_seen
+        |> MapSet.difference(jobs_with_timeout)
+        |> MapSet.difference(reusable_jobs)
 
       Enum.map(missing, fn job_name ->
         %{
diff --git a/test/workflow_audit_test.exs b/test/workflow_audit_test.exs
index c04dbba8..3e99c42b 100644
--- a/test/workflow_audit_test.exs
+++ b/test/workflow_audit_test.exs
@@ -47,6 +47,20 @@ defmodule Hypatia.Rules.WorkflowAuditTest do
       assert findings == []
     end
 
+    test "ignores a full-SHA pin with a trailing version comment (setup-java FP guard)" do
+      content = """
+      jobs:
+        verify:
+          runs-on: ubuntu-latest
+          steps:
+            - name: Set up Temurin JRE 21
+              uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
+      """
+
+      findings = WorkflowAudit.check_unpinned_actions(%{"verify-proofs.yml" => content})
+      assert findings == []
+    end
+
     test "provides known SHA when available" do
       content = "    - uses: actions/checkout@v4\n"
       [finding] = WorkflowAudit.check_unpinned_actions(%{"ci.yml" => content})
@@ -54,6 +68,59 @@ defmodule Hypatia.Rules.WorkflowAuditTest do
     end
   end
 
+  describe "check_missing_timeout_minutes/1" do
+    test "flags a runner job with no timeout-minutes" do
+      content = """
+      jobs:
+        build:
+          runs-on: ubuntu-latest
+          steps:
+            - run: echo hi
+      """
+
+      findings = WorkflowAudit.check_missing_timeout_minutes(%{"ci.yml" => content})
+      assert [%{rule: "missing_timeout_minutes", job: "build"}] = findings
+    end
+
+    test "does not flag a job that declares timeout-minutes" do
+      content = """
+      jobs:
+        build:
+          runs-on: ubuntu-latest
+          timeout-minutes: 10
+          steps:
+            - run: echo hi
+      """
+
+      assert WorkflowAudit.check_missing_timeout_minutes(%{"ci.yml" => content}) == []
+    end
+
+    test "does not flag a reusable-workflow caller job (uses:) — it cannot hold a job-level timeout" do
+      content = """
+      jobs:
+        governance:
+          uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@5eb28d7d8790d5389b7b6a5233fe6265a775e3d0
+      """
+
+      assert WorkflowAudit.check_missing_timeout_minutes(%{"governance.yml" => content}) == []
+    end
+
+    test "still flags a runner job alongside a reusable caller job" do
+      content = """
+      jobs:
+        call:
+          uses: hyperpolymath/standards/.github/workflows/x-reusable.yml@5eb28d7d8790d5389b7b6a5233fe6265a775e3d0
+        build:
+          runs-on: ubuntu-latest
+          steps:
+            - run: echo hi
+      """
+
+      findings = WorkflowAudit.check_missing_timeout_minutes(%{"mixed.yml" => content})
+      assert [%{job: "build"}] = findings
+    end
+  end
+
   describe "check_permissions/1" do
     test "flags write-all" do
       content = "permissions: write-all\njobs:\n  build:"
