Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
65616a8
feat(w2): validate full Qwen3 frontend and runtime contracts
yuki-328 Oct 3, 2026
63fd38b
feat(w3): validate full Qwen3 IR numerics and reproducible CI
yuki-328 Oct 5, 2026
3f486f4
merge: integrate upstream W1 fixes into W3 validation
yuki-328 Oct 5, 2026
b5a4f58
fix(ci): install pinned CPU dependencies for Qwen and W3 tests
yuki-328 Oct 5, 2026
2d07ccc
fix(w2): clean up owned gates on acceptance cancellation
yuki-328 Oct 5, 2026
52337fa
Merge branch 'codex/w2-qwen3-acceptance' into codex/w3-preparation
yuki-328 Oct 5, 2026
1d33dcb
fix(probes): publish W1 evidence safely and align acceptance status
yuki-328 Oct 5, 2026
2e5f9f4
ci(w3): expose opt-in full validation through registered workflow
yuki-328 Oct 5, 2026
ca110be
fix(w3): align CPU arithmetic and handle Linux worker exit sampling
yuki-328 Oct 5, 2026
8132f3c
fix(w3): share exact RoPE parameters between subgraph oracles
yuki-328 Oct 5, 2026
a1e6421
fix(w3): stabilize Haswell FP32 matrix accumulation
yuki-328 Oct 5, 2026
1c49e8a
feat(w3): record loaded NumPy OpenBLAS kernel provenance
yuki-328 Oct 5, 2026
77c3aa8
docs(w3): record complete Linux validation and downloaded evidence audit
yuki-328 Oct 5, 2026
22bd3b5
docs: standardize deployment reproduction on Linux
yuki-328 Oct 6, 2026
a17ba5f
docs: standardize deployment reproduction on Linux
yuki-328 Oct 6, 2026
6326237
docs(w3): deliver Linux-only reproduction guides and source handoff
yuki-328 Oct 6, 2026
f7ebbfa
docs: align shared setup guide with Linux reproduction
yuki-328 Oct 6, 2026
77b9045
docs: align shared setup guide with Linux reproduction
yuki-328 Oct 6, 2026
9397028
feat(reports): add alignment metrics and simplify probe summaries
yuki-328 Oct 8, 2026
f385d42
Merge upstream main and repair architecture shell examples
yuki-328 Oct 8, 2026
d4dc31b
feat(w4): validate full Qwen3 RV64 execution with external weights
yuki-328 Oct 9, 2026
2c82720
docs: disambiguate the kernel architecture guide
yuki-328 Oct 9, 2026
bae051f
fix(compiler): preserve bindings and ONNX operator semantics
yuki-328 Oct 9, 2026
52b3973
fix(probes): reject mixed sources and retry transient report locks
yuki-328 Oct 9, 2026
e9185b9
fix(w4): bind acceptance to fixed inputs and stable sources
yuki-328 Oct 9, 2026
7ba606a
test(w3): provide source evidence in medium probe success fixture
yuki-328 Oct 9, 2026
8fcc6ea
fix(w2): backport compact probe summaries to acceptance CI
yuki-328 Oct 11, 2026
d31618b
feat(ci): publish compact W3 acceptance summaries
yuki-328 Oct 11, 2026
ffcaaa2
fix(ci): keep W2 report availability explicit on later branches
yuki-328 Oct 11, 2026
44a5702
fix(ci): bound W3 summary size and malformed report handling
yuki-328 Oct 11, 2026
611958d
Merge upstream main to retain two-layer evidence publication fixes
yuki-328 Oct 11, 2026
611417a
docs: disambiguate the kernel architecture guide
yuki-328 Oct 9, 2026
210baa5
docs: disambiguate the kernel architecture guide
yuki-328 Oct 9, 2026
d615902
feat(ci): publish compact W4 acceptance metrics in Actions summaries
yuki-328 Oct 11, 2026
3526a45
feat(ci): publish compact W3 acceptance summaries
yuki-328 Oct 11, 2026
80de335
fix(ci): keep W2 report availability explicit on later branches
yuki-328 Oct 11, 2026
fa85e31
fix(ci): bound W3 summary size and malformed report handling
yuki-328 Oct 11, 2026
bf9caa1
docs: make architecture command examples valid Bash
yuki-328 Oct 11, 2026
6614ad9
fix(ci): expose explicit W4 validation through registered LLM workflow
yuki-328 Oct 11, 2026
4394c88
test(w3): publish worker readiness atomically
yuki-328 Oct 11, 2026
d1e50fd
test(w3): publish worker readiness atomically
yuki-328 Oct 11, 2026
701ac22
ci: consolidate PR validation and summaries into one workflow
yuki-328 Oct 11, 2026
f672b92
ci: consolidate PR validation and summaries into one workflow
yuki-328 Oct 11, 2026
4b27889
ci: consolidate PR gates and keep full QEMU explicitly opt-in
yuki-328 Oct 11, 2026
06f427c
Merge upstream main and preserve PR106 W2 summary refinements
yuki-328 Oct 13, 2026
1f6d06d
fix(w2): clarify optimization and execution evidence descriptions
yuki-328 Oct 13, 2026
0f097b6
fix(runtime): honor standard Zig cache for external builds
yuki-328 Oct 13, 2026
36db3a4
merge: sync W3 with merged W2 acceptance and summary fixes
yuki-328 Oct 13, 2026
8dd4565
docs(w3): align acceptance records with executed Linux revisions
yuki-328 Oct 13, 2026
6f65c8b
Merge updated W3 acceptance and main CI into W4
yuki-328 Oct 13, 2026
3cebd77
test(w3): align summary assertions with shared result wording
yuki-328 Oct 13, 2026
1e95bc5
Merge commit '3cebd776607c15d27c3cc0abe972961edc04bc06' into codex/w4…
yuki-328 Oct 13, 2026
af5768c
Merge upstream main after W3 #96 and clarify W4 dependencies
yuki-328 Oct 18, 2026
ed90143
Merge main and integrate W3 fixes with W4 parallel execution
yuki-328 Oct 22, 2026
6a9c714
Merge main hosted-runner migration without restoring duplicate CI
yuki-328 Oct 22, 2026
069d4c8
fix(ci): paginate AI reviews and account for every batch result
yuki-328 Oct 22, 2026
b77e45f
fix(runtime): keep Host C compiler descendants under supervisor owner…
yuki-328 Oct 22, 2026
234e114
docs(w4): publish soft-simulation optimization report
yuki-328 Oct 22, 2026
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
341 changes: 139 additions & 202 deletions .github/workflows/ai-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,234 +2,171 @@ name: AI Code Review

on:
workflow_dispatch:
inputs:
pr_number:
description: Pull request number in this repository
required: true
type: number
pull_request_target:
branches: [main]

permissions:
contents: read
pull-requests: write

concurrency:
group: ai-review-${{ github.repository }}-${{ github.event.pull_request.number || inputs.pr_number }}
cancel-in-progress: true

jobs:
ai-code-review:
prepare:
runs-on: ubuntu-latest
timeout-minutes: 15

timeout-minutes: 10
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
# pull_request_target checks out the trusted base; never run PR head code with secrets.
# workflow_dispatch uses the explicitly selected trusted ref.
- uses: actions/checkout@v4
# default checkout in pull_request_target = base branch
# only needed for reading .github/prompts/code-review-prompt.md

- name: Fetch PR diff data
id: fetch
with:
persist-credentials: false
- name: Fetch every PR file
uses: actions/github-script@v7
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
# 一次性获取所有文件的 patch 数据,存为 JSON 避免重复调用 API
gh api "/repos/${{ github.repository }}/pulls/$PR_NUMBER/files" > pr_files.json
jq -r '.[].filename' pr_files.json > changed_files.txt
COUNT=$(wc -l < changed_files.txt)
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
echo "Changed files ($COUNT):"
cat changed_files.txt
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
with:
script: |
const fs = require('fs');
fs.mkdirSync('review-plan', {recursive: true});
const data = {repository: context.repo.owner + '/' + context.repo.repo, errors: []};
try {
const pull_number = Number(process.env.PR_NUMBER);
if (!Number.isSafeInteger(pull_number) || pull_number <= 0) throw new Error('Invalid PR number');
data.pr_number = pull_number;
const params = {...context.repo, pull_number};
const before = (await github.rest.pulls.get(params)).data;
data.head = before.head.sha;
data.changed_files = before.changed_files;
data.files = await github.paginate(github.rest.pulls.listFiles, {...params, per_page: 100});
const after = (await github.rest.pulls.get(params)).data;
data.head_after = after.head.sha;
} catch (error) {
data.errors.push('GitHub PR fetch failed; see failed step status.');
core.setFailed('Could not fetch a complete PR file manifest.');
} finally {
fs.writeFileSync('review-plan/raw.json', JSON.stringify(data));
}
- name: Plan bounded batches
id: plan
if: always()
run: python3 scripts/ai_code_review.py plan --input review-plan/raw.json --output review-plan/plan.json
- uses: actions/upload-artifact@v4
if: always()
with:
name: ai-review-plan
path: review-plan/
if-no-files-found: error
overwrite: true

- name: AI Code Review
id: review
review:
needs: prepare
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
fail-fast: false
max-parallel: 1
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
with:
name: ai-review-plan
path: review-plan
- name: Review batch
env:
SENSENOVA_API_KEY: ${{ secrets.SENSENOVA_API_KEY }}
REVIEW_BATCH: ${{ matrix.batch }}
run: |
# ── Inline system prompt ──
SYSTEM_PROMPT=$(cat .github/prompts/code-review-prompt.md)

REVIEW_FILE="review_output.md"
: > "$REVIEW_FILE"
HAS_REVIEW=false
FILE_COUNT=0
MAX_REVIEW_FILES=10
SKIPPED_FILES=""
CHANGED_FILES=$(cat changed_files.txt)

# ── Check API key early ──
if [ -z "$SENSENOVA_API_KEY" ] || [ "${#SENSENOVA_API_KEY}" -lt 8 ]; then
echo "::error::SENSENOVA_API_KEY is not set or invalid. Aborting."
exit 1
fi

if [ -z "$CHANGED_FILES" ]; then
echo "No files changed, skipping review." >> "$REVIEW_FILE"
else
while IFS= read -r FILE; do
NL=$'\n'
[ -z "$FILE" ] && continue
if [ "${FILE_COUNT}" -ge "${MAX_REVIEW_FILES}" ]; then
echo " [SKIP] $FILE — max ${MAX_REVIEW_FILES} files reached"
SKIPPED_FILES="${SKIPPED_FILES} - $FILE${NL}"
continue
fi
echo "Reviewing: $FILE"

# ── Get per-file diff from cached JSON ──
DIFF=$(jq -r '.[] | select(.filename == "'"$FILE"'") | .patch // empty' pr_files.json)

# ── Skip if binary or not diffable (patch is null/empty) ──
if [ -z "$DIFF" ]; then
echo " [SKIP] $FILE — binary or not diffable (no patch from API)"
continue
fi

# ── Build user message ──
USER_MSG="请 review 以下文件变更:${NL}${NL}文件: $FILE${NL}${NL}\`\`\`diff${NL}$DIFF${NL}\`\`\`"

# ── Escape for JSON ──
# Use jq to build the payload safely
PAYLOAD=$(jq -n \
--arg model "sensenova-6.8-flash-lite" \
--arg system "$SYSTEM_PROMPT" \
--arg user "$USER_MSG" \
'{
model: $model,
messages: [
{role: "system", content: $system},
{role: "user", content: $user}
],
stream: false
}') || { echo " [ERROR] Failed to build JSON payload for $FILE"; continue; }

# ── Call SenseNova API ──
if [ "${FILE_COUNT:-0}" -gt 0 ]; then
sleep 1 # rate-limit: 1s gap between files
fi
RESPONSE=$(curl -s --max-time 90 --retry 0 -w "\n%{http_code}" \
"https://token.sensenova.cn/v1/chat/completions" \
-H "Authorization: Bearer $SENSENOVA_API_KEY" \
-H "Content-Type: application/json" \
-d "$PAYLOAD") || { echo " [WARN] curl failed for $FILE (exit $?) — skipped (no result)"; continue; }

HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')

if [ "$HTTP_CODE" != "200" ]; then
echo " [ERROR] HTTP $HTTP_CODE for $FILE"
echo " Response: $(echo "$BODY" | head -3)"
continue
fi

# ── Extract review text from response ──
REVIEW_TEXT=$(echo "$BODY" | jq -r '.choices[0].message.content // empty')
if [ -z "$REVIEW_TEXT" ]; then
echo " [WARN] Empty response for $FILE"
continue
fi

# ── Append to review output ──
{
echo "## 📁 \`$FILE\`"
echo ""
echo "$REVIEW_TEXT"
echo ""
echo "---"
echo ""
} >> "$REVIEW_FILE"

HAS_REVIEW=true
FILE_COUNT=$((FILE_COUNT + 1))
echo " [OK] Review completed for $FILE"

done <<< "$CHANGED_FILES"
fi

# ── Final summary ──
{
echo "# 🤖 AI Code Review"
echo ""
echo "> 共审查 **${FILE_COUNT}** 个变更文件"
if [ -n "$SKIPPED_FILES" ]; then
SKIP_COUNT=$(echo "$SKIPPED_FILES" | grep -c ' - ' || true)
echo "> ⚠️ 另有 **${SKIP_COUNT}** 个文件超过上限(最多 ${MAX_REVIEW_FILES} 个)未审查"
fi
echo ""
if [ "$HAS_REVIEW" = true ]; then
cat "$REVIEW_FILE"
else
echo "No reviewable changes found (all files binary, unchanged, or empty)."
fi
if [ -n "$SKIPPED_FILES" ]; then
echo ""
echo "---"
echo "### ⚠️ 未审查的文件"
printf '%b' "$SKIPPED_FILES"
fi
} > "${REVIEW_FILE}.tmp"
mv "${REVIEW_FILE}.tmp" "$REVIEW_FILE"

echo "Review written to $REVIEW_FILE"

- name: Upload review artifact
uses: actions/upload-artifact@v4
python3 scripts/ai_code_review.py review \
--plan review-plan/plan.json --batch "$REVIEW_BATCH" \
--prompt .github/prompts/code-review-prompt.md \
--output "review-batch/batch-$REVIEW_BATCH.json"
- uses: actions/upload-artifact@v4
if: always()
with:
name: ai-code-review
path: review_output.md
if-no-files-found: ignore
name: ai-review-batch-${{ matrix.batch }}
path: review-batch/
if-no-files-found: error
overwrite: true

- name: Post review comment
ai-code-review:
needs: [prepare, review]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
continue-on-error: true
with:
pattern: ai-review-*
path: review-downloads
- name: Account for every file
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
python3 scripts/ai_code_review.py summarize \
--directory review-downloads --output review-summary --run-url "$RUN_URL"
- name: Check current head and post coverage summary
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const reviewFile = 'review_output.md';

if (!fs.existsSync(reviewFile)) {
console.log('No review output file found, skipping comment.');
const pull_number = Number(process.env.PR_NUMBER);
if (!Number.isSafeInteger(pull_number) || pull_number <= 0) {
core.setFailed('Invalid PR number');
return;
}

const reviewBody = fs.readFileSync(reviewFile, 'utf8').trim();
if (!reviewBody) {
console.log('Empty review output, skipping comment.');
return;
}

const pr = context.payload.pull_request;

// For pull_request events, get the PR number
let issueNumber;
if (pr) {
issueNumber = pr.number;
} else {
console.log('Not a pull request event, skipping comment.');
const summaryPath = 'review-summary/summary.json';
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
const pr = (await github.rest.pulls.get({...context.repo, pull_number})).data;
if (summary.head && pr.head.sha !== summary.head) {
summary.complete = false;
summary.errors.push('PR head changed after planning; results are stale.');
fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2));
for (const path of ['review-summary/review_output.md', 'review-summary/review_comment.md']) {
const old = fs.readFileSync(path, 'utf8');
fs.writeFileSync(path, '> STALE: PR head changed; this review does not cover the current PR.\n\n' +
old.replace('Coverage: **complete**', 'Coverage: **INCOMPLETE**'));
}
core.setFailed('Stale review results; not overwriting the current PR comment.');
return;
}

// Check if we already have a review comment from this workflow
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
per_page: 100,
const body = fs.readFileSync('review-summary/review_comment.md', 'utf8');
const comments = await github.paginate(github.rest.issues.listComments, {
...context.repo, issue_number: pull_number, per_page: 100,
});

const botComment = comments.data.find(c =>
c.user.type === 'Bot' &&
c.body.startsWith('# 🤖 AI Code Review')
);

if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: reviewBody,
});
console.log('Updated existing review comment.');
const previous = comments.find(c => c.user.type === 'Bot' && c.body.startsWith('# 🤖 AI Code Review'));
if (previous) {
await github.rest.issues.updateComment({...context.repo, comment_id: previous.id, body});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: reviewBody,
});
console.log('Created new review comment.');
await github.rest.issues.createComment({...context.repo, issue_number: pull_number, body});
}
await core.summary.addRaw(body).write();
- name: Upload full results even when coverage is incomplete
uses: actions/upload-artifact@v4
if: always()
with:
name: ai-code-review
path: review-summary/
if-no-files-found: error
overwrite: true
- name: Require complete review coverage
if: always()
run: python3 scripts/ai_code_review.py gate --summary review-summary/summary.json
Loading
Loading