diff --git a/examples/dark-factory/coder/entrypoint.js b/examples/dark-factory/coder/entrypoint.js index 79b421cf..d7e87bc0 100644 --- a/examples/dark-factory/coder/entrypoint.js +++ b/examples/dark-factory/coder/entrypoint.js @@ -256,11 +256,25 @@ function runCoder(repoDir) { // Inherit stdio so the coder CLI's own output + errors stream into the pod // logs (kubectl logs), instead of being swallowed by execFileSync's exception. const opts = { cwd: repoDir, env, stdio: "inherit", maxBuffer: 64 * 1024 * 1024 }; - const prompt = `Implement the change described in ${WORKSPACE}/SPEC.md. Build and run unit tests until green. Commit your work.`; + // The coder also writes a concise, human-readable summary of WHAT it changed to + // artifacts/description.md — this becomes the "Changes" section of the PR body + // (in addition to the verification section). Keep it short: what changed + why, + // as reviewer-facing markdown bullets. + const descPath = `${WORKSPACE}/artifacts/description.md`; + const prompt = + `Implement the change described in ${WORKSPACE}/SPEC.md. Build and run unit tests until green. Commit your work. ` + + `Then write a concise description of the changes you made (what changed and why, as a few markdown bullet points, ` + + `reviewer-facing — no preamble) to ${descPath}.`; if (ENGINE === "kiro") { // Kiro CLI headless — the coder image carries the `kiro` binary; it reads the // same Bifrost/Bedrock env above. --headless drives it non-interactively. + // Append the description instruction so kiro produces the same artifact. console.log("[coder] engine=kiro (kiro run --headless)"); + try { + fs.appendFileSync(`${WORKSPACE}/SPEC.md`, + `\n\n---\n\n## After implementing\n\nWrite a concise description of the changes you made ` + + `(what changed and why, a few reviewer-facing markdown bullets, no preamble) to ${descPath}.\n`); + } catch { /* non-fatal */ } return execFileSync("kiro", ["run", "--headless", "--spec", `${WORKSPACE}/SPEC.md`], opts); } console.log("[coder] engine=claude (claude -p)"); @@ -429,9 +443,17 @@ async function main() { ? "- ⏳ **Security review (AWS Security Agent):** _queued (DevOps cleared)…_" : "- ⬜ **Security review (AWS Security Agent):** _waiting on DevOps clearance_"; } + // Coder-authored description of the changes (artifacts/description.md). Shown + // as a "Changes" section ahead of the verification block. Falls back to a + // neutral line if the coder didn't produce one, so the PR body is never empty. + const desc = readSecret(`${WORKSPACE}/artifacts/description.md`); + const changesSection = desc + ? ["### 📝 Changes", "", desc, ""] + : ["### 📝 Changes", "", "_Implemented per the linked issue; see the diff for details._", ""]; const prBody = [ `Closes #${ISSUE}.`, "", + ...changesSection, "", "### 🏭 Dark Factory — verification", `- ✅ **Build + unit tests:** ${test.summary}`, diff --git a/gitops/addons/charts/dark-factory/scripts/security-agent.sh b/gitops/addons/charts/dark-factory/scripts/security-agent.sh index 8dc938bb..9601df42 100644 --- a/gitops/addons/charts/dark-factory/scripts/security-agent.sh +++ b/gitops/addons/charts/dark-factory/scripts/security-agent.sh @@ -95,27 +95,53 @@ JOBID="$(aws securityagent start-code-review-job --region "$AWS_REGION" \ log "codeReviewJobId=${JOBID} — polling (timeout ${POLL_TIMEOUT}s)..." # ── 4. Poll to completion ──────────────────────────────────────────────────── +# The job's `status` field lags well behind the actual analysis: the AWS Security +# Agent GitHub App posts its findings comment on the PR (e.g. "No issues identified") +# minutes before batch-get-code-review-jobs flips to COMPLETED. Polling status alone +# therefore blocks this (advisory) step for the full timeout even though the result +# is already known. So we ALSO probe list-findings each iteration: once it returns a +# well-formed result (findingsSummaries key present), the review has produced output +# and we can proceed immediately — this is the early-exit that avoids the long wait. DEADLINE=$(( $(date +%s) + POLL_TIMEOUT )) STATUS="IN_PROGRESS" +DONE="" +: > "$WORK/findings.json" while [ "$(date +%s)" -lt "$DEADLINE" ]; do STATUS="$(aws securityagent batch-get-code-review-jobs --region "$AWS_REGION" \ --agent-space-id "$AGENT_SPACE_ID" --code-review-job-ids "$JOBID" \ --query 'codeReviewJobs[0].status' --output text 2>/dev/null || echo IN_PROGRESS)" log "job status=${STATUS}" case "$STATUS" in - COMPLETED|SUCCEEDED) break ;; + COMPLETED|SUCCEEDED) DONE="status"; break ;; FAILED|STOPPED|ERROR) log "review job ${STATUS}"; post_status "error" "security: review job ${STATUS}"; exit 0 ;; esac + # Early-exit: findings ready before status flips? (App bot already posted them.) + if aws securityagent list-findings --region "$AWS_REGION" \ + --agent-space-id "$AGENT_SPACE_ID" --code-review-job-id "$JOBID" \ + > "$WORK/findings.try.json" 2>/dev/null \ + && python3 -c 'import json,sys; sys.exit(0 if "findingsSummaries" in json.load(open(sys.argv[1])) else 1)' "$WORK/findings.try.json" 2>/dev/null; then + mv "$WORK/findings.try.json" "$WORK/findings.json" + DONE="findings"; log "findings ready (status=${STATUS}) — proceeding without waiting for status flip"; break + fi sleep 20 done -if [ "$STATUS" != "COMPLETED" ] && [ "$STATUS" != "SUCCEEDED" ]; then - log "timed out waiting for review (last=${STATUS})"; post_status "error" "security: review timed out"; exit 0 +if [ -z "$DONE" ]; then + # Advisory step: the App bot posts the authoritative result on the PR regardless, + # so a slow job-status flip is NOT a failure. Post a neutral pending status (not + # error) so the PR check isn't a misleading red, and continue. + log "review still running past ${POLL_TIMEOUT}s (last status=${STATUS}) — see the AWS Security Agent bot comment on the PR for the authoritative result" + post_status "pending" "security: review still running — see AWS Security Agent PR comment" + exit 0 fi # ── 5. Fetch findings, render report + verdict (python3, no node) ──────────── -aws securityagent list-findings --region "$AWS_REGION" \ - --agent-space-id "$AGENT_SPACE_ID" --code-review-job-id "$JOBID" \ - > "$WORK/findings.json" 2>/dev/null || echo '{"findingsSummaries":[]}' > "$WORK/findings.json" +# Reuse the findings we already fetched during the early-exit probe; only re-fetch +# if we broke on the status flip (findings not yet captured). +if [ "$DONE" = "status" ] || [ ! -s "$WORK/findings.json" ]; then + aws securityagent list-findings --region "$AWS_REGION" \ + --agent-space-id "$AGENT_SPACE_ID" --code-review-job-id "$JOBID" \ + > "$WORK/findings.json" 2>/dev/null || echo '{"findingsSummaries":[]}' > "$WORK/findings.json" +fi python3 - "$WORK/findings.json" "$BLOCK_LEVEL" "$WORK/report.md" > "$WORK/verdict.env" <<'PY' import json, sys