Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Adversarial review and fix loop for code, specs, plans, and migration manifests.
SkillScore77/100
Test results 77
3/3 safety checks passed
36%77%of test cases passed +41 pts
Passed 17 of 22 test cases with the skill, and 8 of 22 without it.
9 fixed8 passed either way5 still failing0 broke
One test taskWe want to review `services/billing/charge.py`. Instead of invoking any helper wrapper files on disk, craft an inline bash command using a heredoc containing the full adversarial instructions and pipe it directly to codex exec.
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add sno-ai-peer-review
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install sno-ai-peer-reviewThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/sno-ai-peer-review/SKILL.md https://app.decimal.ai/s/sno-ai-peer-review/SKILL.md.claude/skills/sno-ai-peer-review/SKILL.mdThis gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add sno-ai-peer-reviewimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add sno-ai-peer-reviewfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add sno-ai-peer-reviewimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add sno-ai-peer-reviewimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add sno-ai-peer-reviewfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add sno-ai-peer-reviewfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 12 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add sno-ai-peer-reviewimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add sno-ai-peer-reviewfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 12 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add sno-ai-peer-reviewfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 12 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add sno-ai-peer-reviewimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 12 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add sno-ai-peer-reviewimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 12 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add sno-ai-peer-reviewfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("sno-ai-peer-review")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/sno-ai-peer-review/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/sno-ai-peer-review/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 12 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add sno-ai-peer-reviewNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/sno-ai-peer-review/SKILL.md https://app.decimal.ai/s/sno-ai-peer-review/SKILL.md.agents/skills/sno-ai-peer-review/SKILL.mdThis gets SKILL.md only. The skill's 12 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 12 other files are not included.
Raw files: /s/sno-ai-peer-review/SKILL.md · /s/sno-ai-peer-review.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/sno-ai-peer-review/SKILL.mdcurl -fsSL https://app.decimal.ai/s/sno-ai-peer-review.jsonThis gets SKILL.md only. The skill's 12 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
Classify → gather context → run bundled adversarial-review script → root cause → fix/report → repeat until clean.
"Owner" below means whoever owns the work, usually the user.
Prerequisites: Linux, bash, python3, jq, GNU date, timeout, sha1sum, pgrep and GNU xargs, Sno's quota pick and answer-provider commands, plus an installed, logged-in reviewer CLI on another vendor. Without python3 the findings ledger and receipts do not work.
The bundled script's adversarial-review pass is the authoritative reviewer. It runs in a fresh, separate headless session that has none of the caller's conversation. Only an answer from a different model vendor counts: the script walks Sno's ordered reviewer candidates and discards failed, empty, unknown-vendor or same-vendor answers. There is no same-vendor fallback. The active agent gathers context, investigates root causes, and applies fixes — it never overrides, filters, softens, or rewords the nested reviewer's findings. Every authoritative finding reaches the user; tag suspected false positives [Possible FP] with your reasoning instead of dropping them. Findings the active agent adds on its own are labeled "Active-agent supplementary" and kept clearly separate.
Works for both code and documents: classify first, then load the right verification layers. Code gets a root-cause-driven fix loop; documents get a structured report with optional edits.
The review exists to catch expensive failures — data loss, security holes, silently wrong results, primary flows breaking under real production conditions — not to audit style or completeness. Review primary flows first: establish what the artifact is for and where failure costs most, then trace those paths end to end; do not sweep line-by-line collecting local blemishes.
First, simulate the ordinary use and prove it cannot go wrong there. Before anything else, write down how this artifact is normally used — by a person, by an LLM, by an agent — and walk each of those paths end to end with the values they will really carry: the actual input formats produced by the surrounding system, the actual calls those callers make, and the actual ways their inputs fail (empty, blank, truncated, missing, stale). A defect on the ordinary path outranks any number of exotic ones. Edge cases deserve a few probes and never the bulk of the review; if the findings list is mostly exotic inputs, the review has not been done yet.
Exotic-input hunting feels like rigour and reliably misses the main path — a review whose findings are mostly extreme inputs has not walked the common call yet. The common failure it misses is the plausible, silent one: a caller whose variable broke passes an empty value and the tool answers with a default that looks right.
Process-machinery doctrine: when the target is agent-process machinery — orchestration/supervision scripts, task templates, skill process text, messaging contracts — the severity order is fixed: silent death, stalls/deadlocks, wrong-flow execution, and agents stepping on each other outrank everything else. A judgment error is recoverable; a process that dies quietly, waits forever, or tramples a parallel agent's work is not. Hunt specifically: paths where a failure produces NO signal; waits without timeouts; state that can go stale without an alarm; two writers on one resource; steps that can be skipped with the gate still passing. Wrong-but-loud is acceptable; wrong-and-silent is the bug.
What is extra comes before what is missing. The reviewer fills a "Cut" tier first: what the change added that no observed failure or stated requirement needs (a lock, cap, retry, hash, alias map, flag, gate, state table, queue, one-caller abstraction, un-retired old path, test that restates the code). A Cut item is always a deletion or a swap for something that already exists, never an addition, and it counts against the verdict: unrequested behaviour is a defect.
By default (a project may choose otherwise), a finding is not an instruction to add code. The defect report comes back in two tiers and the active agent keeps them apart: "Must fix" holds only findings on the ordinary call path, caused by the change, whose smallest remedy adds no new behaviour — no new mechanism, constant, threshold, state table, lock, queue, alias map, retry policy or reply shape. "Also found" holds everything else the reviewer dug up (edge paths such as concurrent same-session calls, mid-flight cancellation, cleanup with a partial identity; findings whose only remedy is a new mechanism; pre-existing debt; context-invalid triggers). "Also found" is recorded and reported under its own heading; it is never turned into code in the same task, and a remedy that would add behaviour goes to the owner as a decision, not to a worker as a task.
Every finding must pass three gates: Trigger (a realistic one-to-two-sentence sequence by which a real user, operator, integration, deploy, or retry reaches the failure), Consequence (operational impact: what breaks, for whom, how visible, how reversible), Refutation (an honest attempt to kill the finding against guards, caller contracts, and framework guarantees). These gates bind the nested reviewer at generation time and any supplementary finding the active agent adds. For a nested-reviewer finding the active agent believes fails a gate, authority still holds: do not drop it — tag it [Possible FP] with the refuting evidence so the user decides. Boundary, null, empty, concurrency, retry, and partial-failure issues count only when a supported path actually reaches them. Low-severity findings are omitted unless the user asks for an exhaustive pass. Recommend the smallest concrete change that removes the risk — never speculative redesigns, abstractions, or "make this more robust" feedback. For documents and plans, prioritize what would make an implementer ship the wrong thing, force rework, or block execution. Plan severity order is fixed: contradicted or unverified load-bearing premises > instructions an executor would implement wrong > steps that cannot succeed as specified > guarantees with no enforcing check > everything else — and internal precision is never evidence of correctness.
Recurring failure modes, all from picking the wrong tool:
scripts/run-adversarial-review.sh.| Signal | Type | Reference |
|---|---|---|
.py, .ts, .tsx, .js extension | code | references/code-layers.md |
.md, .txt with code blocks as primary content | code | references/code-layers.md |
| Any design or spec document set | spec | references/doc-layers.md |
| Architecture docs, PRDs, migration plans | plan | references/doc-layers.md |
| Test/eval plans | plan with test-scope admission | references/doc-layers.md |
Test source — a path under tests/, test/, spec/, __tests__/, or a file named *.t, *.test.*, *.spec.*, test_*.py, *_test.py, *-e2e.sh | test — auto-selected, cap 1 | this table's note below |
| Mixed (code + significant prose) | code | Both, code-primary |
Test source is a different review, not a smaller one. A test
exists to prove one thing about something else; it has no users and nobody depends on its
internals. Pointed at one, the code prompt produces findings about the test's robustness
and edge cases — work nobody asked for, on the wrong artifact. REVIEW_KIND=test asks one
question instead: does this test prove what it claims to prove? Can it go red for the
right reason, can it go red for a wrong one, is the proof boundary the lowest sufficient
one, is it already proven elsewhere, does its setup guard fail loudly. Style, robustness,
refactoring and speculative extra cases are explicitly out of scope.
Three things follow, and the wrapper enforces all three so a caller cannot forget:
Production source keeps its own review: same prompt, same cap of 3, same loop. Re-reviewing a source file after a fix is the intended behaviour and nothing here touches it.
For spec / plan artifacts, run the bundled script with REVIEW_KIND=plan — it swaps in the plan-specific adversarial prompt (assets/adversarial-plan-review.prompt.md: load-bearing-claim classification, probe-evidence ground truth, mandatory coverage statement) instead of the code prompt.
For a test/eval plan, the review is an admission gate before test files are written. Review the final plan, not a sample or early draft, together with the behavior contract or bug report, a plan-hash receipt (a file holding the output of sha256sum <plan>, passed as an extra target), and relevant probe evidence. Apply the same rule to every row: admit it only when the current change can turn the test from green to red, a realistic supported path reaches it, the selected layer is the lowest sufficient proof boundary, existing proof does not duplicate or subsume it, and the command/environment is feasible. Reject unchanged unrelated behavior, unreachable states, framework or upstream behavior, fixed-ratio or quota filler, layered duplicates, and speculative cases. Return an explicit admitted/rejected disposition for every row and copy the exact plan SHA-256 from the receipt; a changed plan has no inherited approval.
If ambiguous, ask: "This looks like it could be reviewed as code or as a plan — which angle do you want?"
Review mode from user intent:
| User says | Mode |
|---|---|
| "review", "audit", "check" (default) | review-and-fix (code) or report-only (docs) |
| "just report", "don't fix" | report-only |
| "fix this", "clean this up" | review-and-fix |
| "review and suggest edits" | report-and-edit (docs only) |
Read every target file first. Then gather related context until you can answer: "If I were implementing this, what would confuse or trip me up?" The active agent uses this context to enrich authoritative findings, not to relitigate them.
Probe ground truth (plans/specs) — required, not optional. Before running the script, verify the plan's load-bearing empirical claims against reality — run the counts, check the files exist, dump the schemas, excerpt the referenced docs — and write the raw command output to a PROBE-RESULTS-<topic>.md file. Pass it as an additional target: the plan prompt treats probe files as authoritative reality and the plan's text as claims, which is what lets it mark premises CONTRADICTED instead of merely unverified.
Without one, the reviewer is sitting a closed-book exam: it is told not to use tools (Codex runs in a read-only sandbox, Claude with tools disabled), so it can catch internal contradictions and unenforced guarantees but cannot see that the document claims A while the code does B — and every empirical claim comes back UNVERIFIED rather than refuted. The wrapper warns on stderr and records probe:false when a plan review has no PROBE-RESULTS target; a review carrying that flag has established less than its verdict suggests, and the receipt says so. Skip the probe only when the artifact makes no empirical claims at all, and say that you did.
For code reviews, also read the bundled standards reference (references/python-standards.md for .py, references/ts-standards.md for .ts/.tsx). They are defaults: where the project's own conventions or configuration say otherwise, the project wins.
Read the relevant layers file (references/code-layers.md or references/doc-layers.md) first. The wrapper at scripts/run-adversarial-review.sh (next to this SKILL.md):
codex exec --skip-git-repo-check --sandbox read-only at high effort (the reviewed text arrives on stdin and the prompt forbids tools, so nothing needs write or full access); scripts/lib/sno-model.sh reads role reviewer from ${XDG_CONFIG_HOME:-$HOME/.config}/sno/models.toml. An absent file, missing role, or empty value means no model flag; the harness selects its current default. There is no model environment override or fallback name. To set a model, create the optional file with [models], reviewer = "model-name" and reviewer_claude = "model-name".FOCUS (focus area), REVIEW_KIND (code default | plan for specs/plans/PRDs), CODEX_EFFORT, and the reliability knobs below as env overrides;REVIEW_ARCHIVE_DIR (default ~/.local/state/codex-reviews/<date>/, path printed on stderr; override with REVIEW_OUTPUT_FILE=…) — read that file instead of grepping stdout for large files, since codex echoes the full inlined prompt.high is the default effort for Codex review passes, including reviews before merging. Use a different CODEX_EFFORT only when the caller explicitly requests it.
REVIEW_CALLER names the current executing agent: claude-code, codex, hermes, openclaw or cursor; other values exit 5. Do not use the agent that originally delegated the task. When unset, detection reads the caller's Reach seat (SNO_REACH_ADDR, seat.json launch command runtime), then the nearest known ancestor process, then CURSOR_AGENT, CODEX_THREAD_ID and CLAUDECODE in that order, then defaults to claude-code. Seat and process detection precede markers because a child agent can inherit its parent's markers. A seat's claude command maps to claude-code; cursor-agent maps to cursor.
For Hermes, OpenClaw or Cursor without a Reach seat, set REVIEW_AUTHOR_MODEL to your current model, including its provider when needed (for example REVIEW_CALLER=hermes REVIEW_AUTHOR_MODEL=openai/gpt-model). With a seat, the pick reads that seat's current model/provider instead. If the author model/vendor cannot be determined, the script refuses with exit 2 and records that no review ran; it never guesses. Claude Code and Codex have fixed vendors, Anthropic and OpenAI.
When the work under review was written by another agent — for example a Codex coder you dispatched, or an executor whose result you are auditing — also set REVIEW_AUTHOR to that agent's harness. It defaults to REVIEW_CALLER. It only labels who wrote the work in the uploaded review count; it never changes which reviewer runs.
Reviewer choice. A Cursor caller uses Codex when it is installed and logged in, otherwise Claude, subject to the same other-vendor requirement. The script calls sno subscription-quota-check --pick review --from <caller> [--seat <address> | --model <REVIEW_AUTHOR_MODEL>] --json and walks candidates[] in order, including the caller's own agent on another-vendor model. It uses the existing report archive and watchdog for each candidate. A failed candidate gives way to the next; when none answers on another vendor, stdout has no review text, the ledger records end/refused, and exit 2 says cross review was not possible: no reviewer on another vendor answered. Exit 2 also covers an unreadable prompt or malformed models.toml.
claude -p [--model <reviewer_claude>] --effort medium, model from role reviewer_claude in the same optional models.toml (an absent file, missing role, or empty value omits --model). It receives the file contents on stdin with tools disabled and writes its final text to the report file.hermes chat --query-file <prompt> --oneshot -Q --ignore-user-config --ignore-rules [-m <model> --provider <provider>]. The printed session_id is used with sno rotate-agent-answer-provider --agent hermes --session <id> to confirm the actual answering vendor.openclaw agent --agent main --session-id sno-oneshot-<unique-id> --model <provider/model> --message-file <prompt> --json. Review text comes from the JSON payloads; the answer provider is read using --agent openclaw --session-key agent:main:explicit:sno-oneshot-<unique-id>.REVIEW_REVIEWER=cursor to run cursor-agent -p --trust --mode ask --output-format json on Cursor's default model with the review text on stdin, in a throwaway home holding only the Cursor login and install and from an empty folder, so no Cursor hook fires and nothing reaches memory. The watchdog kills its whole process group. On macOS it needs the desktop session; over SSH a locked login keychain fails with Cursor CLI login unreadable (keychain locked). Its answering vendor must differ from the author's vendor.[sno-oneshot] Station one-shot run, not a user session. Hermes ignores user configuration/rules; OpenClaw's Station session key lets its memory plugin skip capture/injection. Payloads travel by stdin/file, not a large shell argument. All runners remove caller session markers.Choose the wave size; do not inherit it. MAX_FILES_PER_WAVE decides how many files one
reviewer sees at once, and the 8 in the script is a default nobody picked for your case — the
wrapper says on stderr whether the number was chosen or defaulted. The trade runs both
ways and only you know which side this review needs. Larger puts more files in front of
one reviewer, which is the only way a contradiction between files becomes visible:
cross-document contradictions are only reachable when both documents sit in one wave.
Smaller buys depth per file and less
dilution, which is what a correctness or security pass over dense source wants. Rough
starting points: a cross-document consistency review wants the whole set in one wave if it
fits under PAYLOAD_LIMIT; a security sweep over unrelated modules wants 2–3; the default 8
is for a batch of related files where neither concern dominates.
Reliability. Before launch, state the target file count, expected wave count, one response per wave, and projected runtime. The script prints progress every PROGRESS_SECS (default 30s), kills the whole reviewer process tree if stdout, stderr, and the final-message file show no activity for STALL_SECS (default 600s) or an attempt exceeds TIMEOUT_SECS (default 3600s). Codex/Claude retry stalls, timeouts, nonzero exits and empty final responses up to MAX_RETRIES (default 2, so 3 attempts) with bounded backoff; Hermes/OpenClaw get one attempt per candidate. A same-vendor or unknown-vendor answer is discarded immediately without retry. It clears the final-message file before every attempt; an accepted nonempty final answer is the canonical report. POLL_SECS and BACKOFF_BASE_SECS are also overridable. Reviews are safe to run in parallel: with Codex, reasoning summaries stream, so a live reviewer keeps resetting the stall counter. MAX_PARALLEL (default 8) caps how many reviews run at once machine-wide; over the cap the script exits 9 immediately rather than queueing — retry this file later. If the payload exceeds PAYLOAD_LIMIT (default 90KiB) or MAX_FILES_PER_WAVE (default 8), it auto-splits into waves; plan mode pins the first target into every wave. Exhausted candidates exit 2 and retain capture paths; a failed merged-report write exits 7. A legitimately quiet large run can need a larger STALL_SECS, but never replace this wrapper with an unbounded direct call.
A review that produced nothing must not read as a clean bill of health. Every invocation writes a start line to ~/.local/state/codex-review-stats.jsonl at dispatch and one terminal line — end or refused — when it stops; a start with no terminal line is a run that was killed, and is the only trace an uncatchable kill can leave.
review-receipt.sh reads that record; review-findings.sh keeps a separate findings ledger. Neither makes a reviewer call:
scripts/review-receipt.sh [--since <date|today|all>] [--journey <id>] [--check]
One row per invocation — status, round, verdict, finding count, targets — then a "not actually covered" section listing every run that was killed, failed, or refused. Run it after any fan-out of more than a couple of reviews, and before reporting what a batch covered. --check exits non-zero when anything is uncovered, so it can gate.
scripts/review-findings.sh list [--status undecided] [--file <substr>]
scripts/review-findings.sh set <id> <accepted|rejected|superseded> [note]
scripts/review-findings.sh prior <file>...
Findings are filed automatically by every successful review, keyed by reviewed file plus title, with a status column that starts undecided and only a human sets. A finding raised again in a later round updates its row rather than arriving as new — so the ledger keeps one row per finding across rounds. prior prints the settled (rejected/superseded) rulings for given files, size-capped on purpose; to show earlier rulings to the next review, save that output to a file and pass it as an extra target.
scripts/review-backfill.sh [--since <date>] recovers past Codex-reviewer reports from ~/.codex/sessions when report files were lost.
# <skill-dir> is the directory that holds this SKILL.md.
REVIEW_CALLER=claude-code bash <skill-dir>/scripts/run-adversarial-review.sh \
apps/foo/src/a.ts apps/foo/src/b.ts
# Focused review:
FOCUS="concurrency" REVIEW_CALLER=claude-code \
bash <skill-dir>/scripts/run-adversarial-review.sh apps/foo/src/a.ts
# Plan/spec review (plan-specific prompt + probe evidence):
REVIEW_KIND=plan REVIEW_CALLER=claude-code \
bash <skill-dir>/scripts/run-adversarial-review.sh \
docs/plans/migration-plan.md PROBE-RESULTS-migration.md
Paths outside the repo are fine (plans, manifests) — the script never asks the reviewer to read them. The structured Markdown review (verdict + findings + next steps) on stdout is the canonical output of this skill; parse it as the source of truth.
With findings in hand: enrich each with cross-file context, deepen to root cause, classify severity and layer per the layers reference, and preserve every finding. Prioritize Critical and High.
Scope-capped kickoff: when the user's request limits scope ("do not widen the scope", "fix only the necessary issues", "only fix serious problems", "don't touch unrelated code"), fix ONLY Critical/High findings; Medium/Low are reported, never fixed — no matter how easy they look. Every fixed line must trace to a Critical/High finding.
Code (review-and-fix): For each finding, before touching code: trace where the bad state originates (follow imports, callers, data flow); search for sibling instances of the same pattern elsewhere; classify fix depth — Local (isolated mistake), Structural (design flaw), Systemic (repeated pattern: when the user limited scope, fix the instance here and flag the rest; otherwise fix every instance once at the shared layer they route through). A Local classification states what was searched and why no sibling exists. Present the root-cause analysis to the user before applying fixes.
"Cut" is applied first, by deleting. By default (a project may choose otherwise) it is the one tier the active agent acts on without
asking, because removing unrequested behaviour cannot widen scope; if a Cut item is something the
owner explicitly asked for, keep it and say so. Then the "Must fix" tier is fixed. Walk it most-severe-first. Every "Also found" item is
recorded with review-findings.sh and listed to the user under its own heading; if one of them
looks worth building, say so as a proposal and stop — do not dispatch it, do not fold it into the
fix. A fix that would add new behaviour to close a "Must fix" item is a sign the item was
mis-tiered: report the tier disagreement instead of adding the behaviour.
Fix the cause rather than the symptom — a flagged missing null check means deciding whether the caller should guarantee non-null, the function should accept null, or the type should be narrowed upstream, not just adding if (x != null). Simple fixes (formatting, imports, single-line logic): smallest safe direct edit. Complex fixes (architecture, algorithms, security): implement locally after root-cause analysis; delegate to workers only with explicit user authorization. Run the repository's own lint/typecheck command after each batch of related fixes. Assign each issue a stable ID (C1, S1, …) to track persistence vs rephrasing across iterations.
Docs (report-only / report-and-edit): Documents have gaps, ambiguities, and risks rather than bugs — present findings grouped by severity using the doc format from references/doc-layers.md. In report-and-edit mode, show current text → proposed replacement → why it matters, and apply edits only after the user confirms; doc changes are often product decisions the reviewer shouldn't make unilaterally.
Documents and plans do not run confirmation loops. A plan/spec/doc gets at most five judgment
passes per window, counted across the plan, spec and doc kinds, any change of target set, and one
REVIEW_JOURNEY label (below) — a file may be judged up to five times in a day; set REVIEW_CAP_OVERRIDE=<reason> to lift it; the reason is recorded on the stats line.
After the last pass, fix current-slice blockers, record non-blockers, run deterministic
validation once, and implement. For code, re-run the same script invocation on the same
target paths, then decide. The wrapper enforces a hard cap on same-scope
invocations regardless of what reviews return: code = 3 (2 fix rounds + 1
confirmation), plan/spec/doc = 5, test = 1; test and planning caps bind beyond the exact target set — later rounds have diminishing returns. Over the cap the wrapper exits 10 and refuses another review. The caller continues
direct repair and targeted verification; this is not permission to stop the assignment. Changing REVIEW_KIND, adding/removing a companion
file, or inventing a planning override does not start a sixth planning pass; only REVIEW_CAP_OVERRIDE=<reason> does.
Every cap counts only the passes inside a rolling window — REVIEW_CAP_WINDOW_HOURS,
default 24. A cap is a loop-breaker for one working session. Counting all history would
retire an artifact from review for good after its facts were corrected, which is the opposite
of what a loop-breaker is for. Passes older than the window do not count against any cap, so
an artifact that genuinely changed weeks later gets a fresh review without an override and
without dodging the scope key. Within the window the cap binds in full.
REVIEW_CAP_WINDOW_HOURS=0 counts all history. If GNU
date -d is unavailable the window is disabled and the wrapper says so on stderr, so a
missing dependency fails closed rather than silently un-capping every review.
IF "Cut" and "Must fix" tiers are both empty:
-> APPROVE. Exit loop. Every "Also found" item (including a fix-now item whose only
remedy is new behaviour) is already filed in the findings ledger with status
`undecided` — it never blocks, never triggers a fix or a re-review here.
Rule on it with `review-findings.sh set`, or put it to the owner as a proposal.
ELSE IF round < cap:
-> Delete every "Cut" item, then fix the "Must fix" tier (Critical/High first), then re-review (focused on what changed)
ELSE (cap reached, "Must fix" still open):
-> End the review loop, not the task. Record "Also found" as debt.
Fix the verified "Must fix" defects through direct diagnosis and affected checks
under the existing execution authorization, still without adding behaviour. By default, do not
require a new brief or owner approval for ordinary repairs. Never claim a new
review ran when the cap refused it.
Labelled reviews (journey mode). A "journey" is one piece of work that gets several reviews. Setting REVIEW_JOURNEY=<label> groups every review of it under one name, so the planning cap counts across it. When it is set the wrapper refuses to run (exit 11) unless you also pass:
CHARTER= — the variable name is literal: one line, or a file path, saying what the change is for;FENCE= — the path of a file listing the paths the change may touch;
and you should also pass:CONTEXT_FILE= — a file describing the deployment context (trust model, platform, format invariants). Severity is judged against it, and a severity cap is legal only when it cites an entry there.Findings are classified by causality × delivery impact: fix-now (caused by the change, or verified to block the deliverable), debt (real, pre-existing, recorded not fixed), out-of-context (impossible under a cited context entry). Invocation statistics land in ~/.local/state/codex-review-stats.jsonl.
## Code Review Summary
**File(s)**: [list]
**Iterations**: [count]
**Issues**: [initial] -> [final] ([fixed] fixed)
**Source**: adversarial-review script, reviewer <claude-code|codex|hermes|openclaw|cursor-agent>, cross-vendor (authoritative)
### Reviewer Findings
#### [C1] [Issue title]
- **Severity**: Critical/High/Medium/Low
- **Category**: [Correctness/Security/Performance/Type Safety/Quality]
- **Root cause**: [Why this issue exists — the origin, not just the symptom]
- **Fix depth**: Local | Structural | Systemic
- **Fix applied**: [What changed and why it addresses the root cause]
- **Siblings**: [Other instances of the same pattern, if any]
### Active-Agent Supplementary Findings (if any)
[Non-authoritative, clearly labeled]
### Systemic Patterns
[Patterns recurring across findings — worth addressing at a higher level]
### Remaining (if any)
- [S2] [Issue title] (Severity) — why it couldn't be auto-fixed
### Next Steps
[Manual actions, systemic fixes beyond this file, tests to add]
## Document Review Summary
**Artifact**: [file path]
**Type**: [plan | spec]
**Issues**: [count by severity]
### Findings
#### [D1] [Finding Title]
- **Severity**: Critical/High/Medium/Low/Decision-Required
- **Layer**: [layer name]
- **Location**: [section heading or quote]
- **Problem**: [what's wrong or missing]
- **Evidence**: [the text that shows the issue]
- **Recommendation**: [specific change]
- **Decision needed?**: yes/no
### Strengths
[What the document does well]
### Top Priorities
1-3. [Most impactful findings]
User: Review src/orders/service.py # code, auto-fix loop
User: Check src/api/routes/*.ts for security # focused code review
User: Audit src/storage.py but just report # report-only
User: Review docs/design/foo.md # spec review
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.