diff --git a/.archon/workflows/defaults/archon-adversarial-dev.yaml b/.archon/workflows/defaults/archon-adversarial-dev.yaml index 2ab207dc03..9b5bdbd4ee 100644 --- a/.archon/workflows/defaults/archon-adversarial-dev.yaml +++ b/.archon/workflows/defaults/archon-adversarial-dev.yaml @@ -18,6 +18,7 @@ model: sonnet nodes: # ─── Phase 1: Planning ─────────────────────────────────────────────── - id: plan + model: opus prompt: | You are a product planning expert. Your job is to take a short user prompt and expand it into a comprehensive product specification. diff --git a/.archon/workflows/defaults/archon-architect.yaml b/.archon/workflows/defaults/archon-architect.yaml index a41a75cd33..e57c74d839 100644 --- a/.archon/workflows/defaults/archon-architect.yaml +++ b/.archon/workflows/defaults/archon-architect.yaml @@ -253,6 +253,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: fix-failures + model: haiku prompt: | Review the validation output below. @@ -298,6 +299,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: create-pr + model: haiku prompt: | Create a pull request for the architectural improvements. diff --git a/.archon/workflows/defaults/archon-create-issue.yaml b/.archon/workflows/defaults/archon-create-issue.yaml index 24d59f8e0c..354bf50396 100644 --- a/.archon/workflows/defaults/archon-create-issue.yaml +++ b/.archon/workflows/defaults/archon-create-issue.yaml @@ -433,6 +433,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: report-failure + model: haiku prompt: | The issue could not be reproduced. Report this to the user with actionable detail. @@ -557,6 +558,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: create-issue + model: haiku prompt: | Create the GitHub issue using the drafted content. diff --git a/.archon/workflows/defaults/archon-fix-github-issue.yaml b/.archon/workflows/defaults/archon-fix-github-issue.yaml index 12ad675de9..bea9a92145 100644 --- a/.archon/workflows/defaults/archon-fix-github-issue.yaml +++ b/.archon/workflows/defaults/archon-fix-github-issue.yaml @@ -26,6 +26,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: extract-issue-number + model: haiku prompt: | Find the GitHub issue number for this request. @@ -149,6 +150,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: create-pr + model: haiku prompt: | Create a draft pull request for the current branch. @@ -169,7 +171,7 @@ nodes: 4. Check if a PR already exists for this branch: `gh pr list --head $(git branch --show-current)` - If PR exists, skip creation and capture its number 5. Look for the project's PR template at `.github/pull_request_template.md`, `.github/PULL_REQUEST_TEMPLATE.md`, or `docs/PULL_REQUEST_TEMPLATE.md`. Read whichever one exists. - 6. Create a DRAFT PR: `gh pr create --draft --base $BASE_BRANCH` + 6. Create a PR: `gh pr create --base $BASE_BRANCH` - Title: concise, imperative mood, under 70 chars - Body: if a PR template was found, fill in **every section** with details from the artifacts. Don't skip sections or leave placeholders. If no template, write a body with summary, changes, validation evidence, and `Fixes #...`. - Link to issue: include `Fixes #...` or `Closes #...` @@ -308,3 +310,81 @@ nodes: command: archon-issue-completion-report depends_on: [simplify] context: fresh + + # ═══════════════════════════════════════════════════════════════ + # PHASE 11: MARK PR READY & TRIGGER CI + # ═══════════════════════════════════════════════════════════════ + + - id: prepare-merge + bash: | + PR_NUMBER=$(cat "$ARTIFACTS_DIR/.pr-number" 2>/dev/null || echo "") + if [ -z "$PR_NUMBER" ]; then + echo "No PR number found, skipping auto-merge" + exit 0 + fi + # Mark PR as ready for review (remove draft status) + gh pr ready "$PR_NUMBER" 2>/dev/null || true + # Push an empty commit to re-trigger CI on the final state. + # Prior steps (simplify, self-fix) may have pushed after the last CI run, + # leaving required status checks missing on HEAD. + git commit --allow-empty -m "ci: re-trigger checks for auto-merge" + git push + # Enable auto-merge early — GitHub will merge once CI passes. + # This way, if the watch-and-merge loop exits (max iterations), + # the PR still merges automatically when CI eventually goes green. + gh pr merge "$PR_NUMBER" --squash --auto --delete-branch 2>&1 || echo "Auto-merge not available, will retry in watch loop" + echo "$PR_NUMBER" + depends_on: [report] + + # ═══════════════════════════════════════════════════════════════ + # PHASE 12: WATCH CI, FIX FAILURES, AUTO-MERGE + # ═══════════════════════════════════════════════════════════════ + + - id: watch-and-merge + depends_on: [prepare-merge] + context: fresh + model: haiku + loop: + prompt: | + You are watching CI for a pull request. Auto-merge is already enabled — + GitHub will merge automatically once CI passes. Your job is to fix CI + failures so that auto-merge can proceed. + + ## Setup + + 1. Read the PR number from `$ARTIFACTS_DIR/.pr-number` + 2. If empty, output CI_DONE immediately. + + ## Check CI Status + + Run: `gh pr checks $(cat $ARTIFACTS_DIR/.pr-number) --watch --fail-fast 2>&1 || true` + + This blocks until all checks complete. Then check the exit code / output. + + ## If ALL checks passed + + Auto-merge is already enabled and GitHub will handle the merge. + Output: CI_DONE + + ## If any check FAILED + + 1. Identify which check failed from the output + 2. Get the failure logs: `gh run view --log-failed 2>&1 | tail -80` + 3. Read the relevant source files and fix the issue + 4. Run the project's local validation/test commands to verify your fix + 5. Commit the fix with a descriptive message and push: + ```bash + git add -A + git commit -m "fix: " + git push + ``` + 6. End this iteration normally (do NOT output CI_DONE) — the next iteration will re-check CI + + ## Important + + - Be concise. Focus on fixing the failure, not explaining it. + - Only fix what CI reports as broken — don't refactor or improve unrelated code. + - If the failure is environmental (flaky test, infra issue), retry once before trying to fix code. + until: CI_DONE + max_iterations: 4 + fresh_context: true diff --git a/.archon/workflows/defaults/archon-idea-to-pr.yaml b/.archon/workflows/defaults/archon-idea-to-pr.yaml index 9329c55021..16585d30cc 100644 --- a/.archon/workflows/defaults/archon-idea-to-pr.yaml +++ b/.archon/workflows/defaults/archon-idea-to-pr.yaml @@ -134,3 +134,18 @@ nodes: command: archon-workflow-summary depends_on: [implement-fixes] context: fresh + + # ═══════════════════════════════════════════════════════════════════ + # PHASE 9: AUTO-MERGE + # ═══════════════════════════════════════════════════════════════════ + + - id: auto-merge + bash: | + PR_NUMBER=$(cat "$ARTIFACTS_DIR/.pr-number" 2>/dev/null || echo "") + if [ -z "$PR_NUMBER" ]; then + echo "No PR number found, skipping auto-merge" + exit 0 + fi + gh pr ready "$PR_NUMBER" 2>/dev/null || true + gh pr merge "$PR_NUMBER" --squash --auto --delete-branch 2>&1 || echo "Auto-merge not available, PR left open for manual merge" + depends_on: [workflow-summary] diff --git a/.archon/workflows/defaults/archon-piv-loop.yaml b/.archon/workflows/defaults/archon-piv-loop.yaml index 7227900c2f..880c764ba2 100644 --- a/.archon/workflows/defaults/archon-piv-loop.yaml +++ b/.archon/workflows/defaults/archon-piv-loop.yaml @@ -614,6 +614,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: fix-feedback + model: haiku depends_on: [code-review] loop: prompt: | @@ -734,7 +735,7 @@ nodes: cat .github/pull_request_template.md 2>/dev/null || echo "NO_TEMPLATE" ``` - Create with `gh pr create --draft --base $BASE_BRANCH`: + Create with `gh pr create --base $BASE_BRANCH`: - Title from the plan's feature name - Body summarizing the implementation - Use a HEREDOC for the body diff --git a/.archon/workflows/defaults/archon-pr-maintenance.yaml b/.archon/workflows/defaults/archon-pr-maintenance.yaml new file mode 100644 index 0000000000..cfe9a47983 --- /dev/null +++ b/.archon/workflows/defaults/archon-pr-maintenance.yaml @@ -0,0 +1,171 @@ +name: archon-pr-maintenance +description: | + Use when: A single open PR needs rebasing, conflict resolution, or CI fixing. + Triggers: "fix stale PRs", "maintain PRs", "rebase open PRs", "merge open PRs", + "clean up PRs", "PR maintenance", "fix PR #123". + NOT for: Creating new PRs, reviewing code, fixing specific issues from scratch. + + Picks the highest-priority open PR needing attention (or a specific PR if given), + rebases it, resolves conflicts, fixes CI failures, and enables auto-merge. + Designed to be run on a cron — one PR per run, zero AI cost if nothing to do. + +provider: claude +model: sonnet + +nodes: + # ═══════════════════════════════════════════════════════════════ + # PHASE 1: FIND THE NEXT PR TO FIX (bash only — zero AI cost) + # ═══════════════════════════════════════════════════════════════ + + - id: find-pr + bash: | + # If a specific PR number was passed, use that + REQUESTED=$(echo "$ARGUMENTS" | grep -oE '[0-9]+' | head -1) + + if [ -n "$REQUESTED" ]; then + # Validate the PR exists and is open + STATE=$(gh pr view "$REQUESTED" --json state -q '.state' 2>/dev/null) + if [ "$STATE" = "OPEN" ]; then + echo "$REQUESTED" + exit 0 + else + echo "PR #$REQUESTED is not open (state: $STATE)" >&2 + exit 1 + fi + fi + + # No specific PR requested — find the highest priority actionable one. + # Priority: BEHIND (just needs rebase) > DIRTY (conflicts) > UNSTABLE (CI failing) + # Skip CLEAN (handled by wrapper script) and BLOCKED (can't do anything). + for STATUS in BEHIND DIRTY UNSTABLE UNKNOWN; do + PR=$(gh pr list --state open --json number,mergeStateStatus,isDraft \ + --jq "[.[] | select(.isDraft == false and .mergeStateStatus == \"$STATUS\")] | .[0].number // empty") + if [ -n "$PR" ]; then + echo "$PR" + exit 0 + fi + done + + echo "No PRs need maintenance" >&2 + exit 1 + timeout: 30000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 2: CHECKOUT & DIAGNOSE (bash only — zero AI cost) + # ═══════════════════════════════════════════════════════════════ + + - id: checkout-and-diagnose + bash: | + PR_NUMBER="$find-pr.output" + + # Checkout the PR branch + gh pr checkout "$PR_NUMBER" + + # Gather diagnostic info + echo "=== PR INFO ===" + gh pr view "$PR_NUMBER" --json number,title,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup \ + --jq '{number, title, head: .headRefName, base: .baseRefName, mergeable, mergeState: .mergeStateStatus, checks: [.statusCheckRollup[]? | {name, status, conclusion}]}' + + echo "" + echo "=== BEHIND COUNT ===" + BASE=$(gh pr view "$PR_NUMBER" --json baseRefName -q '.baseRefName') + git fetch origin "$BASE" --quiet + BEHIND=$(git rev-list --count HEAD.."origin/$BASE") + echo "$BEHIND commits behind $BASE" + + echo "" + echo "=== FILES CHANGED ===" + gh pr diff "$PR_NUMBER" --name-only 2>/dev/null | head -30 + depends_on: [find-pr] + timeout: 60000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 3: FIX THE PR (AI — only runs if phases 1-2 succeeded) + # ═══════════════════════════════════════════════════════════════ + + - id: fix-pr + prompt: | + You are a PR maintenance agent. Fix this single PR so it can merge. + + ## PR Diagnostics + + ``` + $checkout-and-diagnose.output + ``` + + ## Instructions + + You are already on the PR's branch. Follow these steps: + + ### Step 1: Rebase onto base branch + + ```bash + BASE=$(gh pr view $find-pr.output --json baseRefName -q '.baseRefName') + git rebase "origin/$BASE" + ``` + + If conflicts arise: + - Check conflicting files: `git diff --name-only --diff-filter=U` + - For each file, read the full file to understand the conflict markers + - Resolve based on intent: + - Both sides added different things → keep both + - One side updated, other didn't → keep the update + - Both changed same lines → understand the PR's intent from the title/diff and resolve accordingly + - Stage each resolved file: `git add ` + - Continue: `git rebase --continue` + - If the rebase has more than 5 conflicting files, abort (`git rebase --abort`) and report failure + + ### Step 2: Validate + + Run the project's validation. Check for these in order and run the first that exists: + ```bash + # Check what package manager / validation commands exist + if [ -f "pubspec.yaml" ]; then + flutter analyze && flutter test + elif [ -f "bun.lockb" ] || [ -f "bunfig.toml" ]; then + bun run type-check 2>/dev/null; bun test 2>/dev/null + elif [ -f "package.json" ]; then + npm test 2>/dev/null + elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then + python -m pytest 2>/dev/null + elif [ -f "go.mod" ]; then + go test ./... 2>/dev/null + fi + ``` + + If validation fails, fix the issue and re-run. If you can't fix it after one attempt, + proceed anyway — CI will catch it. + + ### Step 3: Push + + ```bash + git push --force-with-lease + ``` + + ### Step 4: Enable auto-merge + + ```bash + gh pr merge $find-pr.output --squash --auto --delete-branch 2>&1 || \ + echo "Auto-merge not available" + ``` + + If auto-merge is not available (no branch protection rules), check CI status + and merge directly once passing: + ```bash + gh pr checks $find-pr.output --watch --fail-fast 2>&1 || true + gh pr merge $find-pr.output --squash --delete-branch 2>&1 || \ + echo "Could not merge — may need manual review" + ``` + + ### Step 5: Report result + + Output a brief summary: PR number, what you did, whether it merged or has auto-merge enabled. + + ## Important + + - Be concise. Fix the problem, don't explain it at length. + - Only fix what's needed for the PR to merge — don't refactor or improve unrelated code. + - If a CI failure is environmental (flaky test, infra issue), push an empty commit to retry. + depends_on: [checkout-and-diagnose] + context: fresh + model: sonnet diff --git a/.archon/workflows/defaults/archon-ralph-dag.yaml b/.archon/workflows/defaults/archon-ralph-dag.yaml index 5c0d7c9099..18be2e5f18 100644 --- a/.archon/workflows/defaults/archon-ralph-dag.yaml +++ b/.archon/workflows/defaults/archon-ralph-dag.yaml @@ -528,7 +528,7 @@ nodes: If no template was found, write a summary with: problem, what changed, stories table, and validation evidence. - 3. **Create a draft PR** using `gh pr create --draft --base $BASE_BRANCH --title "feat: {PRD feature name}"` with the filled-in template as the body. Use a HEREDOC for the body. + 3. **Create a PR** using `gh pr create --base $BASE_BRANCH --title "feat: {PRD feature name}"` with the filled-in template as the body. Use a HEREDOC for the body. 4. **Output completion signal:** ``` diff --git a/.archon/workflows/defaults/archon-refactor-safely.yaml b/.archon/workflows/defaults/archon-refactor-safely.yaml index 56bc96ac36..4cc0723ecf 100644 --- a/.archon/workflows/defaults/archon-refactor-safely.yaml +++ b/.archon/workflows/defaults/archon-refactor-safely.yaml @@ -207,7 +207,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: execute-refactor - model: claude-opus-4-6[1m] + model: sonnet prompt: | You are executing a refactoring plan with strict safety guardrails. @@ -327,6 +327,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: fix-failures + model: haiku prompt: | Review the validation output below. @@ -430,6 +431,7 @@ nodes: # ═══════════════════════════════════════════════════════════════ - id: create-pr + model: haiku prompt: | Create a pull request for the refactoring. diff --git a/.archon/workflows/defaults/archon-security-audit.yaml b/.archon/workflows/defaults/archon-security-audit.yaml new file mode 100644 index 0000000000..ab2d00ab64 --- /dev/null +++ b/.archon/workflows/defaults/archon-security-audit.yaml @@ -0,0 +1,388 @@ +name: archon-security-audit +description: | + Use when: User wants a deep security and privacy audit of the codebase. + Triggers: "security audit", "privacy audit", "security review", "vulnerability scan", + "check for security issues", "owasp review", "pentest the code". + Does: Scans attack surface -> 5 parallel specialized security agents -> synthesize findings -> + auto-fix critical/high issues -> create PR for fixes, issues for the rest. + NOT for: Single-file fixes, feature development, architecture review. + +provider: claude + +nodes: + # ═══════════════════════════════════════════════════════════════ + # PHASE 1: ENUMERATE ATTACK SURFACE + # ═══════════════════════════════════════════════════════════════ + + - id: scan-surface + bash: | + echo "=== PROJECT STRUCTURE ===" + find . -name '*.py' -o -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' \ + | grep -v node_modules | grep -v .git | grep -v dist | grep -v __pycache__ | head -100 + + echo "" + echo "=== API ENDPOINTS ===" + # FastAPI/Flask routes + grep -rn '@app\.\(get\|post\|put\|delete\|patch\)\|@router\.\(get\|post\|put\|delete\|patch\)\|app\.route' \ + --include='*.py' --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=__pycache__ . 2>/dev/null | head -50 + # Express/Next.js routes + grep -rn 'app\.\(get\|post\|put\|delete\|patch\)\|router\.\(get\|post\|put\|delete\|patch\)\|export.*\(GET\|POST\|PUT\|DELETE\)' \ + --include='*.ts' --include='*.js' --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist . 2>/dev/null | head -50 + + echo "" + echo "=== AUTHENTICATION / AUTH ===" + grep -rn 'auth\|token\|session\|cookie\|jwt\|oauth\|password\|credential\|api.key\|secret' \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' --include='*.jsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ \ + -il . 2>/dev/null | head -30 + + echo "" + echo "=== DATABASE QUERIES ===" + grep -rn 'execute\|raw_sql\|text(\|\.query\|sql\.\|SELECT\|INSERT\|UPDATE\|DELETE.*FROM' \ + --include='*.py' --include='*.ts' --include='*.js' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -30 + + echo "" + echo "=== USER INPUT HANDLING ===" + grep -rn 'request\.\(form\|args\|json\|body\|query\|params\|files\)\|req\.body\|req\.query\|req\.params\|FormData\|useFormData\|innerHTML\|dangerouslySetInnerHTML' \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' --include='*.jsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -30 + + echo "" + echo "=== FILE OPERATIONS ===" + grep -rn 'open(\|write\|upload\|multer\|FormData\|multipart\|file_path\|os\.path\|shutil' \ + --include='*.py' --include='*.ts' --include='*.js' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -20 + + echo "" + echo "=== ENV / SECRETS ===" + grep -rn 'process\.env\|os\.environ\|os\.getenv\|dotenv\|\.env\|SECRET\|API_KEY\|PASSWORD\|TOKEN' \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -30 + echo "" + echo "=== HARDCODED SECRETS CHECK ===" + grep -rn "sk-\|ghp_\|gho_\|glpat-\|xoxb-\|xoxp-\|AKIA\|password.*=.*['\"]" \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' --include='*.jsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -20 + + echo "" + echo "=== DEPENDENCIES ===" + cat requirements.txt 2>/dev/null || cat backend/requirements.txt 2>/dev/null || true + cat package.json 2>/dev/null | grep -A 999 '"dependencies"' | head -40 || true + + echo "" + echo "=== CORS / HEADERS ===" + grep -rn 'cors\|CORS\|Access-Control\|X-Frame\|Content-Security-Policy\|helmet\|CSP' \ + --include='*.py' --include='*.ts' --include='*.js' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=__pycache__ . 2>/dev/null | head -20 + + echo "" + echo "=== DOCKER / DEPLOY CONFIG ===" + cat Dockerfile 2>/dev/null | head -30 || true + cat docker-compose.yml 2>/dev/null | head -30 || true + timeout: 60000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 2: PARALLEL SECURITY ANALYSIS (5 specialized agents) + # ═══════════════════════════════════════════════════════════════ + + - id: injection-agent + prompt: | + You are a security specialist focused on **injection vulnerabilities**. + + ## Attack Surface + $scan-surface.output + + ## Scope + Audit the ENTIRE codebase for injection attacks. Read every file that handles user input, database queries, or shell commands. + + ## Checklist + - **SQL injection**: raw queries, string interpolation in SQL, ORM misuse, unparameterized queries + - **Command injection**: subprocess calls with user input, os.system, exec, eval + - **Template injection**: user input in templates, SSTI + - **Path traversal**: user-controlled file paths, directory escapes, file uploads without path sanitization + - **LDAP/NoSQL injection**: if applicable + - **XSS**: reflected, stored, DOM-based — innerHTML, dangerouslySetInnerHTML, unescaped output + - **Header injection**: CRLF in headers, host header attacks + + ## Output + Write findings to $ARTIFACTS_DIR/injection-findings.md with: + - Finding ID (INJ-001, etc.) + - Severity: CRITICAL / HIGH / MEDIUM / LOW + - File and line number + - Description of the vulnerability + - Proof: show the vulnerable code + - Fix: specific code change needed + depends_on: [scan-surface] + context: fresh + denied_tools: [Write, Edit, Bash] + + - id: auth-agent + prompt: | + You are a security specialist focused on **authentication and authorization**. + + ## Attack Surface + $scan-surface.output + + ## Scope + Audit the ENTIRE codebase for auth vulnerabilities. Read every file related to authentication, sessions, tokens, and access control. + + ## Checklist + - **Broken authentication**: weak password policies, no rate limiting on login, credential stuffing + - **Session management**: session fixation, insecure cookies (missing Secure/HttpOnly/SameSite), session timeout + - **JWT issues**: algorithm confusion, missing expiration, weak signing keys, token in URL + - **OAuth issues**: state parameter missing, redirect URI validation, token storage + - **Broken access control**: IDOR, missing authorization checks on endpoints, privilege escalation + - **CSRF**: missing CSRF tokens on state-changing operations, SameSite cookie config + - **API authentication**: missing auth on endpoints, API key exposure, bearer token handling + + ## Output + Write findings to $ARTIFACTS_DIR/auth-findings.md — same format as injection agent (AUTH-001, etc.) + depends_on: [scan-surface] + context: fresh + denied_tools: [Write, Edit, Bash] + + - id: data-privacy-agent + prompt: | + You are a security specialist focused on **data privacy and exposure**. + + ## Attack Surface + $scan-surface.output + + ## Scope + Audit the ENTIRE codebase for data privacy issues. Read every file that handles user data, logging, error responses, or external API calls. + + ## Checklist + - **Sensitive data exposure**: PII in logs, verbose error messages leaking internals, stack traces in production + - **Data at rest**: unencrypted sensitive fields in DB, plaintext passwords, API keys in code + - **Data in transit**: HTTP instead of HTTPS, missing TLS, insecure websocket + - **Secrets management**: hardcoded secrets, secrets in git history, .env in Docker image, exposed in client bundle + - **Third-party data leakage**: sending user data to analytics/CDN without consent, excessive data in API responses + - **Logging**: logging passwords, tokens, PII, credit cards + - **Error handling**: different error messages revealing valid/invalid usernames, timing attacks + - **GDPR/privacy**: data retention, deletion capability, consent mechanisms, right to export + + ## Output + Write findings to $ARTIFACTS_DIR/privacy-findings.md — same format (PRIV-001, etc.) + depends_on: [scan-surface] + context: fresh + denied_tools: [Write, Edit, Bash] + + - id: config-deps-agent + prompt: | + You are a security specialist focused on **configuration and dependency security**. + + ## Attack Surface + $scan-surface.output + + ## Scope + Audit the ENTIRE codebase for configuration and dependency issues. Read all config files, Dockerfiles, CI configs, and dependency manifests. + + ## Checklist + - **Security headers**: missing HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy + - **CORS misconfiguration**: wildcard origins, credentials with wildcard, overly permissive + - **Docker security**: running as root, unnecessary capabilities, secrets in build layers, outdated base images + - **Dependency vulnerabilities**: check versions against known CVEs, outdated packages with known issues + - **CI/CD security**: secrets in logs, permissive permissions, unsigned artifacts + - **Debug/dev in production**: debug mode enabled, development credentials, verbose logging + - **TLS/SSL**: weak cipher suites, self-signed certs, missing certificate validation + - **Rate limiting**: missing rate limits on auth endpoints, API abuse protection + + ## Output + Write findings to $ARTIFACTS_DIR/config-findings.md — same format (CFG-001, etc.) + depends_on: [scan-surface] + context: fresh + denied_tools: [Write, Edit, Bash] + + - id: logic-agent + prompt: | + You are a security specialist focused on **business logic and application security**. + + ## Attack Surface + $scan-surface.output + + ## Scope + Audit the ENTIRE codebase for business logic vulnerabilities. Read the core application logic, state machines, payment flows, and user-facing operations. + + ## Checklist + - **Race conditions**: TOCTOU, double-submit, concurrent modification without locks + - **Business logic bypass**: skipping steps in multi-step flows, parameter tampering, mass assignment + - **Insecure direct object references**: predictable IDs, missing ownership checks + - **File upload**: unrestricted file types, missing size limits, path traversal in filenames, executable uploads + - **Denial of service**: regex DoS (ReDoS), unbounded queries, missing pagination, resource exhaustion + - **Integer overflow/underflow**: in financial calculations, ratings, scores + - **Insecure randomness**: using Math.random or random for security-sensitive operations + - **Open redirects**: unvalidated redirect URLs + + ## Output + Write findings to $ARTIFACTS_DIR/logic-findings.md — same format (LOGIC-001, etc.) + depends_on: [scan-surface] + context: fresh + denied_tools: [Write, Edit, Bash] + + # ═══════════════════════════════════════════════════════════════ + # PHASE 3: SYNTHESIZE + # ═══════════════════════════════════════════════════════════════ + + - id: synthesize + prompt: | + You are the lead security auditor. Synthesize all findings from the parallel security agents. + + ## Findings + + ### Injection Analysis + $injection-agent.output + + ### Authentication & Authorization + $auth-agent.output + + ### Data Privacy + $data-privacy-agent.output + + ### Configuration & Dependencies + $config-deps-agent.output + + ### Business Logic + $logic-agent.output + + ## Instructions + + 1. Deduplicate findings across agents (same issue found by multiple agents) + 2. Validate findings — check if the agent's assessment is accurate by reading the actual code + 3. Assign final severity: CRITICAL / HIGH / MEDIUM / LOW / FALSE_POSITIVE + 4. Categorize: FIXABLE_NOW (can be fixed in code) vs NEEDS_DISCUSSION (architectural, needs human input) + 5. Prioritize by: severity × exploitability × data impact + + ## Output + + Write to $ARTIFACTS_DIR/security-report.md: + + ### Executive Summary + - Total findings by severity + - Overall security posture assessment + - Top 3 most urgent issues + + ### CRITICAL & HIGH Findings (to fix now) + For each: ID, title, severity, file:line, description, fix + + ### MEDIUM & LOW Findings (to track as issues) + For each: ID, title, severity, file:line, description + + ### False Positives (dismissed) + For each: ID, why it was dismissed + + Also write $ARTIFACTS_DIR/fix-plan.md with specific code changes for all CRITICAL and HIGH findings, ordered by dependency. + depends_on: [injection-agent, auth-agent, data-privacy-agent, config-deps-agent, logic-agent] + trigger_rule: one_success + context: fresh + + # ═══════════════════════════════════════════════════════════════ + # PHASE 4: FIX CRITICAL & HIGH + # ═══════════════════════════════════════════════════════════════ + + - id: fix-security + prompt: | + You are implementing security fixes for CRITICAL and HIGH severity findings. + + ## Fix Plan + $synthesize.output + + Read $ARTIFACTS_DIR/fix-plan.md for the ordered list of fixes. + + ## Rules + - Fix ONLY CRITICAL and HIGH severity issues + - Each fix must be minimal — do not refactor surrounding code + - After each fix, verify the vulnerability is actually resolved + - If a fix would break functionality, skip it and note why + - Commit each logical fix separately with a descriptive message + + ## Instructions + 1. Read the fix plan + 2. For each fix: read the vulnerable code, apply the fix, verify it works + 3. Run tests after all fixes to ensure nothing is broken + 4. Do a final `git diff --stat` to verify scope + depends_on: [synthesize] + context: fresh + model: sonnet + hooks: + PostToolUse: + - matcher: "Write|Edit" + response: + systemMessage: > + You just applied a security fix. Verify NOW: + 1. Is the vulnerability actually fixed? Re-read the code. + 2. Did you introduce any new issues? + 3. Run relevant tests to confirm nothing broke. + + # ═══════════════════════════════════════════════════════════════ + # PHASE 5: VALIDATE + # ═══════════════════════════════════════════════════════════════ + + - id: validate + bash: | + echo "=== RUNNING PROJECT TESTS ===" + # Try common test runners + if [ -f "pyproject.toml" ] || [ -f "backend/requirements.txt" ]; then + echo "--- Python tests ---" + python -m pytest 2>&1 || pytest 2>&1 || echo "No pytest found" + fi + if [ -f "package.json" ]; then + echo "--- JS/TS tests ---" + npm test 2>&1 || bun run test 2>&1 || echo "No JS tests found" + fi + if [ -f "frontend/package.json" ]; then + echo "--- Frontend tests ---" + cd frontend && npm test 2>&1 || bun run test 2>&1; cd .. + fi + echo "VALIDATION_COMPLETE" + depends_on: [fix-security] + timeout: 300000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 6: CREATE PR + ISSUES + # ═══════════════════════════════════════════════════════════════ + + - id: create-pr + prompt: | + Create a PR for the security fixes and GitHub issues for remaining findings. + + ## Context + - Security report: $synthesize.output + - Validation: $validate.output + + ## Instructions + + ### Step 1: Create PR for fixes (if any changes were made) + 1. Check `git diff --stat` — if no changes, skip PR creation + 2. Stage and commit all changes if uncommitted + 3. Push: `git push -u origin HEAD` + 4. Create PR: + - Title: "security: fix [N] critical/high vulnerabilities" + - Body: list each fix with the finding ID, what was vulnerable, what was changed + - Include the executive summary from the security report + 5. Save PR number: `echo "$(gh pr view --json number -q '.number')" > $ARTIFACTS_DIR/.pr-number` + 6. Mark ready and enable auto-merge: + ```bash + PR_NUM=$(cat $ARTIFACTS_DIR/.pr-number) + gh pr ready "$PR_NUM" + gh pr merge "$PR_NUM" --squash --auto --delete-branch + ``` + + ### Step 2: Create issues for MEDIUM/LOW findings + Read $ARTIFACTS_DIR/security-report.md. For each MEDIUM or LOW finding that was NOT fixed: + - Create a GitHub issue with label "security" + - Title: "[Security] FINDING_ID: brief description" + - Body: severity, file:line, description, suggested fix + + Do NOT create issues for findings marked as FALSE_POSITIVE. + depends_on: [validate] + context: fresh + model: haiku + hooks: + PreToolUse: + - matcher: "Write|Edit" + response: + hookSpecificOutput: + hookEventName: PreToolUse + permissionDecision: deny + permissionDecisionReason: "PR creation node — do not modify source files." diff --git a/.archon/workflows/defaults/archon-test-audit.yaml b/.archon/workflows/defaults/archon-test-audit.yaml new file mode 100644 index 0000000000..36e25dc44c --- /dev/null +++ b/.archon/workflows/defaults/archon-test-audit.yaml @@ -0,0 +1,357 @@ +name: archon-test-audit +description: | + Use when: User wants to improve test coverage, fix flaky tests, or audit test quality. + Triggers: "test audit", "improve coverage", "fix flaky tests", "test coverage", + "add tests", "test quality", "stability audit", "robustness". + Does: Measures current coverage -> identifies critical gaps -> fixes flaky tests -> + writes new tests for uncovered code -> validates -> creates PR. + NOT for: Writing tests for a specific feature (use archon-fix-github-issue), + PR-scoped test review (that's the test-coverage-agent in review workflows). + +provider: claude + +nodes: + # ═══════════════════════════════════════════════════════════════ + # PHASE 1: MEASURE CURRENT STATE + # ═══════════════════════════════════════════════════════════════ + + - id: measure-coverage + bash: | + echo "=== PROJECT DETECTION ===" + # Detect project type + HAS_PYTHON=false + HAS_NODE=false + [ -f "requirements.txt" ] || [ -f "backend/requirements.txt" ] || [ -f "pyproject.toml" ] && HAS_PYTHON=true + [ -f "package.json" ] || [ -f "frontend/package.json" ] && HAS_NODE=true + echo "Python: $HAS_PYTHON" + echo "Node: $HAS_NODE" + + echo "" + echo "=== PYTHON COVERAGE ===" + if [ "$HAS_PYTHON" = "true" ]; then + pip install pytest-cov 2>/dev/null || true + # Try common pytest locations + if [ -f "backend/requirements.txt" ]; then + python -m pytest backend/tests/ --cov=backend --cov-report=term-missing --tb=no -q 2>&1 || echo "pytest failed" + elif [ -f "pyproject.toml" ]; then + python -m pytest --cov=. --cov-report=term-missing --tb=no -q 2>&1 || echo "pytest failed" + else + python -m pytest --cov=. --cov-report=term-missing --tb=no -q 2>&1 || echo "pytest failed" + fi + else + echo "No Python project detected" + fi + + echo "" + echo "=== NODE COVERAGE ===" + if [ "$HAS_NODE" = "true" ]; then + # Try running coverage from root or frontend + if [ -f "package.json" ]; then + npx vitest run --coverage --reporter=verbose 2>&1 | tail -60 || \ + npm test -- --coverage 2>&1 | tail -60 || \ + echo "Node tests failed at root" + fi + if [ -f "frontend/package.json" ]; then + echo "--- Frontend coverage ---" + cd frontend + npx vitest run --coverage --reporter=verbose 2>&1 | tail -60 || \ + npm test -- --coverage 2>&1 | tail -60 || \ + echo "Frontend tests failed" + cd .. + fi + else + echo "No Node project detected" + fi + + echo "" + echo "=== TEST FILE INVENTORY ===" + echo "Python test files:" + find . -name 'test_*.py' -o -name '*_test.py' | grep -v node_modules | grep -v .git | sort + echo "" + echo "JS/TS test files:" + find . -name '*.test.*' -o -name '*.spec.*' | grep -v node_modules | grep -v .git | sort + + echo "" + echo "=== SOURCE FILES WITHOUT TESTS ===" + echo "Python source files without corresponding tests:" + for f in $(find . -name '*.py' -not -name 'test_*' -not -name '*_test.py' -not -name '__init__.py' -not -path '*/tests/*' -not -path '*/migrations/*' -not -path '*/.git/*' -not -path '*/node_modules/*' | sort); do + base=$(basename "$f" .py) + if ! find . -name "test_${base}.py" -o -name "${base}_test.py" 2>/dev/null | grep -q .; then + echo " UNTESTED: $f" + fi + done + + echo "" + echo "JS/TS source files without corresponding tests:" + for f in $(find . -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' | grep -v node_modules | grep -v .git | grep -v dist | grep -v '.test.' | grep -v '.spec.' | grep -v '__tests__' | sort); do + base=$(basename "$f" | sed 's/\.\(ts\|tsx\|js\|jsx\)$//') + if ! find . -name "${base}.test.*" -o -name "${base}.spec.*" 2>/dev/null | grep -v node_modules | grep -q .; then + echo " UNTESTED: $f" + fi + done + + echo "" + echo "=== FLAKY TEST INDICATORS ===" + echo "Skipped tests:" + grep -rn '@pytest.mark.skip\|pytest.skip\|@skip\|xit(\|xdescribe(\|test.todo\|test.skip\|\.skip(' \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' --include='*.jsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist . 2>/dev/null | head -20 + + echo "" + echo "Retry/flaky patterns:" + grep -rn 'flaky\|retry\|eventually\|wait_for\|sleep.*assert\|xfail' \ + --include='*.py' --include='*.ts' --include='*.js' --include='*.tsx' --include='*.jsx' \ + --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist . 2>/dev/null | head -20 + timeout: 300000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 2: ANALYZE GAPS (parallel agents) + # ═══════════════════════════════════════════════════════════════ + + - id: coverage-gap-agent + prompt: | + You are a test coverage specialist. Analyze the coverage report and identify the most critical gaps. + + ## Coverage Data + $measure-coverage.output + + ## Focus: $ARGUMENTS + + ## Instructions + + 1. Read the coverage report — identify files/functions with lowest coverage + 2. For each untested source file, read it and assess criticality: + - Business logic (payment, auth, core algorithms) = CRITICAL + - API endpoints handling user input = HIGH + - Data transformations, validators = MEDIUM + - Utilities, formatters, config = LOW + 3. For files with partial coverage, read them and identify which branches/paths are untested + 4. Prioritize: what would catch the most bugs if tested? + + ## Output + + Write to $ARTIFACTS_DIR/coverage-gaps.md: + - Total coverage percentage (if available) + - Top 10 most critical untested code paths, ordered by risk + - For each: file, function/method, why it's critical, what test to write + - Estimated number of tests needed to reach next coverage milestone + depends_on: [measure-coverage] + context: fresh + model: sonnet + denied_tools: [Write, Edit, Bash] + + - id: flaky-test-agent + prompt: | + You are a test stability specialist. Find and diagnose flaky, slow, or unreliable tests. + + ## Test Data + $measure-coverage.output + + ## Instructions + + 1. Read every skipped/xfail test and determine: is it actually broken, or just neglected? + 2. Look for common flaky patterns: + - Time-dependent tests (sleep, wall clock, timeouts) + - Order-dependent tests (shared state, missing teardown) + - Network-dependent tests (external APIs without mocking) + - Race conditions (async without proper awaiting) + - Hardcoded paths or ports + 3. Read the test files and identify tests that are: + - Slow (>5s) — check for unnecessary sleeps, full DB rebuilds + - Brittle — testing implementation details instead of behavior + - Missing assertions — tests that always pass + + ## Output + + Write to $ARTIFACTS_DIR/flaky-tests.md: + - List of skipped tests with recommendation: fix, delete, or keep skipped + - List of flaky patterns found with specific file:line references + - List of slow tests with optimization suggestions + - List of tests with weak/missing assertions + depends_on: [measure-coverage] + context: fresh + model: sonnet + denied_tools: [Write, Edit, Bash] + + # ═══════════════════════════════════════════════════════════════ + # PHASE 3: PLAN + # ═══════════════════════════════════════════════════════════════ + + - id: plan + prompt: | + You are planning test improvements. Synthesize the coverage analysis and flaky test findings. + + ## Coverage Gaps + $coverage-gap-agent.output + + ## Flaky Tests + $flaky-test-agent.output + + ## Principles + + - Fix flaky tests FIRST — unreliable tests are worse than no tests + - Delete tests that can never pass or test nothing + - Write tests for critical business logic before edge cases + - Each new test must test BEHAVIOR, not implementation + - Don't mock what you don't own — use fakes/fixtures for external dependencies + - Keep tests fast — mock/stub slow dependencies (network, disk) + - Max scope: 15 test changes per PR to keep reviews manageable + + ## Instructions + + 1. From both reports, select up to 15 highest-impact improvements: + - Fix/delete flaky tests (up to 5) + - Write new tests for critical gaps (up to 10) + 2. For each: specify file, what to test, expected assertions, any fixtures needed + 3. Order by: flaky fixes first, then critical coverage gaps, then medium gaps + + ## Output + + Numbered plan. Be specific — the implement node will follow this literally. + depends_on: [coverage-gap-agent, flaky-test-agent] + context: fresh + allowed_tools: [Read] + + # ═══════════════════════════════════════════════════════════════ + # PHASE 4: IMPLEMENT + # ═══════════════════════════════════════════════════════════════ + + - id: implement + prompt: | + You are implementing test improvements. + + ## Plan + $plan.output + + ## Rules + + - Follow the plan exactly — do not add extra tests beyond the plan + - Each test must have clear arrange/act/assert structure + - Use the project's existing test patterns and fixtures — read existing tests first + - Every test must have a descriptive name that explains what behavior it verifies + - After writing each test, run it individually to verify it passes + - If a test requires new fixtures or mocks, create them in the appropriate conftest/setup file + - Commit each logical group of tests separately + + ## Instructions + + 1. Read existing test files to understand patterns, fixtures, and conventions + 2. Work through the plan items in order + 3. For flaky test fixes: read the test, diagnose, fix, verify it's stable + 4. For new tests: write, run, verify passing + 5. After all changes, run the full test suite to ensure nothing regressed + depends_on: [plan] + context: fresh + hooks: + PostToolUse: + - matcher: "Write|Edit" + response: + systemMessage: > + You just modified a test file. Run the specific test NOW to verify it passes. + Do not batch — verify each test immediately after writing it. + + # ═══════════════════════════════════════════════════════════════ + # PHASE 5: VALIDATE + # ═══════════════════════════════════════════════════════════════ + + - id: validate + bash: | + echo "=== RUNNING FULL TEST SUITE ===" + PASS=true + + if [ -f "backend/requirements.txt" ] || [ -f "pyproject.toml" ]; then + echo "--- Python tests ---" + if ! python -m pytest -v 2>&1; then + PASS=false + fi + fi + + if [ -f "package.json" ]; then + echo "--- Root JS/TS tests ---" + if ! npx vitest run 2>&1 && ! npm test 2>&1; then + PASS=false + fi + fi + + if [ -f "frontend/package.json" ]; then + echo "--- Frontend tests ---" + cd frontend + if ! npx vitest run 2>&1 && ! npm test 2>&1; then + PASS=false + fi + cd .. + fi + + if [ "$PASS" = "true" ]; then + echo "VALIDATION_STATUS: PASS" + else + echo "VALIDATION_STATUS: FAIL" + fi + depends_on: [implement] + timeout: 300000 + + # ═══════════════════════════════════════════════════════════════ + # PHASE 6: FIX FAILURES + # ═══════════════════════════════════════════════════════════════ + + - id: fix-failures + prompt: | + Review the validation output below. + + ## Validation Output + $validate.output + + ## Instructions + + If "VALIDATION_STATUS: PASS", respond with "All tests pass" and stop. + + If there are failures: + 1. Read the failing test output carefully + 2. Fix ONLY the failing tests — do not modify passing tests + 3. If a new test is fundamentally wrong (bad assumption), delete it rather than forcing it to pass + 4. Run the failing test after each fix to verify + 5. After all fixes, run the full test suite + depends_on: [validate] + context: fresh + model: haiku + + # ═══════════════════════════════════════════════════════════════ + # PHASE 7: CREATE PR + # ═══════════════════════════════════════════════════════════════ + + - id: create-pr + prompt: | + Create a PR for the test improvements. + + ## Context + - Coverage gaps: $coverage-gap-agent.output + - Flaky tests: $flaky-test-agent.output + - Plan: $plan.output + - Validation: $validate.output + + ## Instructions + + 1. Check `git diff --stat` — if no changes, skip PR creation + 2. Stage and commit all changes if uncommitted + 3. Push: `git push -u origin HEAD` + 4. Create PR: + - Title: "test: improve coverage and fix flaky tests" + - Body: summary of what was added/fixed, coverage before/after if available + 5. Save PR number: `echo "$(gh pr view --json number -q '.number')" > $ARTIFACTS_DIR/.pr-number` + 6. Mark ready and auto-merge: + ```bash + PR_NUM=$(cat $ARTIFACTS_DIR/.pr-number) + gh pr ready "$PR_NUM" + gh pr merge "$PR_NUM" --squash --auto --delete-branch + ``` + depends_on: [fix-failures] + context: fresh + model: haiku + hooks: + PreToolUse: + - matcher: "Write|Edit" + response: + hookSpecificOutput: + hookEventName: PreToolUse + permissionDecision: deny + permissionDecisionReason: "PR creation node — do not modify source files." diff --git a/.archon/workflows/defaults/archon-test-loop-dag.yaml b/.archon/workflows/defaults/archon-test-loop-dag.yaml index 99f6ee896f..d554653142 100644 --- a/.archon/workflows/defaults/archon-test-loop-dag.yaml +++ b/.archon/workflows/defaults/archon-test-loop-dag.yaml @@ -12,6 +12,7 @@ nodes: echo "Counter initialized to 0" - id: loop-counter + model: haiku depends_on: [setup] loop: prompt: | @@ -42,6 +43,7 @@ nodes: fresh_context: false - id: report + model: haiku depends_on: [loop-counter] prompt: | The loop counter test has completed. The loop node output was: diff --git a/.archon/workflows/defaults/archon-workflow-builder.yaml b/.archon/workflows/defaults/archon-workflow-builder.yaml index a311b8d970..877c7a9f43 100644 --- a/.archon/workflows/defaults/archon-workflow-builder.yaml +++ b/.archon/workflows/defaults/archon-workflow-builder.yaml @@ -202,6 +202,7 @@ nodes: depends_on: [generate-yaml] - id: save-or-report + model: haiku prompt: | You are a workflow installer. Save the generated workflow and report to the user. diff --git a/.gitignore b/.gitignore index a2f33c5d5c..1ab645070c 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ packages/server/.env skills-lock.json test-results/ .archon/ralph/ + +# Keystores — must never be committed +*.jks diff --git a/bun.lock b/bun.lock index 04517f4fbf..1d1233ce8d 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "name": "archon", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.74", + "@openai/codex-sdk": "^0.121.0", }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -23,7 +24,7 @@ }, "packages/adapters": { "name": "@archon/adapters", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@archon/core": "workspace:*", "@archon/git": "workspace:*", @@ -41,7 +42,7 @@ }, "packages/cli": { "name": "@archon/cli", - "version": "0.3.5", + "version": "0.3.6", "bin": { "archon": "./src/cli.ts", }, @@ -62,7 +63,7 @@ }, "packages/core": { "name": "@archon/core", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.89", "@archon/git": "workspace:*", @@ -83,7 +84,7 @@ }, "packages/docs-web": { "name": "@archon/docs-web", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@astrojs/starlight": "^0.38.0", "astro": "^6.1.0", @@ -92,7 +93,7 @@ }, "packages/git": { "name": "@archon/git", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@archon/paths": "workspace:*", }, @@ -102,7 +103,7 @@ }, "packages/isolation": { "name": "@archon/isolation", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@archon/git": "workspace:*", "@archon/paths": "workspace:*", @@ -113,7 +114,7 @@ }, "packages/paths": { "name": "@archon/paths", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "dotenv": "^17", "pino": "^9", @@ -125,7 +126,7 @@ }, "packages/server": { "name": "@archon/server", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@archon/adapters": "workspace:*", "@archon/core": "workspace:*", @@ -143,7 +144,7 @@ }, "packages/web": { "name": "@archon/web", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@dagrejs/dagre": "^2.0.4", "@radix-ui/react-alert-dialog": "^1.1.15", @@ -195,7 +196,7 @@ }, "packages/workflows": { "name": "@archon/workflows", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "@archon/git": "workspace:*", "@archon/paths": "workspace:*", @@ -565,21 +566,21 @@ "@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], - "@openai/codex": ["@openai/codex@0.116.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.116.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.116.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.116.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.116.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.116.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.116.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-K6q9P2ZmpnzGmpS6Ybjvsdtvu8AbJx3f/Z4KmjH1u85StSS9TWMSQB8z0PPObKMejbtiIkHwhGyEIHi4iBYjig=="], + "@openai/codex": ["@openai/codex@0.121.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.121.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.121.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.121.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.121.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.121.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.121.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-kCJ2NeATd4QBQRmqV04ymdN1ZU3MSwnJQDm/KzjpuzGvCuUVEn7no/T2mRyxQ2x77AACqriNOyPPoM/yufyvNg=="], - "@openai/codex-darwin-arm64": ["@openai/codex@0.116.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WkdL083p8uMeASpg8bwV0DPGgzkm48LjN3MyU2m/YukujbiLnknAmG29O2q2rFCLm0oLSDIGUK8EnXA4ZcAF9Q=="], + "@openai/codex-darwin-arm64": ["@openai/codex@0.121.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZyBqIB6Fb4I0hGb/h65Vu7ePYjHSmGiqqfm+/1djEuxDPkqjfi4wkxYxNYNY+6najyNGN4UijOSTTf19eDCrqw=="], - "@openai/codex-darwin-x64": ["@openai/codex@0.116.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ax8uTwYSNIwGrzcNRcn0jJQhZzNcKGDbbn00Emde7gGOemjSLhRALjUaKjckAaW5xWnNqHTGdtzzPB4phNlDYg=="], + "@openai/codex-darwin-x64": ["@openai/codex@0.121.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-1/OAtdkAZ5yPI3xqaEFlHuPziS1yCqL2gOZdswE7HTmmwpIxi6Z3FCo60JWDPluIp89z4tftdjq73/OCN0YVcw=="], - "@openai/codex-linux-arm64": ["@openai/codex@0.116.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-X7cL8rBSGDB+RSZc2FoKiqcMVeLPMmo06bkss/en4lLQsV1XG2DZI56WuXg92IOX3SjYl6Av/eOWgsb1t3UeLQ=="], + "@openai/codex-linux-arm64": ["@openai/codex@0.121.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-2UgMmdo237o7SCMsfb529cOSEM2HFUgN6OBkv5SBLwfNY1NO2Ex6JnUjlppEXlX6/4cXfZ5qjDghVz5j/+B9zw=="], - "@openai/codex-linux-x64": ["@openai/codex@0.116.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-S9InOgJT3tj6uQp55NqrCA1k5tklwFaH00JdC2ElbRmxchm7ard4WxHSJZX9TiY8enj4cQoLIC04NFTUCO+/PQ=="], + "@openai/codex-linux-x64": ["@openai/codex@0.121.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-vlpNJXIqss800J+32Vy7TUZzv31n61b45OLxmsVQGFkTNLJcjFrj9jDUC7I62eC4F16gLioilefNfv4CdJQOEw=="], - "@openai/codex-sdk": ["@openai/codex-sdk@0.116.0", "", { "dependencies": { "@openai/codex": "0.116.0" } }, "sha512-qrn1Pu5G1GJ9w4m/Lk3L3466ulMGG9SfyR0LPAaXdisuQI1rqgoUOuoZ4byX7cCzn0x1g2+WPc0apZgjMEK04Q=="], + "@openai/codex-sdk": ["@openai/codex-sdk@0.121.0", "", { "dependencies": { "@openai/codex": "0.121.0" } }, "sha512-LfDbIBIrRYya6Y+zR8i5ci+wGx8zyTpzTy9iSeRTzZnb4N8Cn30cW+un1Vd2JfjoWhxGXcOTpXy8sSjlSeyvKA=="], - "@openai/codex-win32-arm64": ["@openai/codex@0.116.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-kX2oAUzkgZX9OsYpd4omv9IGf+9VWj4Vy3UtIAnQKBu1DTSzmTJmXDuDn87mkyUciSZadm2QbeqQQzm2NC0NYw=="], + "@openai/codex-win32-arm64": ["@openai/codex@0.121.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-m88q4f3XI5npn1t6OG0nWGHWWAjO5FgjRwxh4hdujbLO6t9CiCNfhfPZIOSsoATbrCNwLC+6S77m3cjbNToPNg=="], - "@openai/codex-win32-x64": ["@openai/codex@0.116.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-6sBIMOoA9FNuxQvCCnK0P548Wqrlk3I9SMdtOCUg2zYzYU7jOF2mWS1VpRQ6R+Jvo2x50dxeJZ+W37dBmXfprw=="], + "@openai/codex-win32-x64": ["@openai/codex@0.121.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-Fp0ecVOyM+VcBi/y4HVvRzhifO9YqRiHzhV3rhtAppC7flh22WPguLC4kmvXYAR0p3RPzbo35M2CedWnkOT+cw=="], "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], @@ -2439,6 +2440,8 @@ "@archon/core/@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.89", "", { "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@modelcontextprotocol/sdk": "^1.27.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-/9W0lyBGuGHw1uu7pQafsp6BLpxfqCv1QYE0Z/eZTX6lGHht4j4Q+O3UImzjsiyEE9cGkOAwZBGAEHDEqt+QUA=="], + "@archon/core/@openai/codex-sdk": ["@openai/codex-sdk@0.116.0", "", { "dependencies": { "@openai/codex": "0.116.0" } }, "sha512-qrn1Pu5G1GJ9w4m/Lk3L3466ulMGG9SfyR0LPAaXdisuQI1rqgoUOuoZ4byX7cCzn0x1g2+WPc0apZgjMEK04Q=="], + "@astrojs/markdown-remark/remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], "@astrojs/markdown-remark/unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], @@ -2737,6 +2740,8 @@ "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "@archon/core/@openai/codex-sdk/@openai/codex": ["@openai/codex@0.116.0", "", { "optionalDependencies": { "@openai/codex-darwin-arm64": "npm:@openai/codex@0.116.0-darwin-arm64", "@openai/codex-darwin-x64": "npm:@openai/codex@0.116.0-darwin-x64", "@openai/codex-linux-arm64": "npm:@openai/codex@0.116.0-linux-arm64", "@openai/codex-linux-x64": "npm:@openai/codex@0.116.0-linux-x64", "@openai/codex-win32-arm64": "npm:@openai/codex@0.116.0-win32-arm64", "@openai/codex-win32-x64": "npm:@openai/codex@0.116.0-win32-x64" }, "bin": { "codex": "bin/codex.js" } }, "sha512-K6q9P2ZmpnzGmpS6Ybjvsdtvu8AbJx3f/Z4KmjH1u85StSS9TWMSQB8z0PPObKMejbtiIkHwhGyEIHi4iBYjig=="], + "@astrojs/markdown-remark/unified/bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], "@astrojs/markdown-remark/unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], @@ -3065,6 +3070,18 @@ "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-darwin-arm64": ["@openai/codex@0.116.0-darwin-arm64", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WkdL083p8uMeASpg8bwV0DPGgzkm48LjN3MyU2m/YukujbiLnknAmG29O2q2rFCLm0oLSDIGUK8EnXA4ZcAF9Q=="], + + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-darwin-x64": ["@openai/codex@0.116.0-darwin-x64", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ax8uTwYSNIwGrzcNRcn0jJQhZzNcKGDbbn00Emde7gGOemjSLhRALjUaKjckAaW5xWnNqHTGdtzzPB4phNlDYg=="], + + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-linux-arm64": ["@openai/codex@0.116.0-linux-arm64", "", { "os": "linux", "cpu": "arm64" }, "sha512-X7cL8rBSGDB+RSZc2FoKiqcMVeLPMmo06bkss/en4lLQsV1XG2DZI56WuXg92IOX3SjYl6Av/eOWgsb1t3UeLQ=="], + + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-linux-x64": ["@openai/codex@0.116.0-linux-x64", "", { "os": "linux", "cpu": "x64" }, "sha512-S9InOgJT3tj6uQp55NqrCA1k5tklwFaH00JdC2ElbRmxchm7ard4WxHSJZX9TiY8enj4cQoLIC04NFTUCO+/PQ=="], + + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-win32-arm64": ["@openai/codex@0.116.0-win32-arm64", "", { "os": "win32", "cpu": "arm64" }, "sha512-kX2oAUzkgZX9OsYpd4omv9IGf+9VWj4Vy3UtIAnQKBu1DTSzmTJmXDuDn87mkyUciSZadm2QbeqQQzm2NC0NYw=="], + + "@archon/core/@openai/codex-sdk/@openai/codex/@openai/codex-win32-x64": ["@openai/codex@0.116.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-6sBIMOoA9FNuxQvCCnK0P548Wqrlk3I9SMdtOCUg2zYzYU7jOF2mWS1VpRQ6R+Jvo2x50dxeJZ+W37dBmXfprw=="], + "@dotenvx/dotenvx/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "@inquirer/core/wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], diff --git a/homebrew/archon.rb b/homebrew/archon.rb index 59c801c015..0bac58a339 100644 --- a/homebrew/archon.rb +++ b/homebrew/archon.rb @@ -7,28 +7,28 @@ class Archon < Formula desc "Remote agentic coding platform - control AI assistants from anywhere" homepage "https://github.com/coleam00/Archon" - version "0.3.5" + version "0.3.6" license "MIT" on_macos do on_arm do url "https://github.com/coleam00/Archon/releases/download/v#{version}/archon-darwin-arm64" - sha256 "2c2065e580a085baaea02504cb5451be3f68e0d9fdb13a364cd45194d5b22de1" + sha256 "96b6dac50b046eece9eddbb988a0c39b4f9a0e2faac66e49b977ba6360069e86" end on_intel do url "https://github.com/coleam00/Archon/releases/download/v#{version}/archon-darwin-x64" - sha256 "515aca3b2bc30d3b5d4dfb67c04648f70b66e8ed345ea6ab039e76e6578e82fe" + sha256 "09f1dbe12417b4300b7b07b531eb7391a286305f8d4eafc11e7f61f5d26eb8eb" end end on_linux do on_arm do url "https://github.com/coleam00/Archon/releases/download/v#{version}/archon-linux-arm64" - sha256 "96920d98ae0d4dc7ef78e6de4f9018a9ba2031b9c2b010fd5d748d9513c49f60" + sha256 "80b06a6ff699ec57cd4a3e49cfe7b899a3e8212688d70285f5a887bf10086731" end on_intel do url "https://github.com/coleam00/Archon/releases/download/v#{version}/archon-linux-x64" - sha256 "80e7d115da424d5ee47b7db773382c9b8d0db728408f9815c05081872da6b74f" + sha256 "09f5dac6db8037ed6f3e5b7e9c5eb8e37f19822a4ed2bf4cd7e654780f9d00de" end end diff --git a/package.json b/package.json index b296d638ca..3dcb4479a2 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "test-exclude": "^7.0.1" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.2.74" + "@anthropic-ai/claude-agent-sdk": "^0.2.74", + "@openai/codex-sdk": "^0.121.0" } } diff --git a/scripts/pr-maintenance-cron.sh b/scripts/pr-maintenance-cron.sh new file mode 100755 index 0000000000..27f01ec8a2 --- /dev/null +++ b/scripts/pr-maintenance-cron.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# pr-maintenance-cron.sh — Run from cron every 15 minutes. +# Zero AI cost when nothing to do. Processes one PR per project per run. +# +# Usage: +# ./scripts/pr-maintenance-cron.sh # all projects +# ./scripts/pr-maintenance-cron.sh cosmic-match reli # specific projects +# +# Crontab entry: +# */15 * * * * /mnt/ext-fast/archon/scripts/pr-maintenance-cron.sh >> /tmp/pr-maintenance.log 2>&1 + +set -euo pipefail + +# Cron runs with a minimal PATH (/usr/bin:/bin). archon, gh, bun, git +# often live in user-local bins; prepend them so the script works from +# both cron and an interactive shell. +export PATH="$HOME/.bun/bin:$HOME/.local/bin:/usr/local/bin:$PATH" + +# --- Configuration --- +source /home/asiri/gt/mayor/scripts/lib/archon-projects.sh +load_archon_projects DEFAULT_PROJECTS +BASE_DIR="/mnt/ext-fast" +LOG_PREFIX="[pr-maintenance]" + +# Use arguments if provided, otherwise all projects +if [ $# -gt 0 ]; then + PROJECTS=("$@") +else + PROJECTS=("${DEFAULT_PROJECTS[@]}") +fi + +log() { echo "$(date -Is) $LOG_PREFIX $*"; } + +for PROJECT in "${PROJECTS[@]}"; do + REPO_DIR="$BASE_DIR/$PROJECT" + + if [ ! -d "$REPO_DIR/.git" ]; then + log "$PROJECT: not a git repo, skipping" + continue + fi + + cd "$REPO_DIR" + + # --- Phase 0: Promote CLEAN draft PRs to ready-for-review --- + # Archon workflows create PRs as drafts by default. When CI is green the + # draft has nothing left to gate on, but the Phase 1 merge filter skips + # drafts — so left alone a green draft sits forever. Flip it to ready so + # Phase 1 can merge it on this same tick. + GREEN_DRAFTS=$(gh pr list --state open --json number,mergeStateStatus,isDraft \ + --jq '[.[] | select(.isDraft == true and .mergeStateStatus == "CLEAN")] | .[].number' 2>/dev/null || true) + + for PR in $GREEN_DRAFTS; do + log "$PROJECT: promoting draft PR #$PR to ready (CI CLEAN)" + if ! gh pr ready "$PR" 2>>"/tmp/pr-maintenance-errors.log"; then + log "$PROJECT: PR #$PR — could not mark ready (see /tmp/pr-maintenance-errors.log)" + fi + done + + # --- Phase 1: Merge CLEAN PRs directly (bash only, zero AI cost) --- + CLEAN_PRS=$(gh pr list --state open --json number,mergeStateStatus,isDraft \ + --jq '[.[] | select(.isDraft == false and .mergeStateStatus == "CLEAN")] | .[].number' 2>/dev/null || true) + + for PR in $CLEAN_PRS; do + log "$PROJECT: PR #$PR is CLEAN — merging directly" + # Surface stderr to the cron log so actual failures (permissions, branch + # protection, etc.) are diagnosable on the next tick instead of vanishing. + if ! gh pr merge "$PR" --squash --auto --delete-branch 2>&1; then + if ! gh pr merge "$PR" --squash --delete-branch 2>&1; then + log "$PROJECT: PR #$PR — could not merge, skipping" + fi + fi + done + + # --- Phase 2: Check for one PR needing AI attention --- + ACTIONABLE=$(gh pr list --state open --json number,mergeStateStatus,isDraft \ + --jq '[.[] | select(.isDraft == false and (.mergeStateStatus == "BEHIND" or .mergeStateStatus == "DIRTY" or .mergeStateStatus == "UNSTABLE" or .mergeStateStatus == "UNKNOWN"))] | .[0].number // empty' 2>/dev/null || true) + + if [ -z "$ACTIONABLE" ]; then + log "$PROJECT: no PRs need AI maintenance" + continue + fi + + log "$PROJECT: PR #$ACTIONABLE needs maintenance — launching archon" + archon workflow run archon-pr-maintenance --cwd "$REPO_DIR" "PR #$ACTIONABLE" & + +done + +# Wait for any background archon runs to complete +wait +log "Done"