Tutorial · Skylos 4.49.0 · about 10 minutes
Skylos tutorial: 8 checks we didn't find in other tools
This page walks through eight Skylos checks that, as of October 2026, we did not find in competing tools; seven come with a demo you paste into a terminal and see run in about a minute. Skylos also finds dead code, security issues, secrets and vulnerable dependencies, which other tools do as well, so this page covers only what we didn't find elsewhere.
Before you start
You need Python 3.10 or later and git. In a new virtual environment, install Skylos and pytest (demos 2, 5 and 7 run tests):
The eight checks at a glance
Checks 1 to 7 run in the free CLI on your machine or in CI. Check 8 is part of the Workspace plan in Skylos Cloud.
- 1.Risk review: risky PRs need a person; bot approvals never count
A pull request that deletes a test, removes an auth check, adds a dependency or weakens CI can't merge until a person with write access approves it. Approvals from review bots never count.
- 2.“Was it already failing?”
When tests fail, Skylos reruns the failing ones on main and labels each failure as introduced by the change or already failing there, so “pre-existing” is checked, not taken on trust.
- 3.AI agents in CI
Flags GitHub Actions workflows where anyone who can open an issue or comment can start an AI coding agent that has shell commands, secrets or write access.
- 4.A PR that rewrites its own AI reviewer
Flags a pull request that edits the files an AI code reviewer reads its settings from, such as a path excluded from review in .coderabbit.yaml, and names the setting that changed.
- 5.Test cheating: hard-coded answers and rigged comparisons
Fails a change whose code returns a test's exact expected answer for that test's input, or adds a comparison that always passes, instead of fixing the code.
- 6.Removed auth fails the PR
Fails a change that removes an auth check from a route or view, such as @login_required, and names the view that lost it.
- 7.Code a branch orphaned, with the line that did it
Names code the branch left without callers, often in a file the branch never touched, and the changed line that removed its last caller.
- 8.Skylos Cloud: the approval guard and audit of AI approvals
When an AI reviewer approves a risky pull request, the Skylos approval guard check waits for a person, and the Workspace keeps a record of every AI approval with per-bot counts.
Check 1 of 8 · Free CLI and GitHub check
Risk review: risky PRs need a person; bot approvals never count
A pull request that deletes a test, removes an auth check, adds a dependency or weakens CI can't merge until a person with write access approves it. Approvals from review bots never count.
The problem
AI code reviewers can now approve pull requests: since September 2026 a Copilot approval can count toward a repository's required approvals (GitHub changelog). GitHub's rules can require approvals and code-owner review, but none of them looks at what the change does, such as deleting a test (available ruleset rules).
Try it
The agent's branch deletes a test and removes @login_required from an admin page.
mkdir skylos-demo-risk && cd skylos-demo-risk && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
cat > app.py <<'EOF'
from flask import Flask
from flask_login import login_required
app = Flask(__name__)
@app.route("/admin")
@login_required
def admin():
return "admin page"
EOF
mkdir tests && cat > tests/test_app.py <<'EOF'
def test_add():
assert 1 + 1 == 2
def test_multiply():
assert 2 * 3 == 6
EOF
g add -A && g commit -qm "base"
# The agent's branch: drops a test and the login check
g checkout -qb agent-change
sed -i.bak '/^@login_required$/d' app.py && rm app.py.bak
python3 -c "p='tests/test_app.py'; s=open(p).read(); open(p,'w').write(s.split('def test_multiply')[0])"
g commit -qam "agent change"
skylos cicd risk-review --base main
What you'll see
Skylos risk review: NEEDS A HUMAN APPROVAL (exit 1)
Needs a human approval: tests_weakened (tests/test_app.py:5), auth_removed (app.py:8)
Compared 65ef19e077c6 (merge base of main) with 0da2f1612580; 2 files, 3 lines changed
Risks that need a human approval:
tests_weakened SKY-A110 tests/test_app.py:5 test_multiply was deleted; no test with the same body exists now
auth_removed SKY-L021 app.py:8 Security control regression: Auth decorator @login_required was removed from 'admin'
Checked: tests_weakened, auth_removed, dependency_changed, ci_weakened, checks_silenced, secret_added, guardrail_config_changed, migration_changed, large_diff
The command exits with code 1. The risk review lists the two risks with their file and line, then the nine risk classes it checked. On your machine there is no approval to look for, so it stops at “needs a human approval”. In GitHub Actions it also reads the pull request's reviews.
Use it on your repo
Add the check to your repository (Skylos 4.49.0 or later):
- Commit the generated
.github/workflows/skylos.yml. It adds the Skylos risk review job, and a second job that reruns it when a review is submitted or dismissed. - Open a pull request so the check runs once. GitHub lists a check in branch rules only after it has run.
- In branch protection or a ruleset for your default branch, turn on Require status checks to pass before merging and add Skylos risk review. Keep Require approvals on: the check only decides whether a person must be one of the approvers.
From then on a risky pull request can't merge, however many approvals it has. When a person with write access approves the head commit, the check turns green. Approvals from bot accounts, such as Copilot or CodeRabbit, never count, and neither does an approval of an older commit. Dismissing the approval blocks the merge again. We ran this on GitHub on 14 October 2026: blocked, approved by a person, unblocked, approval dismissed, blocked again.
What other tools do
If you never want a bot's approval to count, GitHub's defaults already do that: Copilot approvals are off by default, and admins who turn them on choose which file paths Copilot may approve (GitHub). Macroscope holds its own auto-approval when a changed path matches a neverApprove pattern (Macroscope), and CodeRabbit doesn't approve automatically while one of its pre-merge checks fails (CodeRabbit). In these tools and in GitHub's ruleset options, as of October 2026, we did not find a rule that asks for a person based on what the change does, such as a deleted test, while ignoring every review bot's approval.
Check 2 of 8 · Free CLI: skylos done
“Was it already failing?”
When tests fail, Skylos reruns the failing ones on main and labels each failure as introduced by the change or already failing there, so “pre-existing” is checked, not taken on trust.
The problem
When tests fail, coding agents often call the failures pre-existing and stop, sometimes when their own change caused them. One open report: “A regression introduced by the model's own fix is reported as a pre-existing gap” ( anthropics/claude-code#96463, September 2026).
Try it
On main, mul() is already broken and its test fails. The agent's branch also breaks sub().
mkdir skylos-demo-failing && cd skylos-demo-failing && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
cat > calc.py <<'EOF'
def sub(a, b):
return a - b
def mul(a, b):
return a + b # already broken on main
EOF
mkdir tests && cat > tests/test_calc.py <<'EOF'
from calc import mul, sub
def test_sub():
assert sub(5, 3) == 2
def test_mul():
assert mul(2, 3) == 6
EOF
printf '[tool.pytest.ini_options]\npythonpath = ["."]\n' > pyproject.toml
g add -A && g commit -qm "base"
# The agent's branch breaks sub(), then says the failures were "already there"
g checkout -qb agent-change
sed -i.bak 's/return a - b/return a - b + 1/' calc.py && rm calc.py.bak
g commit -qam "agent change"
skylos done --base main
What you'll see
Skylos done · base e0bb49b (merge base) · head f75014a
FAIL Tests pass when Skylos runs them: 2 of 2 tests failed: 1 introduced, 1 already failing on the base
Failing tests rerun on main (merge base e0bb49b): 1 introduced, 1 already failing on the base (0.8 s)
tests/test_calc.py:4 SKY-A113 tests/test_calc.py::test_sub failed (introduced by this change: passes on main (merge base e0bb49b)): assert 3 == 2 + where 3 = sub(5, 3)
tests/test_calc.py:8 SKY-A113 tests/test_calc.py::test_mul failed (already failing on main (merge base e0bb49b)): assert 5 == 6 + where 5 = mul(2, 3)
Fix: Fix the failing tests, or the code they test.
PASS No tests deleted, skipped or weakened: No tests deleted, skipped or loosened (2 compared)
PASS Skylos settings and hooks left alone: Skylos settings, hooks and protected paths unchanged
PASS No secrets added: No secrets added
PASS Every package and import is real: Every added import and package resolves
WARN Tests check the changed lines: Earlier required checks failed or were incomplete
PASS Code doesn't special-case the tests: No code special-cases the tests (1 changed file(s) read)
PASS Linters, type checkers, scanners and CI not silenced: No linter, type-checker, scanner or CI settings weakened
PASS Security controls checked for removals: No security control removals detected (1 changed source file(s) compared)
PASS AI-reviewer settings and scanner markers left alone: AI-reviewer settings unchanged; no scanner suppression markers added
PASS No code left without callers: No code left without callers
Verdict: FAIL: 2 of 2 tests failed when Skylos ran them (1 introduced, 1 already failing on the base)
Receipt: .skylos/receipts/f75014a63680-e0bb49ba6a78.json
The command exits with code 1. Skylos reran both failing tests on main: test_sub passes there, so this change broke it; test_mul fails there the same way, so it was already failing. Both still fail the check: the labels tell you which failures are the agent's, they don't excuse the others.
Use it on your repo
Run the same check on an agent's branch before you accept “done”:
To have Claude Code's Stop hook run it on the session's edits and tell the agent which failures are new, install the hooks (use --codex or --cursor for those agents):
What other tools do
GitLab's merge request test report separates newly failed tests from tests that failed on both branches, using the target branch's pipeline (GitLab docs). The prove-pre-existing skill in claude-tdd-kit asks the agent to stash its edits and rerun, which compares with your working tree rather than main (skill), and isitdone's Stop hook lets a failure that names none of the session's files block only once (isitdone). As of October 2026 we did not find, in these or other tools, one that reruns the failing tests on the base commit on your machine before the agent says it's done.
Check 3 of 8 · Free CLI: skylos . -a
AI agents in CI
Flags GitHub Actions workflows where anyone who can open an issue or comment can start an AI coding agent that has shell commands, secrets or write access.
The problem
Workflows that let an AI agent answer issues and comments run it on text anyone can write. claude-code-action's own docs call allowed_non_write_users “a significant security risk” (security.md). The Clinejection write-up in March 2026 described an issue-triage workflow that ran Claude with that setting at "*" and with shell tools (Hacker News discussion).
Try it
The demo workflow starts Claude when anyone comments on an issue, lets any account past the action's write-access check, and allows shell commands.
mkdir skylos-demo-ci-agent && cd skylos-demo-ci-agent && git init -q -b main
mkdir -p .github/workflows && cat > .github/workflows/claude.yml <<'EOF'
name: claude
on:
issue_comment:
types: [created]
permissions:
contents: write
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
claude_args: "--allowedTools Bash"
EOF
skylos . -a --format concise
What you'll see
… (trimmed: scan notes and other workflow findings)
.github/workflows/claude.yml:15 SKY-D351 issue_comment (anyone who can comment) → claude-code-action (the action loads the triggering issue, comment or pull request) with allowed_non_write_users: "*", which lets any account past the action's write-access check; the agent has shell commands (Bash in --allowedTools) and secrets ANTHROPIC_API_KEY; contents: write.
.github/workflows/claude.yml:16 SKY-D352 claude-code-action can run any shell command (Bash in --allowedTools) in a workflow that issue_comment (anyone who can comment) starts; a prompt injection in that text becomes commands run with secrets ANTHROPIC_API_KEY; contents: write.
… (trimmed: other workflow findings)
The command exits with code 1. SKY-D351 reports that allowed_non_write_users: "*" lets any account past the action's write-access check. SKY-D352 reports that the agent can run any shell command in a workflow anyone can start, so a prompt injection in a comment becomes commands run with the workflow's secrets. The trimmed lines are scan notes and general workflow issues, such as broad permissions and an unpinned action, that other workflow scanners report too.
Use it on your repo
Check the workflows you already have (workflow rules run with -a):
What other tools do
zizmor, CodeQL and poutine check GitHub Actions workflows for injection, permissions and pinning, and Skylos doesn't replace them. As of 14 October 2026, AI-agent rules for CodeQL (github/codeql#21675) and zizmor (zizmorcore/zizmor#1655) are open pull requests, not merged, and poutine's rules don't cover AI agents (poutine). Trail of Bits publishes an agentic actions auditor as a Claude Code skill, which the model runs rather than a fixed rule set (Trail of Bits). As of October 2026 we did not find these AI-agent rules in a release of zizmor, CodeQL or poutine.
Check 4 of 8 · Free CLI: skylos done and diff scans
A PR that rewrites its own AI reviewer
Flags a pull request that edits the files an AI code reviewer reads its settings from, such as a path excluded from review in .coderabbit.yaml, and names the setting that changed.
The problem
Some AI code reviewers read their settings from the pull request they are reviewing. CodeRabbit uses the .coderabbit.yaml in the feature branch under review (CodeRabbit docs), and since July 2026 Copilot code review reads custom instructions from the pull request's head branch (GitHub changelog). So a pull request can tell its reviewer to skip part of itself.
Try it
The agent's branch adds !src/auth/** to CodeRabbit's path filters, so CodeRabbit would skip the auth code.
mkdir skylos-demo-reviewer && cd skylos-demo-reviewer && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
mkdir -p src/auth && printf 'def check(user):\n return user.is_admin\n' > src/auth/check.py
cat > .coderabbit.yaml <<'EOF'
reviews:
path_filters:
- "!dist/**"
EOF
g add -A && g commit -qm "base"
# The pull request quietly tells the AI reviewer to skip the auth code
g checkout -qb agent-change
printf ' - "!src/auth/**"\n' >> .coderabbit.yaml
g commit -qam "tidy review config"
skylos . -a --diff-base main --format concise
What you'll see
SCA coverage limited: no supported dependency manifests found; 0 dependencies queried
Dependency inventory: 0 packages queried · 0 supported lockfiles read · 0 manifest versions unresolved · 0 lockfile entries unresolved · 0 non-registry entries not queried · 0 lockfile entries not checked · 0 parse errors · 0 unsupported lockfiles
Dependency next step: for unresolved public-package versions, provide a supported lockfile (e.g. uv.lock, poetry.lock or package-lock.json) or exact manifest versions; version ranges are not resolved
Dependency scope: recorded versions only; installed environment, lockfile freshness and runtime reachability are not verified
.coderabbit.yaml:4 SKY-A122 .coderabbit.yaml: CodeRabbit review settings (read from the pull request's head) changed: reviews.path_filters gained `!src/auth/**` (excluded from review)
The command exits with code 1. SKY-A122 names the setting that changed and what it does: the auth folder is now excluded from review. The lines above it are dependency notes; this demo has no dependency manifest.
Use it on your repo
skylos done reports the same change as advice:
What other tools do
Some reviewers now guard their own settings: Cursor Bugbot reads .cursor/config/bugbot.yaml from the base branch (Cursor docs), Claude Code Review has used CLAUDE.md from the base branch when a pull request edits it since 6 October 2026 (Claude Code changelog), and Macroscope holds for a person any pull request that changes its approval file (Macroscope). AgentShield scans Claude Code setups (permissions, hooks, MCP servers), not AI-reviewer files (AgentShield). In these tools, as of October 2026, we did not find a check that flags a pull request's edits to other vendors' reviewer settings. A CODEOWNERS entry for these files is a good native alternative.
Check 5 of 8 · Free CLI: skylos done
Test cheating: hard-coded answers and rigged comparisons
Fails a change whose code returns a test's exact expected answer for that test's input, or adds a comparison that always passes, instead of fixing the code.
The problem
When a test won't pass, agents sometimes make the code answer the test instead of fixing it. One developer described Claude adding “if test-is-running { return success; }” level stuff (Hacker News, June 2025).
Try it
On main, factorial() is off by one and Money has no equality, so both tests fail. The agent's branch makes them pass: factorial() returns 120 when n == 5, and Money gets an __eq__ that always returns True.
mkdir skylos-demo-cheat && cd skylos-demo-cheat && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
cat > mathlib.py <<'EOF'
def factorial(n):
result = 1
for i in range(2, n):
result *= i
return result
class Money:
def __init__(self, cents):
self.cents = cents
EOF
mkdir tests && cat > tests/test_mathlib.py <<'EOF'
from mathlib import Money, factorial
def test_factorial():
assert factorial(5) == 120
def test_money():
assert Money(100) == Money(100)
EOF
printf '[tool.pytest.ini_options]\npythonpath = ["."]\n' > pyproject.toml
g add -A && g commit -qm "base"
# The agent "fixes" the failing tests by hard-coding the answer and rigging ==
g checkout -qb agent-change
python3 - <<'EOF'
p = "mathlib.py"
s = open(p).read()
s = s.replace(" result = 1\n", " if n == 5:\n return 120\n result = 1\n")
s += "\n def __eq__(self, other):\n return True\n"
open(p, "w").write(s)
EOF
g commit -qam "fix tests"
skylos done --base main
What you'll see
Skylos done · base bd4e924 (merge base) · head 6159184
UNFINISHED Tests pass when Skylos runs them: Earlier required checks failed or were incomplete
PASS No tests deleted, skipped or weakened: No tests deleted, skipped or loosened (2 compared)
PASS Skylos settings and hooks left alone: Skylos settings, hooks and protected paths unchanged
PASS No secrets added: No secrets added
PASS Every package and import is real: Every added import and package resolves
WARN Tests check the changed lines: Earlier required checks failed or were incomplete
FAIL Code doesn't special-case the tests: 2 place(s) in the code special-case the tests
mathlib.py:2 SKY-A115 factorial() returns 120 when n == 5, the exact input and answer of tests/test_mathlib.py::test_factorial: this special-cases the test instead of computing the answer
mathlib.py:14 SKY-A117 Money.__eq__ always returns True: any comparison with it passes, so tests that compare values cannot fail
Fix: Remove the code that answers the tests' exact inputs, detects the test run or rigs comparisons, and make the general code right.
PASS Linters, type checkers, scanners and CI not silenced: No linter, type-checker, scanner or CI settings weakened
PASS Security controls checked for removals: No security control removals detected (1 changed source file(s) compared)
PASS AI-reviewer settings and scanner markers left alone: AI-reviewer settings unchanged; no scanner suppression markers added
PASS No code left without callers: No code left without callers
Verdict: FAIL: code special-cases the tests. The tests were not run because of this
Receipt: .skylos/receipts/615918426c11-bd4e924d1cbf.json
The command exits with code 1. SKY-A115 names the test whose exact input and answer were hard-coded, and SKY-A117 flags the comparison that lets every test pass. Skylos didn't run the tests because this check failed first, and the receipt says so instead of reporting a pass.
Use it on your repo
Both rules block by default:
What other tools do
fallow has merged audit rules for weakened tests and test-only branches in JavaScript and TypeScript, due in its next release (fallow docs source). isitdone's Stop hook runs your tests and flags deleted test files, new skips and dropped assertions (isitdone), and AI reviewers can spot a suspicious special case when they read the diff. In these tools, as of October 2026, we did not find a rule-based check for an answer hard-coded to an existing test's exact input, or for a comparison rigged to always pass.
Check 6 of 8 · Free CLI: skylos done and diff scans
Removed auth fails the PR
Fails a change that removes an auth check from a route or view, such as @login_required, and names the view that lost it.
The problem
Agents remove checks that get in their way. In a report filed against Gemini CLI, a user wrote that the agent “explicitly added flags to disable all authentication” on a JupyterLab server (google-gemini/gemini-cli#26616, May 2026).
Try it
The agent's branch removes @login_required from a Django billing view.
mkdir skylos-demo-auth && cd skylos-demo-auth && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
cat > views.py <<'EOF'
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
@login_required
def billing(request):
return HttpResponse("billing")
EOF
g add -A && g commit -qm "base"
g checkout -qb agent-change
sed -i.bak '/^@login_required$/d' views.py && rm views.py.bak
g commit -qam "simplify view"
skylos . -a --diff-base main --format concise
What you'll see
SCA coverage limited: no supported dependency manifests found; 0 dependencies queried
Dependency inventory: 0 packages queried · 0 supported lockfiles read · 0 manifest versions unresolved · 0 lockfile entries unresolved · 0 non-registry entries not queried · 0 lockfile entries not checked · 0 parse errors · 0 unsupported lockfiles
Dependency next step: for unresolved public-package versions, provide a supported lockfile (e.g. uv.lock, poetry.lock or package-lock.json) or exact manifest versions; version ranges are not resolved
Dependency scope: recorded versions only; installed environment, lockfile freshness and runtime reachability are not verified
views.py:5 SKY-U001 unused function: billing
views.py:1 SKY-U002 unused import: login_required
views.py:5 SKY-U006 unused parameter: request
views.py:5 SKY-L021 Security control regression: Auth decorator @login_required was removed from 'billing'
The command exits with code 1. SKY-L021 names the view that lost its auth decorator. The SKY-U lines are dead-code issues in this one-file demo, where nothing calls the view.
Use it on your repo
skylos done blocks on SKY-L021 by default:
What other tools do
Cursor's Security Review bot, on its Teams and Enterprise plans, looks for authentication and authorization bypasses, “including checks that a refactor stopped running” ( Cursor changelog). As of October 2026 we did not find a free, rule-based check that fails a pull request because it removed an auth decorator or middleware.
Check 7 of 8 · Free CLI: skylos done
Code a branch orphaned, with the line that did it
Names code the branch left without callers, often in a file the branch never touched, and the changed line that removed its last caller.
The problem
Agents rewrite a caller and leave the old helper behind. Later, “the agent sometimes finds that old dead code while building something new, hooks into it, and now you have bugs from code that was never meant to run” (Hacker News, February 2026).
Try it
On main, checkout.py calls util.format_price(). The agent's branch inlines the formatting, so format_price has no callers left, in a file the branch never touched.
mkdir skylos-demo-orphans && cd skylos-demo-orphans && git init -q -b main
g() { git -c user.name=demo -c user.email=demo@example.com "$@"; }
cat > util.py <<'EOF'
def format_price(cents):
return f"${cents / 100:.2f}"
EOF
cat > checkout.py <<'EOF'
from util import format_price
def total(items):
return format_price(sum(items))
if __name__ == "__main__":
print(total([150, 250]))
EOF
mkdir tests && cat > tests/test_checkout.py <<'EOF'
from checkout import total
def test_total():
assert total([150, 250]) == "$4.00"
EOF
printf '[tool.pytest.ini_options]\npythonpath = ["."]\n' > pyproject.toml
g add -A && g commit -qm "base"
# The agent inlines the formatting; util.format_price now has no caller
g checkout -qb agent-change
cat > checkout.py <<'EOF'
def total(items):
return f"${sum(items) / 100:.2f}"
if __name__ == "__main__":
print(total([150, 250]))
EOF
g commit -qam "inline formatting"
skylos done --base main
What you'll see
Skylos done · base 956cabf (merge base) · head 72e5223
PASS Tests pass when Skylos runs them: 1 tests run by Skylos, 1 passed (0 s)
PASS No tests deleted, skipped or weakened: No tests deleted, skipped or loosened (1 compared)
PASS Skylos settings and hooks left alone: Skylos settings, hooks and protected paths unchanged
PASS No secrets added: No secrets added
PASS Every package and import is real: Every added import and package resolves
PASS Tests check the changed lines: Tests check all 1 changed lines
PASS Code doesn't special-case the tests: No code special-cases the tests (1 changed file(s) read)
PASS Linters, type checkers, scanners and CI not silenced: No linter, type-checker, scanner or CI settings weakened
PASS Security controls checked for removals: No security control removals detected (1 changed source file(s) compared)
PASS AI-reviewer settings and scanner markers left alone: AI-reviewer settings unchanged; no scanner suppression markers added
PASS No code left without callers: 1 existing symbol(s) left unused
util.py:1 SKY-A135 (advice) `format_price` (not touched by this change) lost its last caller when checkout.py:2 changed (`return format_price(sum(items))`); Skylos isn't certain nothing else uses it (weak helper function)
Verdict: PASS
Receipt: .skylos/receipts/72e522369649-956cabf035e7.json
The command exits with code 0. The verdict is PASS because this check is advice by default. SKY-A135 names the helper, says the branch didn't touch it, and points at checkout.py:2, the changed line that was its last caller.
Use it on your repo
Run it on an agent's branch:
What other tools do
In JavaScript and TypeScript, fallow can fail a pull request when its dead-code baseline grows anywhere in the project (fallow docs), and Knip finds unused files, exports and dependencies (Knip). In these tools, as of October 2026, we did not find a report that names the changed line that removed a symbol's last caller.
Check 8 of 8 · Skylos Cloud, Workspace plan
Skylos Cloud: the approval guard and audit of AI approvals
When an AI reviewer approves a risky pull request, the Skylos approval guard check waits for a person, and the Workspace keeps a record of every AI approval with per-bot counts.
The problem
Review bots now approve pull requests. Copilot approvals can count toward required approvals, and CodeRabbit approves after its review when no inline comments or failed statuses are left (CodeRabbit changelog, July 2026). A team that allows this needs to know which of those approvals it shouldn't have relied on.
What it does
When an AI reviewer such as GitHub Copilot, CodeRabbit, Sourcery, Greptile, Macroscope, Devin or Cursor Bugbot approves a pull request, Skylos Cloud reads the CI scan of the head commit. If the change has a risk (tests weakened, auth removed, dependencies or migrations changed, CI or checks weakened, a secret added, guardrail settings changed, or a very large diff), the Skylos approval guard check fails until a person with write access approves that exact commit. Without a scan of the head commit it waits; it never passes on missing evidence.
The Pull requests page shows a line for each review bot, with how many pull requests it approved and how many of those approvals Skylos held back, and a list of each AI approval with the pull request, the bot, the head commit and the issues found. Check 1 makes every risky pull request wait for a person; the approval guard acts when a bot approves, and keeps the record across your repositories.
Set it up
- Connect the repository to a Skylos Cloud workspace with the Skylos GitHub App.
- Upload Skylos results from CI:
skylos cicd initadds the upload job. - Require the Skylos approval guard check in the branch rules of your default branch. Without that, GitHub shows the result but doesn't block the merge.
The approval guard is part of the Workspace plan. See pricing opens the plans and what each one includes.
What other tools do
GitHub lets admins choose which file paths Copilot may approve (GitHub changelog) and, by default, asks for one more approval on Copilot pull requests that no person is attributed to (GitHub docs). As of October 2026 we did not find, in GitHub or the review bots above, a report per review bot of which approvals landed on pull requests that weakened tests or removed auth.
Run it on your own repository
Install the CLI, check out an agent's branch and run skylos done --base origin/main. It runs on your machine, with no account.