Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .factory/droids/file-group-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ You are a senior staff software engineer and expert code reviewer.
Your task: Review the assigned files from the PR and generate a JSON array of **high-confidence, actionable** review comments that pinpoint genuine issues.

<review_guidelines>

- You are currently checked out to the PR branch.
- Review ALL files assigned to you thoroughly.
- Focus on: functional correctness, syntax errors, logic bugs, broken dependencies/contracts/tests, security issues, and performance problems.
Expand All @@ -24,7 +25,7 @@ Your task: Review the assigned files from the PR and generate a JSON array of **
- Type-assumption bugs (e.g., numeric ops on datetime/strings, ordering key type mismatches)
- Offset/cursor/pagination semantic mismatches (off-by-one, prev/next behavior, commit semantics)
- Only flag issues you are confident about—avoid speculative or stylistic nitpicks.
</review_guidelines>
</review_guidelines>

<workflow>
1. Read each assigned file in full to understand the context
Expand Down Expand Up @@ -57,6 +58,7 @@ Return your findings as a JSON array (no wrapper object, just the array):
If no issues found, return an empty array: `[]`

Field definitions:

- `path`: Relative file path (must match exactly as provided in your assignment)
- `body`: Comment text starting with priority tag [P0|P1|P2], then title, then 1 paragraph explanation
- P0: Critical bugs (crashes, security vulnerabilities, data loss)
Expand All @@ -65,7 +67,7 @@ Field definitions:
- `line`: Target line number (single-line) or end line number (multi-line). Must be ≥ 0.
- `startLine`: `null` for single-line comments, or start line number for multi-line comments
- `side`: "RIGHT" for new/modified code (default), "LEFT" only for commenting on removed code
</output_format>
</output_format>

<constraints>
- Output ONLY the JSON array—no additional commentary or markdown formatting around it.
Expand Down
60 changes: 15 additions & 45 deletions .github/workflows/droid-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ jobs:
outputs:
comment_id: ${{ steps.prepare.outputs.comment_id }}
run_code_review: ${{ steps.prepare.outputs.run_code_review }}
run_security_review: ${{ steps.prepare.outputs.run_security_review }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
Expand All @@ -25,7 +24,7 @@ jobs:

- name: Prepare
id: prepare
uses: Factory-AI/droid-action/prepare@v2
uses: Factory-AI/droid-action/prepare@vn/bug-fixes
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
automatic_review: true
Expand All @@ -40,14 +39,16 @@ jobs:
issues: write
id-token: write
actions: read
env:
ACTIONS_STEP_DEBUG: true
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1
fetch-depth: 0

- name: Run Code Review
uses: Factory-AI/droid-action/review@v2
uses: Factory-AI/droid-action/review@vn/bug-fixes
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
Expand All @@ -60,44 +61,22 @@ jobs:
path: ${{ runner.temp }}/code-review-results.json
if-no-files-found: ignore

security-review:
needs: prepare
if: needs.prepare.outputs.run_security_review == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Run Security Review
uses: Factory-AI/droid-action/security@v2
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
security_severity_threshold: medium
output_file: ${{ runner.temp }}/security-results.json

- name: Upload Results
- name: Upload debug artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: security-results
path: ${{ runner.temp }}/security-results.json
name: droid-review-debug-${{ github.run_id }}
path: |
~/.factory/**
${{ runner.temp }}/droid-prompts/**
if-no-files-found: ignore
retention-days: 7

combine:
needs: [prepare, code-review, security-review]
# Run combine when EITHER code review OR security review was executed
needs: [prepare, code-review]
if: |
always() &&
(needs.prepare.outputs.run_code_review == 'true' ||
needs.prepare.outputs.run_security_review == 'true')
needs.prepare.outputs.run_code_review == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -118,19 +97,10 @@ jobs:
path: ${{ runner.temp }}
continue-on-error: true

- name: Download Security Results
uses: actions/download-artifact@v4
with:
name: security-results
path: ${{ runner.temp }}
continue-on-error: true

- name: Combine Results
uses: Factory-AI/droid-action/combine@v2
uses: Factory-AI/droid-action/combine@vn/bug-fixes
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
tracking_comment_id: ${{ needs.prepare.outputs.comment_id }}
code_review_results: ${{ runner.temp }}/code-review-results.json
security_results: ${{ runner.temp }}/security-results.json
code_review_status: ${{ needs.code-review.result }}
security_review_status: ${{ needs.security-review.result }}
4 changes: 3 additions & 1 deletion .github/workflows/droid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ jobs:
issues: write
id-token: write
actions: read
env:
ACTIONS_STEP_DEBUG: true
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Run Droid Exec
uses: Factory-AI/droid-action@v2
uses: Factory-AI/droid-action@vn/bug-fixes
with:
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
8 changes: 2 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ inputs:
reasoning_effort:
description: "Override reasoning effort for review flows (passed to Droid Exec as --reasoning-effort). If empty and review_model is also empty, the action defaults internally to gpt-5.2 at high reasoning."
required: false
default: ""
default: "high"
review_use_validator:
description: "Enable two-pass review: generate candidate comments to JSON, then validate and post only approved ones."
required: false
Expand Down Expand Up @@ -373,7 +373,6 @@ runs:
DETAILED_PERMISSION_MESSAGES: "1"
FACTORY_API_KEY: ${{ inputs.factory_api_key }}


- name: Update comment with job link
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.droid_comment_id && always()
shell: bash
Expand Down Expand Up @@ -402,10 +401,7 @@ runs:
with:
name: droid-review-debug-${{ github.run_id }}
path: |
~/.factory/logs/droid-log-single.log
~/.factory/logs/console.log
~/.factory/sessions/*
~/.factory/droids/*
~/.factory/**
Copy link
Contributor

Choose a reason for hiding this comment

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

[P1] Uploading ~/.factory/** as an artifact can leak sensitive data

This action uploads the entire ~/.factory/** directory whenever Droid runs. Factory sessions/logs frequently contain full prompts, tool outputs, and configuration that can include tokens or other sensitive data; uploading everything increases the risk of accidental secret disclosure and can also create very large artifacts. Consider gating this behind an explicit debug input and/or limiting the artifact to a small, known-safe allowlist (and excluding sessions by default).

${{ runner.temp }}/droid-prompts/**
if-no-files-found: ignore
retention-days: 7
59 changes: 58 additions & 1 deletion review/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,27 @@ inputs:
review_model:
description: "Model to use for review"
required: false
default: ""
default: "gpt-5.2"
reasoning_effort:
description: "Reasoning effort for review (passed to Droid Exec as --reasoning-effort)"
required: false
default: "high"
output_file:
description: "Path to write review results JSON"
required: false
default: ""
review_use_validator:
description: "Enable two-pass review: generate candidate comments, then validate and post only approved ones."
required: false
default: "true"
review_candidates_path:
description: "Path to write review candidates JSON (pass 1)."
required: false
default: "${{ runner.temp }}/droid-prompts/review_candidates.json"
review_validated_path:
description: "Path to write review validated JSON (pass 2)."
required: false
default: "${{ runner.temp }}/droid-prompts/review_validated.json"

outputs:
conclusion:
Expand Down Expand Up @@ -68,6 +84,19 @@ runs:
DROID_COMMENT_ID: ${{ inputs.tracking_comment_id }}
REVIEW_MODEL: ${{ inputs.review_model }}
REVIEW_TYPE: "code"
REVIEW_USE_VALIDATOR: ${{ inputs.review_use_validator }}
REVIEW_CANDIDATES_PATH: ${{ inputs.review_candidates_path }}
REASONING_EFFORT: ${{ inputs.reasoning_effort }}
DROID_OUTPUT_FILE: ${{ inputs.output_file }}

- name: Setup Custom Droids
shell: bash
run: |
mkdir -p ~/.factory/droids
if [ -d "${{ github.action_path }}/../.factory/droids" ]; then
cp -r ${{ github.action_path }}/../.factory/droids/* ~/.factory/droids/
echo "Copied custom droids to ~/.factory/droids/"
fi

- name: Run Code Review
id: review
Expand All @@ -81,3 +110,31 @@ runs:
FACTORY_API_KEY: ${{ inputs.factory_api_key }}
GITHUB_TOKEN: ${{ steps.token.outputs.github_token }}
DROID_OUTPUT_FILE: ${{ inputs.output_file }}

- name: Prepare Validator
id: prepare_validator
if: steps.prompt.outputs.review_use_validator == 'true'
shell: bash
run: |
bun run ${{ github.action_path }}/../src/entrypoints/prepare-validator.ts
env:
GITHUB_TOKEN: ${{ steps.token.outputs.github_token }}
REVIEW_USE_VALIDATOR: ${{ inputs.review_use_validator }}
REVIEW_VALIDATED_PATH: ${{ inputs.review_validated_path }}
REVIEW_CANDIDATES_PATH: ${{ inputs.review_candidates_path }}
REVIEW_MODEL: ${{ inputs.review_model }}
REASONING_EFFORT: ${{ inputs.reasoning_effort }}
DROID_COMMENT_ID: ${{ inputs.tracking_comment_id }}

- name: Run Validator
id: validator
if: steps.prompt.outputs.review_use_validator == 'true'
shell: bash
run: |
bun run ${{ github.action_path }}/../base-action/src/index.ts
env:
INPUT_PROMPT_FILE: ${{ runner.temp }}/droid-prompts/droid-prompt.txt
INPUT_DROID_ARGS: ${{ steps.prepare_validator.outputs.droid_args }}
INPUT_MCP_TOOLS: ${{ steps.prepare_validator.outputs.mcp_tools }}
FACTORY_API_KEY: ${{ inputs.factory_api_key }}
GITHUB_TOKEN: ${{ steps.token.outputs.github_token }}
1 change: 1 addition & 0 deletions security/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ runs:
SECURITY_BLOCK_ON_CRITICAL: ${{ inputs.security_block_on_critical }}
SECURITY_BLOCK_ON_HIGH: ${{ inputs.security_block_on_high }}
REVIEW_TYPE: "security"
DROID_OUTPUT_FILE: ${{ inputs.output_file }}

- name: Run Security Review
id: review
Expand Down
6 changes: 6 additions & 0 deletions src/create-prompt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export type PromptCreationOptions = {
disallowedTools?: string[];
includeActionsTools?: boolean;
reviewArtifacts?: ReviewArtifacts;
outputFilePath?: string;
};

export async function createPrompt({
Expand All @@ -316,6 +317,7 @@ export async function createPrompt({
disallowedTools = [],
includeActionsTools = false,
reviewArtifacts,
outputFilePath,
}: PromptCreationOptions) {
try {
const droidCommentId = commentId?.toString();
Expand All @@ -328,6 +330,10 @@ export async function createPrompt({
reviewArtifacts,
);

if (outputFilePath) {
preparedContext.outputFilePath = outputFilePath;
}

await mkdir(`${process.env.RUNNER_TEMP || "/tmp"}/droid-prompts`, {
recursive: true,
});
Expand Down
34 changes: 33 additions & 1 deletion src/create-prompt/templates/review-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,5 +353,37 @@ In the submitted review body:

* State whether the changes are correct or incorrect
* Provide a 1-3 sentence overall assessment
`;
${
context.outputFilePath
? `
---

## Output File (REQUIRED)

After completing your review, you MUST write your findings to \`${context.outputFilePath}\` as a JSON file with this structure:

\`\`\`json
{
"type": "code-review",
"findings": [
{
"id": "CR-001",
"severity": "P0|P1|P2|P3",
"file": "path/to/file.ts",
"line": 55,
"side": "RIGHT",
"description": "Brief description of the issue",
"suggestion": "Optional suggested fix"
}
],
"summary": "Brief overall summary of the review"
}
\`\`\`

If no issues were found, write: \`{"type": "code-review", "findings": [], "summary": "No issues found"}\`

This file is required for the combine step to aggregate results.
`
: ""
}`;
}
2 changes: 1 addition & 1 deletion src/create-prompt/templates/security-review-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ You have access to these Factory security skills (installed in ~/.factory/skills
IMPORTANT: Do NOT post inline comments directly. Instead, write findings to a JSON file.
The finalize step will post all inline comments to avoid overlapping with code review comments.

1. Write findings to \`security-review-results.json\` with this structure:
1. Write findings to \`${context.outputFilePath || "security-review-results.json"}\` with this structure:
\`\`\`json
{
"type": "security",
Expand Down
1 change: 1 addition & 0 deletions src/create-prompt/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,5 @@ export type PreparedContext = CommonFields & {
headRefOid: string;
};
reviewArtifacts?: ReviewArtifacts;
outputFilePath?: string;
};
6 changes: 1 addition & 5 deletions src/entrypoints/generate-combine-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,7 @@ async function run() {
});

const droidArgParts: string[] = [];
// Only include built-in tools in --enabled-tools
const builtInTools = allowedTools.filter((t) => !t.includes("___"));
if (builtInTools.length > 0) {
droidArgParts.push(`--enabled-tools "${builtInTools.join(",")}"`);
}
droidArgParts.push(`--enabled-tools "${allowedTools.join(",")}"`);

if (normalizedUserArgs) {
droidArgParts.push(normalizedUserArgs);
Expand Down
Loading