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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,70 @@ jobs:

- name: Secret scan
run: node scripts/secret-scan.mjs

- name: Set advisor mode for pull requests
if: github.event_name == 'pull_request'
id: advisor-mode-pr
run: |
node -e "const fs=require('node:fs'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'enabled=false\nmode=skip\n');"

- name: Detect advisor mode for trusted events
if: github.event_name != 'pull_request'
id: advisor-mode-trusted
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const fs=require('node:fs'); const has=Object.values({ANTHROPIC_AUTH_TOKEN:process.env.ANTHROPIC_AUTH_TOKEN,GEMINI_API_KEY:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:process.env.GOOGLE_API_KEY,OPENAI_API_KEY:process.env.OPENAI_API_KEY}).some((value)=>Boolean((value||'').trim())); fs.appendFileSync(process.env.GITHUB_OUTPUT, `enabled=${has}\nmode=${has?'real':'skip'}\n`);"

- name: Finalize advisor mode
id: advisor-mode
env:
PR_ENABLED: ${{ steps.advisor-mode-pr.outputs.enabled }}
PR_MODE: ${{ steps.advisor-mode-pr.outputs.mode }}
TRUSTED_ENABLED: ${{ steps.advisor-mode-trusted.outputs.enabled }}
TRUSTED_MODE: ${{ steps.advisor-mode-trusted.outputs.mode }}
run: |
node -e "const fs=require('node:fs'); const enabled=(process.env.TRUSTED_ENABLED||process.env.PR_ENABLED||'false').trim()||'false'; const mode=(process.env.TRUSTED_MODE||process.env.PR_MODE||'skip').trim()||'skip'; fs.appendFileSync(process.env.GITHUB_OUTPUT, `enabled=${enabled}\nmode=${mode}\n`);"

- name: Report advisor mode
run: |
echo "Advisor mode: ${{ steps.advisor-mode.outputs.mode }}"

- name: Install real LLM CLIs
if: steps.advisor-mode.outputs.enabled == 'true'
run: npm i -g @anthropic-ai/claude-code @google/gemini-cli @openai/codex

- name: Validate real LLM prerequisites
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const {execSync}=require('node:child_process'); const hasCmd=(cmd)=>{try{execSync(cmd+' --help',{stdio:'pipe'}); return true;}catch{return false;}}; const hasClaude=hasCmd('claude') && Boolean((process.env.ANTHROPIC_AUTH_TOKEN||'').trim()); const hasGemini=hasCmd('gemini') && Boolean((process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY||'').trim()); const hasOpenAI=(hasCmd('chatgpt')||hasCmd('codex')) && Boolean((process.env.OPENAI_API_KEY||'').trim()); if(!(hasClaude||hasGemini||hasOpenAI)){throw new Error('No ready LLM provider. Need at least one: claude+ANTHROPIC_AUTH_TOKEN | gemini+GEMINI_API_KEY | (chatgpt|codex)+OPENAI_API_KEY');}"

- name: E2E fullflow real
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:fullflow:real

- name: E2E failures real
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:failures:real

- name: Skip real LLM E2E
if: steps.advisor-mode.outputs.enabled != 'true'
run: echo "Skipping real LLM E2E because this event has no configured provider secret."
90 changes: 87 additions & 3 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,51 @@ jobs:
- name: Build
run: npm run build

- name: Set advisor mode for pull requests
if: github.event_name == 'pull_request'
id: advisor-mode-pr
run: |
node -e "const fs=require('node:fs'); fs.appendFileSync(process.env.GITHUB_OUTPUT, 'enabled=false\nmode=mock\n');"

- name: Detect advisor mode for trusted events
if: github.event_name != 'pull_request'
id: advisor-mode-trusted
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const fs=require('node:fs'); const has=Object.values({ANTHROPIC_AUTH_TOKEN:process.env.ANTHROPIC_AUTH_TOKEN,GEMINI_API_KEY:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:process.env.GOOGLE_API_KEY,OPENAI_API_KEY:process.env.OPENAI_API_KEY}).some((value)=>Boolean((value||'').trim())); fs.appendFileSync(process.env.GITHUB_OUTPUT, `enabled=${has}\nmode=${has?'real':'mock'}\n`);"

- name: Finalize advisor mode
id: advisor-mode
env:
PR_ENABLED: ${{ steps.advisor-mode-pr.outputs.enabled }}
PR_MODE: ${{ steps.advisor-mode-pr.outputs.mode }}
TRUSTED_ENABLED: ${{ steps.advisor-mode-trusted.outputs.enabled }}
TRUSTED_MODE: ${{ steps.advisor-mode-trusted.outputs.mode }}
run: |
node -e "const fs=require('node:fs'); const enabled=(process.env.TRUSTED_ENABLED||process.env.PR_ENABLED||'false').trim()||'false'; const mode=(process.env.TRUSTED_MODE||process.env.PR_MODE||'mock').trim()||'mock'; fs.appendFileSync(process.env.GITHUB_OUTPUT, `enabled=${enabled}\nmode=${mode}\n`);"

- name: Report advisor mode
run: |
echo "Advisor mode: ${{ steps.advisor-mode.outputs.mode }}"

- name: Install real LLM CLIs
if: steps.advisor-mode.outputs.enabled == 'true'
run: npm i -g @anthropic-ai/claude-code @google/gemini-cli @openai/codex

- name: Validate real LLM prerequisites
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const {execSync}=require('node:child_process'); const hasCmd=(cmd)=>{try{execSync(cmd+' --help',{stdio:'pipe'}); return true;}catch{return false;}}; const hasClaude=hasCmd('claude') && Boolean((process.env.ANTHROPIC_AUTH_TOKEN||'').trim()); const hasGemini=hasCmd('gemini') && Boolean((process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY||'').trim()); const hasOpenAI=(hasCmd('chatgpt')||hasCmd('codex')) && Boolean((process.env.OPENAI_API_KEY||'').trim()); if(!(hasClaude||hasGemini||hasOpenAI)){throw new Error('No ready LLM provider. Need at least one: claude+ANTHROPIC_AUTH_TOKEN | gemini+GEMINI_API_KEY | (chatgpt|codex)+OPENAI_API_KEY');}"

- name: Prepare gate fixtures
shell: bash
run: |
Expand Down Expand Up @@ -53,7 +98,40 @@ jobs:
}
JSON

- name: Prepare advisors (mock-only)
- name: Real E2E fullflow
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:fullflow:real

- name: Real E2E failures
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:failures:real

- name: Run release gate (real advisors)
if: steps.advisor-mode.outputs.enabled == 'true'
shell: bash
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node scripts/release-gate.mjs \
--plan .salacia/plans/release-gate-plan.json \
--exec .salacia/journal/release-gate-exec.json \
--require-convergence

- name: Prepare advisors (mock fallback)
if: steps.advisor-mode.outputs.enabled != 'true'
shell: bash
run: |
printf '%s\n' \
Expand All @@ -66,9 +144,15 @@ jobs:
'console.log(JSON.stringify({ vote: "approve", summary: "mock gemini approve", evidenceRef: "mock-gemini" }));' \
> scripts/validate-gemini.mjs

chmod +x scripts/validate-claude.mjs scripts/validate-gemini.mjs
printf '%s\n' \
'#!/usr/bin/env node' \
'console.log(JSON.stringify({ vote: "approve", summary: "mock chatgpt approve", evidenceRef: "mock-chatgpt" }));' \
> scripts/validate-chatgpt.mjs

chmod +x scripts/validate-claude.mjs scripts/validate-gemini.mjs scripts/validate-chatgpt.mjs

- name: Run release gate (mock advisors, no external API keys)
- name: Run release gate (mock advisors)
if: steps.advisor-mode.outputs.enabled != 'true'
shell: bash
run: |
node scripts/release-gate.mjs \
Expand Down
73 changes: 70 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ jobs:
- name: Build
run: npm run build

- name: Detect advisor mode
id: advisor-mode
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const fs=require('node:fs'); const has=Object.values({ANTHROPIC_AUTH_TOKEN:process.env.ANTHROPIC_AUTH_TOKEN,GEMINI_API_KEY:process.env.GEMINI_API_KEY,GOOGLE_API_KEY:process.env.GOOGLE_API_KEY,OPENAI_API_KEY:process.env.OPENAI_API_KEY}).some((value)=>Boolean((value||'').trim())); fs.appendFileSync(process.env.GITHUB_OUTPUT, `enabled=${has}\nmode=${has?'real':'mock'}\n`);"

- name: Report advisor mode
run: |
echo "Advisor mode: ${{ steps.advisor-mode.outputs.mode }}"

- name: Install real LLM CLIs
if: steps.advisor-mode.outputs.enabled == 'true'
run: npm i -g @anthropic-ai/claude-code @google/gemini-cli @openai/codex

- name: Validate real LLM prerequisites
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node -e "const {execSync}=require('node:child_process'); const hasCmd=(cmd)=>{try{execSync(cmd+' --help',{stdio:'pipe'}); return true;}catch{return false;}}; const hasClaude=hasCmd('claude') && Boolean((process.env.ANTHROPIC_AUTH_TOKEN||'').trim()); const hasGemini=hasCmd('gemini') && Boolean((process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY||'').trim()); const hasOpenAI=(hasCmd('chatgpt')||hasCmd('codex')) && Boolean((process.env.OPENAI_API_KEY||'').trim()); if(!(hasClaude||hasGemini||hasOpenAI)){throw new Error('No ready LLM provider. Need at least one: claude+ANTHROPIC_AUTH_TOKEN | gemini+GEMINI_API_KEY | (chatgpt|codex)+OPENAI_API_KEY');}"

- name: Prepare gate fixtures
shell: bash
run: |
Expand Down Expand Up @@ -57,7 +85,40 @@ jobs:
}
JSON

- name: Prepare advisors (mock-only)
- name: Real E2E fullflow
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:fullflow:real

- name: Real E2E failures
if: steps.advisor-mode.outputs.enabled == 'true'
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npm run test:e2e:failures:real

- name: Run release gate (real advisors)
if: steps.advisor-mode.outputs.enabled == 'true'
shell: bash
env:
ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
node scripts/release-gate.mjs \
--plan .salacia/plans/release-gate-plan.json \
--exec .salacia/journal/release-gate-exec.json \
--require-convergence

- name: Prepare advisors (mock fallback)
if: steps.advisor-mode.outputs.enabled != 'true'
shell: bash
run: |
printf '%s\n' \
Expand All @@ -70,9 +131,15 @@ jobs:
'console.log(JSON.stringify({ vote: "approve", summary: "mock gemini approve", evidenceRef: "mock-gemini" }));' \
> scripts/validate-gemini.mjs

chmod +x scripts/validate-claude.mjs scripts/validate-gemini.mjs
printf '%s\n' \
'#!/usr/bin/env node' \
'console.log(JSON.stringify({ vote: "approve", summary: "mock chatgpt approve", evidenceRef: "mock-chatgpt" }));' \
> scripts/validate-chatgpt.mjs

chmod +x scripts/validate-claude.mjs scripts/validate-gemini.mjs scripts/validate-chatgpt.mjs

- name: Run release gate (mock advisors, no external API keys)
- name: Run release gate (mock advisors)
if: steps.advisor-mode.outputs.enabled != 'true'
shell: bash
run: |
node scripts/release-gate.mjs \
Expand Down
Loading
Loading