Skip to content

ci: bump actions/deploy-pages from 4 to 5 #68

ci: bump actions/deploy-pages from 4 to 5

ci: bump actions/deploy-pages from 4 to 5 #68

Workflow file for this run

name: AI PR Review
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
# Checkout base repo (not PR head) for safe access to CLAUDE.md and proposals.
# The diff is fetched via GitHub API, not from the PR code.
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 1
- name: Get PR diff
id: diff
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-full.diff
FULL_SIZE=$(wc -c < /tmp/pr-full.diff)
echo "diff_size=$FULL_SIZE" >> "$GITHUB_OUTPUT"
# Truncate to ~80k chars to stay within API limits
if [ "$FULL_SIZE" -gt 80000 ]; then
head -c 80000 /tmp/pr-full.diff > /tmp/pr.diff
echo "truncated=true" >> "$GITHUB_OUTPUT"
else
cp /tmp/pr-full.diff /tmp/pr.diff
echo "truncated=false" >> "$GITHUB_OUTPUT"
fi
- name: Get PR info
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr view ${{ github.event.pull_request.number }} --json title,body > /tmp/pr-info.json
- name: Gather project context
run: |
# Read full CLAUDE.md (contains architecture, review checklist, key concepts)
if [ -f "CLAUDE.md" ]; then
cat CLAUDE.md > /tmp/project-context.txt
else
echo "No CLAUDE.md found." > /tmp/project-context.txt
fi
# List proposals
if [ -d "docs/03-development/proposals" ]; then
ls docs/03-development/proposals/ 2>/dev/null | head -20 > /tmp/proposals.txt
else
echo "No proposals directory." > /tmp/proposals.txt
fi
# List existing doctype test files for overlap checking
if [ -d "mdl-examples/doctype-tests" ]; then
ls mdl-examples/doctype-tests/ 2>/dev/null | head -30 > /tmp/doctype-tests.txt
else
echo "No doctype-tests directory." > /tmp/doctype-tests.txt
fi
- name: Build API request
env:
TRUNCATED: ${{ steps.diff.outputs.truncated }}
DIFF_SIZE: ${{ steps.diff.outputs.diff_size }}
run: |
TRUNCATION_NOTE=""
if [ "$TRUNCATED" = "true" ]; then
TRUNCATION_NOTE="NOTE: The diff was truncated to 80k characters. Total size: ${DIFF_SIZE} bytes. Focus on what is visible."
fi
# Write the user prompt to a file (avoids shell variable limits)
cat > /tmp/user-prompt.txt <<PROMPT
Review this pull request.
PR Info:
$(cat /tmp/pr-info.json)
Project context (CLAUDE.md — contains architecture + review checklist):
$(cat /tmp/project-context.txt)
Proposals in repo:
$(cat /tmp/proposals.txt)
Existing doctype test files:
$(cat /tmp/doctype-tests.txt)
${TRUNCATION_NOTE}
Review against ALL of the following checklist items:
## Overlap & duplication
- Check proposals list above for existing proposals covering the same functionality
- Check if the diff introduces code that duplicates existing implementations (look for similar function names, command names, types)
- Check doctype test files above for existing test coverage of the feature area
- Flag if the PR re-documents already-shipped features as new
## MDL syntax design (for new or modified MDL syntax)
If the diff adds or modifies MDL syntax (grammar rules, keywords, statement types), check:
- Follows standard CREATE/ALTER/DROP/SHOW/DESCRIBE pattern — no custom verbs for CRUD operations
- Uses Module.Element qualified names everywhere — no bare names, no implicit module context
- Property lists use ( Key: value, ... ) format with colon separators, one per line
- Colon for property definitions (Key: value), AS for name-to-name mappings ('oldName' AS 'newName') — do not flag AS in mapping/renaming contexts as wrong
- Keywords are full English words — no abbreviations, no symbols for domain operations
- Statement reads as English — a business analyst can understand the intent
- No keyword overloading — each keyword has one consistent meaning
- Diff-friendly — adding one property produces a one-line diff
- Consistent with existing MDL patterns (check docs/01-project/MDL_QUICK_REFERENCE.md)
## Full-stack consistency (for MDL features)
New MDL commands or language features must be wired through the full pipeline. If the diff adds any MDL feature, check:
- Grammar rule added to MDLParser.g4 (and MDLLexer.g4 if new tokens)
- AST node type added in mdl/ast/
- Visitor handler in mdl/visitor/
- Executor handler in mdl/executor/
- LSP wiring (cmd/mxcli/lsp.go) if the feature adds formatting, diagnostics, or navigation
- DESCRIBE roundtrip — if the feature creates artifacts, DESCRIBE should output re-executable MDL
- VS Code extension (vscode-mdl/package.json) updated if new LSP capabilities added
## Test coverage
- New packages should have test files
- New executor commands should have MDL examples in mdl-examples/doctype-tests/
- Integration paths should be tested, not just helpers
- No time.Sleep for synchronization — use channels or polling with timeout
## Security & robustness
- Command injection, XSS, SQL injection, temp file issues
- Unix sockets should use restrictive permissions (0600)
- File I/O should not be in hot paths (event loops, per-keystroke handlers)
- No silent side effects on typos (e.g., auto-creating resources on misspelled names)
- Correct method receivers (pointer vs value) for mutations
## Scope & atomicity
- Each commit should do one thing — a feature, a bugfix, or a refactor, not a mix
- PR should be scoped to a single feature or concern — flag if description needs "and" between unrelated items
- Independent features should be in separate PRs
- Refactors touching many files should be their own commit
## Code quality
- Refactors should be applied consistently (look for old patterns still present)
- Manually maintained lists (keyword lists, type mappings) should be flagged as maintenance risks
- Design docs should match the actual implementation
## Bugs & correctness
- Logic errors, race conditions, resource leaks
- Error handling gaps
Structure your review as: Critical Issues, Moderate Issues, Minor Issues, What Looks Good, Recommendation (approve/request changes).
Only list sections that have findings — skip empty sections.
Diff:
$(cat /tmp/pr.diff)
PROMPT
# Build JSON payload using jq with file input (no shell variable limits)
jq -n --rawfile prompt /tmp/user-prompt.txt '{
model: "nvidia/nemotron-3-super-120b-a12b:free",
messages: [
{
role: "system",
content: "You are a code reviewer for a Go CLI project (mxcli) that reads/modifies Mendix application projects. Key patterns: ANTLR4 grammar → AST → visitor → executor → BSON writer. New MDL features MUST be wired through the full pipeline (grammar → AST → visitor → executor → LSP → DESCRIBE roundtrip). New MDL syntax must follow design guidelines: reads as English (target audience is citizen developers), uses standard CRUD verbs (CREATE/ALTER/DROP/SHOW/DESCRIBE), consistent property format (Key: value) for property definitions, AS for name-to-name mappings ('old' AS 'new'), qualified names (Module.Element), no keyword overloading, no symbolic syntax. IMPORTANT: colon is for property definitions (Key: value), AS is for name mappings/renaming — do not flag AS in mapping contexts as a syntax violation. Generated ANTLR parser files (mdl/grammar/parser/) are noise — note but skip. The project context includes the full CLAUDE.md with architecture details and the review checklist. Review against the checklist thoroughly but concisely."
},
{
role: "user",
content: $prompt
}
],
max_tokens: 4000
}' > /tmp/request.json
echo "Request payload size: $(wc -c < /tmp/request.json) bytes"
- name: Call OpenRouter API
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
run: |
if [ -z "$OPENROUTER_API_KEY" ]; then
echo "::warning::OPENROUTER_API_KEY secret is not set"
exit 0
fi
HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/response.json -X POST \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/request.json \
https://openrouter.ai/api/v1/chat/completions)
echo "HTTP status: $HTTP_CODE"
if [ "$HTTP_CODE" != "200" ]; then
echo "::warning::OpenRouter API returned HTTP $HTTP_CODE"
cat /tmp/response.json | head -c 1000
exit 0
fi
REVIEW=$(jq -r '.choices[0].message.content // empty' /tmp/response.json)
if [ -z "$REVIEW" ]; then
echo "::warning::AI review returned empty content. Response:"
cat /tmp/response.json | head -c 1000
exit 0
fi
# Save review for next step
echo "$REVIEW" > /tmp/review.txt
echo "Review generated ($(wc -c < /tmp/review.txt) bytes)"
- name: Post review comment
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ ! -f /tmp/review.txt ]; then
echo "No review to post."
exit 0
fi
# Build comment body
{
echo "## AI Code Review"
echo ""
cat /tmp/review.txt
echo ""
echo "---"
echo "*Automated review via OpenRouter (Nemotron Super 120B) — [workflow source](${{ github.server_url }}/${{ github.repository }}/blob/main/.github/workflows/ai-review.yml)*"
} > /tmp/comment.md
gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/comment.md