Skip to content

feat: implement ci/cd pipeline with docker#258

Closed
codebestia wants to merge 6 commits intoSolFoundry:mainfrom
codebestia:feat/docker-integration
Closed

feat: implement ci/cd pipeline with docker#258
codebestia wants to merge 6 commits intoSolFoundry:mainfrom
codebestia:feat/docker-integration

Conversation

@codebestia
Copy link
Copy Markdown
Contributor

Description

Implemented production-grade Docker containerization and a unified CI/CD pipeline for the SolFoundry monorepo. This ensures environment parity and automates the build-test-deploy lifecycle.

Key Changes:

  • Backend: Multi-stage Dockerfile (Python 3.12-slim) with health checks and optimized layers.
  • Frontend: Multi-stage Dockerfile (Node 20 -> Nginx) with SPA routing and API proxying.
  • Orchestration: docker-compose.yml for local stack (Frontend on 8081, Backend on 8000, Postgres 16, Redis 7).
  • CI/CD: Unified GitHub Actions (ci-cd.ym) covering Linting, Testing (Pytest/Vitest), Docker builds (GHCR), and multi-environment deployment logic.
  • Fixes: Resolved a missing mockAgents.ts module causing frontend build failures.

Closes #184

Solana Wallet for Payout

Wallet: 4QhseKvBuaCQhdkP248iXoUxohPzVC5m8pE9hAv4nMYw

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🏗️ Infrastructure / DevOps update
  • 📝 Documentation update
  • ✅ Test addition/update

Checklist

  • Code is clean and follows the issue spec exactly
  • One PR per bounty
  • Tests included for new functionality (Docker Health Checks)
  • All existing tests pass (Verified via local pytest and vitest)
  • No console.log or debugging code left behind
  • No hardcoded secrets or API keys (Used .env.example)

Testing

  • Manual testing performed: Verified docker compose up starts all 4 services as healthy.
  • Unit tests added/updated: Verified frontend build (npm run build) with path fixes.
  • Integration tests added/updated: Verified Backend/Frontend communication via Nginx proxy on port 8081.

Screenshots (if applicable)

Verification results:

  • Backend Health: http://localhost:8000/health -> {"status":"ok", ...}
  • Frontend Health: http://localhost:8081/health -> ok

Additional Notes

  • Remapped frontend to port 8081 to avoid host conflicts with port 8080 (common for Hasura/GraphQL engines).
  • Backend waits for DB/Redis to be healthy before starting.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds CI/CD and containerization assets: a new GitHub Actions workflow .github/workflows/ci-cd.yml (PR quality gate and manual deploy trigger), updates existing workflows (.github/workflows/ci.yml, .github/workflows/deploy.yml) with compose smoke tests, Postgres-backed integration job, deploy input handling and verification; adds backend/Dockerfile, frontend/Dockerfile, docker-compose.yml, frontend/nginx.conf, frontend/public/health, multiple .env.example files (.env.example, backend/.env.example, frontend/.env.example); removes .env.* from .gitignore; and updates backend/tests/conftest.py to respect TEST_DATABASE_URL.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

approved, paid

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: implement ci/cd pipeline with docker' directly summarizes the main change, which is the addition of Docker containerization and CI/CD pipeline infrastructure.
Description check ✅ Passed The PR description is well-related to the changeset, detailing Docker Dockerfiles, docker-compose configuration, GitHub Actions CI/CD, environment setup, and testing performed.
Linked Issues check ✅ Passed The PR fulfills all major coding requirements from issue #184: multi-stage Dockerfiles for backend/frontend, docker-compose with all services, GitHub Actions CI/CD pipeline, environment variable management via .env.example, health checks integrated into orchestration, and Solana wallet included.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the CI/CD and Docker objectives from issue #184; no extraneous modifications outside the stated scope were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/Dockerfile`:
- Around line 19-53: Create and switch to a non-root runtime user in the
Dockerfile by adding a dedicated user (e.g., appuser) in the runtime stage,
chown'ing application directories and the copied virtualenv (/opt/venv, /app,
/app/logs) to that user, and then setting USER appuser (ensure ENV HOME is set
if needed); keep HEALTHCHECK and CMD as-is but after the USER switch so the
container and health probe run unprivileged. Reference the runtime stage, the
copied /opt/venv, the created logs directory, and the final CMD/HEALTHCHECK when
making these changes.
- Around line 37-38: Replace the overly-permissive chmod 777 on the logs
directory with ownership and tighter permissions: create the logs directory,
chown it to the application user/group that the container runs as (e.g.,
APP_USER or the non-root user defined elsewhere) and set restrictive permissions
such as 750 (or 755 if group read/execute is needed) instead of 777; update the
Dockerfile RUN line that currently uses "mkdir -p logs && chmod 777 logs" to
ensure the logs directory is owned by the app user (using chown) and has
non-world-writable permissions so only the app user (and optionally its group)
can write to logs.

In `@docker-compose.yml`:
- Around line 52-61: Update the frontend service's depends_on to wait for the
backend healthcheck instead of only declaring a dependency: replace the simple
"depends_on: - backend" with a structured depends_on that specifies backend:
condition: service_healthy so the frontend container won't start until the
backend healthcheck reports healthy; modify the frontend service block (service
name "frontend", key "depends_on") to reference the backend service health
condition.
- Around line 12-13: The docker-compose service currently maps the PostgreSQL
port with the ports entry "- \"5432:5432\"" which exposes the DB to the host;
remove or modify that mapping in the docker-compose.yml service definition (the
ports key for the PostgreSQL service) to avoid host exposure—either delete the
ports line so the container-only port remains internal, or replace it with a
conditional/dev-only mapping (e.g., use an environment-specific override or a
compose profile) so production-like runs do not publish 5432 to the host.

In `@frontend/Dockerfile`:
- Around line 14-31: The container runs as root; modify the Dockerfile to
create/use a non-root user and run nginx as that user: add a non-root user
creation step and a USER directive (e.g., create user "nginx" or "appuser")
after copying artifacts, update the exposed port and HEALTHCHECK if you move
nginx to an unprivileged port like 8080, and adjust the nginx startup and
nginx.conf to bind to that port; ensure the Dockerfile's CMD still starts nginx
and that file ownership of /usr/share/nginx/html and any required runtime dirs
is changed to the non-root user so nginx can read/write them.
- Around line 6-8: Replace the non-reproducible dependency installation in the
Dockerfile: change the RUN npm install step that follows COPY package*.json ./
to use npm ci instead (ensure package-lock.json is copied by the existing COPY
package*.json ./ statement), so builds use exact versions from the lockfile and
are faster/more reproducible in CI.

In `@frontend/nginx.conf`:
- Around line 1-40: Add production security headers at the server level (or
inside the location / block) by setting X-Frame-Options, X-Content-Type-Options,
Content-Security-Policy, and Referrer-Policy to secure values; update the
location /health block to include add_header Content-Type text/plain; so health
checks return the correct MIME type; and verify that the error_page target
/50x.html exists under the configured root (/usr/share/nginx/html) or
replace/remove the error_page directive to avoid broken error responses
(references: server block, location /, location /health, error_page, root).
- Around line 14-21: The /api location block (location /api { ... }) lacks proxy
timeout settings; add proxy_connect_timeout 30s, proxy_read_timeout 60s, and
proxy_send_timeout 60s inside the same location block alongside proxy_pass and
proxy_set_header directives to prevent hung connections and improve resilience.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 45222f13-1443-45ac-9229-93b2dff38a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 06994bb and 26287b7.

📒 Files selected for processing (5)
  • .github/workflows/ci-cd.yml
  • backend/Dockerfile
  • docker-compose.yml
  • frontend/Dockerfile
  • frontend/nginx.conf

Comment on lines +12 to +13
ports:
- "5432:5432"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider whether database port exposure to host is necessary.

Exposing PostgreSQL port 5432 to the host network allows direct database access from outside Docker, which may be convenient for development tools but could be a security consideration in some environments.

For local development this is typically acceptable. For production-like testing, consider removing host port mappings for internal services.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 12 - 13, The docker-compose service
currently maps the PostgreSQL port with the ports entry "- \"5432:5432\"" which
exposes the DB to the host; remove or modify that mapping in the
docker-compose.yml service definition (the ports key for the PostgreSQL service)
to avoid host exposure—either delete the ports line so the container-only port
remains internal, or replace it with a conditional/dev-only mapping (e.g., use
an environment-specific override or a compose profile) so production-like runs
do not publish 5432 to the host.

Comment on lines +1 to +40
server {
listen 80;
server_name localhost;

# Root directory for static files
root /usr/share/nginx/html;
index index.html;

# SPA routing - redirect all to index.html
location / {
try_files $uri $uri/ /index.html;
}

# API Proxy
location /api {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# Internal health check for container
location /health {
access_log off;
return 200 'ok';
}

# Cache Control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}

# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding security headers for production deployment.

The nginx configuration is functionally correct for SPA serving and API proxying. However, for production use, consider adding security headers:

  • X-Frame-Options to prevent clickjacking
  • X-Content-Type-Options: nosniff
  • Content-Security-Policy for XSS mitigation
  • Referrer-Policy

These can be added in a location / block or at the server level.

Additionally:

  • Line 26: The /health endpoint should include add_header Content-Type text/plain; for proper response handling.
  • Line 36-38: Ensure 50x.html exists in the frontend build output, or the error page will fail to render.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/nginx.conf` around lines 1 - 40, Add production security headers at
the server level (or inside the location / block) by setting X-Frame-Options,
X-Content-Type-Options, Content-Security-Policy, and Referrer-Policy to secure
values; update the location /health block to include add_header Content-Type
text/plain; so health checks return the correct MIME type; and verify that the
error_page target /50x.html exists under the configured root
(/usr/share/nginx/html) or replace/remove the error_page directive to avoid
broken error responses (references: server block, location /, location /health,
error_page, root).

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
docker-compose.yml (1)

52-61: 🧹 Nitpick | 🔵 Trivial

Frontend service should wait for backend health before starting.

The frontend's depends_on (Line 58-59) uses a simple list format, which only waits for the container to start, not for it to become healthy. Since the backend has a healthcheck defined, the frontend should wait for it:

depends_on:
  backend:
    condition: service_healthy

Without this, the frontend may start while the backend is still initializing, causing /api requests to fail with 502 errors until the backend becomes ready.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 52 - 61, Update the frontend service's
depends_on so it waits for the backend healthcheck instead of just container
start: replace the simple list under the "frontend" service's depends_on with a
mapping that references "backend" and adds "condition: service_healthy" (i.e.,
change the depends_on block for the frontend service to use backend: condition:
service_healthy) so the frontend will start only after the backend is healthy.
frontend/nginx.conf (1)

1-40: 🧹 Nitpick | 🔵 Trivial

Configuration is functional; consider adding security headers for production.

The SPA routing, API proxying, and health check are correctly configured. However, for production deployment, the configuration lacks important security headers that help prevent common web attacks:

  • X-Frame-Options: DENY or SAMEORIGIN — prevents clickjacking
  • X-Content-Type-Options: nosniff — prevents MIME-type sniffing
  • Content-Security-Policy — mitigates XSS attacks

These can be added at the server level:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/nginx.conf` around lines 1 - 40, Add standard security headers at
the server level to harden the nginx config: update the server block (the
top-level server { ... } that defines listen, root, index and locations) to
always send X-Frame-Options (e.g., SAMEORIGIN), X-Content-Type-Options (nosniff)
and a restrictive Content-Security-Policy header; ensure these headers are
applied globally (not only to a single location) and use the nginx "always"
behavior so they are included on successful responses and errors (apply changes
near the existing error_page/50x.html or above the location blocks for SPA and
/api).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker-compose.yml`:
- Around line 43-48: Add a .env.example listing GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET, and JWT_SECRET_KEY (noting JWT_SECRET_KEY is optional
because auth_service.py falls back to secrets.token_urlsafe(32)), and update
README or CONTRIBUTING.md to document these required/optional env vars and how
to obtain/configure them for local development; reference the docker-compose
environment block (DATABASE_URL, REDIS_URL, GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET, JWT_SECRET) and auth_service.py (the GitHubOAuthError
checks and JWT_SECRET_KEY fallback) so the new docs explicitly state which
variables must be set before first auth and which have defaults.

---

Duplicate comments:
In `@docker-compose.yml`:
- Around line 52-61: Update the frontend service's depends_on so it waits for
the backend healthcheck instead of just container start: replace the simple list
under the "frontend" service's depends_on with a mapping that references
"backend" and adds "condition: service_healthy" (i.e., change the depends_on
block for the frontend service to use backend: condition: service_healthy) so
the frontend will start only after the backend is healthy.

In `@frontend/nginx.conf`:
- Around line 1-40: Add standard security headers at the server level to harden
the nginx config: update the server block (the top-level server { ... } that
defines listen, root, index and locations) to always send X-Frame-Options (e.g.,
SAMEORIGIN), X-Content-Type-Options (nosniff) and a restrictive
Content-Security-Policy header; ensure these headers are applied globally (not
only to a single location) and use the nginx "always" behavior so they are
included on successful responses and errors (apply changes near the existing
error_page/50x.html or above the location blocks for SPA and /api).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: edae26e4-db8c-4b5f-9c85-75b6c34ef9a6

📥 Commits

Reviewing files that changed from the base of the PR and between 26287b7 and 893d93a.

📒 Files selected for processing (5)
  • .github/workflows/ci-cd.yml
  • backend/Dockerfile
  • docker-compose.yml
  • frontend/Dockerfile
  • frontend/nginx.conf

Comment on lines +43 to +48
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-solfoundry}
REDIS_URL: redis://redis:6379/0
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET}
JWT_SECRET: ${JWT_SECRET}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if .env.example documents the required secrets
fd -t f ".env.example" | xargs cat 2>/dev/null || echo "No .env.example found"

Repository: SolFoundry/solfoundry

Length of output: 47


🏁 Script executed:

# Look for documentation files (README, .md files) that might document environment variables
fd -t f "README|\.md$" | head -20 | xargs -I {} sh -c 'echo "=== {} ===" && head -50 {}'

Repository: SolFoundry/solfoundry

Length of output: 6375


🏁 Script executed:

# Search for backend code that validates these environment variables
rg "GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|JWT_SECRET" --type py --type js -A 3 -B 1 | head -100

Repository: SolFoundry/solfoundry

Length of output: 5331


🏁 Script executed:

# Look for any environment configuration files (.env.*)
fd -t f "\.env" | xargs ls -la 2>/dev/null || echo "No .env files found"

Repository: SolFoundry/solfoundry

Length of output: 1157


🏁 Script executed:

# Check docker-compose.yml for full context and any validation hints
cat docker-compose.yml | head -80

Repository: SolFoundry/solfoundry

Length of output: 1653


Backend validation partially addresses the concern, but documentation is still missing.

The backend code (backend/app/services/auth_service.py) includes runtime validation that handles two of the three secrets:

  • GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET will raise GitHubOAuthError if not configured when auth is first attempted, so they fail fast rather than silently
  • JWT_SECRET_KEY auto-generates a random secret if not provided (os.getenv("JWT_SECRET_KEY") or secrets.token_urlsafe(32))

However, this validation is reactive (triggered during authentication) rather than proactive, and there is no .env.example file or documentation in README/CONTRIBUTING.md to inform users which environment variables are required for local setup. Adding a .env.example file documenting GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and optionally JWT_SECRET_KEY would improve developer experience and clarify setup requirements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 43 - 48, Add a .env.example listing
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and JWT_SECRET_KEY (noting
JWT_SECRET_KEY is optional because auth_service.py falls back to
secrets.token_urlsafe(32)), and update README or CONTRIBUTING.md to document
these required/optional env vars and how to obtain/configure them for local
development; reference the docker-compose environment block (DATABASE_URL,
REDIS_URL, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, JWT_SECRET) and
auth_service.py (the GitHubOAuthError checks and JWT_SECRET_KEY fallback) so the
new docs explicitly state which variables must be set before first auth and
which have defaults.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 3: Add a POSIX-compliant trailing newline to the end of the .env.example
file so the final line (containing REDIS_URL=) ends with a newline character;
update the file by ensuring there is a newline after the REDIS_URL= line (no
other changes needed).

In `@backend/.env.example`:
- Line 5: Update the .env.example to clarify that DATABASE_URL and REDIS_URL
should use localhost for host-run development but must use docker-compose
service names when running in containers: specifically reference the
DATABASE_URL and REDIS_URL entries and either add a short inline comment above
each (or a single block comment) explaining localhost vs service names (use "db"
for DATABASE_URL and "redis" for REDIS_URL under docker-compose), or provide two
example blocks labeled "host development" and "docker-compose development"
showing the alternate values; ensure the examples mention which setup they apply
to so developers know which to pick.
- Line 16: Update the ALLOWED_ORIGINS environment variable value so it includes
the frontend's actual dev port (http://localhost:8081) and remove the outdated
http://localhost:8080 entry; keep http://localhost:5173 if you want to preserve
the common Vite default. Modify the ALLOWED_ORIGINS line (the ALLOWED_ORIGINS
variable) accordingly so backend CORS allows the remapped frontend port.
- Line 13: Replace the weak JWT_SECRET placeholder with a stronger, clearly
non-usable example and add a prominent security warning comment; specifically
update the JWT_SECRET entry (symbol: JWT_SECRET) in .env.example to use a
complex placeholder (e.g., a long random-looking string) and prepend or append a
brief comment stating this MUST be changed before production to avoid critical
security risk.
- Line 3: Add a brief security note next to the POSTGRES_PASSWORD entry in the
.env.example to warn that the default value "postgres" is only for local
development and that a strong, unique password must be used in
staging/production; update the comment near the POSTGRES_PASSWORD line so
developers see the reminder when copying the example.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f0a9ecd6-63a9-469b-9377-0ac7124f6a9a

📥 Commits

Reviewing files that changed from the base of the PR and between 893d93a and de39573.

📒 Files selected for processing (4)
  • .env.example
  • .gitignore
  • backend/.env.example
  • frontend/.env.example
💤 Files with no reviewable changes (1)
  • .gitignore

.env.example Outdated
@@ -0,0 +1,3 @@
GITHUB_TOKEN=

REDIS_URL= No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a trailing newline.

The file is missing a trailing newline, which is flagged by static analysis and is a POSIX text file convention.

📝 Proposed fix
 REDIS_URL=
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
REDIS_URL=
REDIS_URL=
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 3-3: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example at line 3, Add a POSIX-compliant trailing newline to the end of
the .env.example file so the final line (containing REDIS_URL=) ends with a
newline character; update the file by ensuring there is a newline after the
REDIS_URL= line (no other changes needed).

@@ -0,0 +1,16 @@
# Database
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a security note for the weak default password.

While POSTGRES_PASSWORD=postgres is acceptable for local development examples, adding a brief comment reminding developers to use strong passwords in staging/production environments would improve security awareness.

💡 Proposed addition
 POSTGRES_USER=postgres
+# WARNING: Change this to a strong password for staging/production
 POSTGRES_PASSWORD=postgres
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 3-3: [UnorderedKey] The POSTGRES_PASSWORD key should go before the POSTGRES_USER key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/.env.example` at line 3, Add a brief security note next to the
POSTGRES_PASSWORD entry in the .env.example to warn that the default value
"postgres" is only for local development and that a strong, unique password must
be used in staging/production; update the comment near the POSTGRES_PASSWORD
line so developers see the reminder when copying the example.

POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=solfoundry
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/solfoundry
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Clarify localhost vs. container service names for docker-compose usage.

The DATABASE_URL and REDIS_URL use localhost, which is correct when running the backend directly on the host machine. However, when using docker-compose (the primary local development setup per the PR), these should reference the service names (db and redis) instead of localhost.

Consider either:

  1. Adding a comment explaining when to use localhost vs. service names, OR
  2. Providing separate environment examples for host-based vs. container-based development
📋 Proposed clarification
 # Database
+# For local host-based development, use localhost
+# For docker-compose, change localhost to 'db' (service name)
 POSTGRES_USER=postgres
 POSTGRES_PASSWORD=postgres
 POSTGRES_DB=solfoundry
 DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/solfoundry

 # Cache
+# For docker-compose, change localhost to 'redis' (service name)
 REDIS_URL=redis://localhost:6379/0

Also applies to: 8-8

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 5-5: [UnorderedKey] The DATABASE_URL key should go before the POSTGRES_DB key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/.env.example` at line 5, Update the .env.example to clarify that
DATABASE_URL and REDIS_URL should use localhost for host-run development but
must use docker-compose service names when running in containers: specifically
reference the DATABASE_URL and REDIS_URL entries and either add a short inline
comment above each (or a single block comment) explaining localhost vs service
names (use "db" for DATABASE_URL and "redis" for REDIS_URL under
docker-compose), or provide two example blocks labeled "host development" and
"docker-compose development" showing the alternate values; ensure the examples
mention which setup they apply to so developers know which to pick.

JWT_SECRET=your_jwt_secret_change_me

# CORS
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8080
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix ALLOWED_ORIGINS to match the frontend port.

The ALLOWED_ORIGINS lists ports 5173 and 8080, but according to the PR summary, the frontend has been remapped to port 8081. This mismatch will cause CORS errors when the frontend tries to communicate with the backend.

🔧 Proposed fix
-ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8080
+ALLOWED_ORIGINS=http://localhost:8081,http://localhost:5173

Note: Keeping 5173 as it's the default Vite dev server port that developers might use outside of Docker.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8080
ALLOWED_ORIGINS=http://localhost:8081,http://localhost:5173
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/.env.example` at line 16, Update the ALLOWED_ORIGINS environment
variable value so it includes the frontend's actual dev port
(http://localhost:8081) and remove the outdated http://localhost:8080 entry;
keep http://localhost:5173 if you want to preserve the common Vite default.
Modify the ALLOWED_ORIGINS line (the ALLOWED_ORIGINS variable) accordingly so
backend CORS allows the remapped frontend port.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
.env.example (1)

59-59: 🧹 Nitpick | 🔵 Trivial

Add trailing newline for POSIX compliance.

The file is missing a trailing newline, which is a POSIX text file convention and flagged by static analysis.

📝 Proposed fix
 API_URL=https://api.solfoundry.org
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example at line 59, The file ends with the line
"API_URL=https://api.solfoundry.org" but lacks a trailing newline; add a single
POSIX-compliant newline character at the end of the file so the file terminates
with a newline (i.e., ensure the final line "API_URL=https://api.solfoundry.org"
is followed by a newline).
frontend/nginx.conf (1)

38-42: 🧹 Nitpick | 🔵 Trivial

Health endpoint should specify Content-Type header.

The /health endpoint returns plain text but doesn't specify the Content-Type, which may cause clients or monitoring tools to misinterpret the response.

♻️ Proposed fix
     # Internal health check for container
     location /health {
         access_log off;
+        default_type text/plain;
         return 200 'ok';
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/nginx.conf` around lines 38 - 42, The /health location currently
returns plain text without a Content-Type; update the location /health block to
explicitly set the response Content-Type (e.g., add_header Content-Type
"text/plain; charset=utf-8"; or set default_type/text/plain) so clients and
monitoring tools correctly interpret the response from the location /health
handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/nginx.conf`:
- Around line 44-48: The static-assets location block (location ~*
\.(js|css|png|jpg|jpeg|gif|ico|svg)$) currently uses add_header which overrides
parent add_header directives and thus drops the security headers defined earlier
(lines 6-11); fix by adding the same security add_header directives into this
static-assets location (or include a shared file with those headers) and use the
"always" flag where appropriate so headers like X-Frame-Options,
X-Content-Type-Options, X-XSS-Protection, Referrer-Policy and
Content-Security-Policy are applied to static assets as well.

---

Duplicate comments:
In @.env.example:
- Line 59: The file ends with the line "API_URL=https://api.solfoundry.org" but
lacks a trailing newline; add a single POSIX-compliant newline character at the
end of the file so the file terminates with a newline (i.e., ensure the final
line "API_URL=https://api.solfoundry.org" is followed by a newline).

In `@frontend/nginx.conf`:
- Around line 38-42: The /health location currently returns plain text without a
Content-Type; update the location /health block to explicitly set the response
Content-Type (e.g., add_header Content-Type "text/plain; charset=utf-8"; or set
default_type/text/plain) so clients and monitoring tools correctly interpret the
response from the location /health handler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7238714c-d8ce-4048-a652-0e7aa010bb13

📥 Commits

Reviewing files that changed from the base of the PR and between de39573 and 204747a.

📒 Files selected for processing (6)
  • .env.example
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • backend/tests/conftest.py
  • docker-compose.yml
  • frontend/nginx.conf

Comment on lines +44 to +48
# Cache Control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Security headers are lost in nested location blocks with add_header.

Nginx's add_header directive in a nested location block (like this one for static assets) completely overrides all add_header directives from parent blocks. This means the security headers defined at lines 6-11 won't apply to static assets, removing XSS and clickjacking protections.

🔧 Proposed fix - include security headers in static asset location
     # Cache Control for static assets
     location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
         expires 30d;
         add_header Cache-Control "public, no-transform";
+        # Must re-include security headers (nginx clears parent add_header in nested blocks)
+        add_header X-Frame-Options "DENY" always;
+        add_header X-Content-Type-Options "nosniff" always;
+        add_header Referrer-Policy "no-referrer" always;
     }

Alternatively, consider using include directives with a shared security headers file, or use the more_set_headers module from nginx-extras which doesn't have this inheritance issue.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Cache Control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
# Cache Control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
# Must re-include security headers (nginx clears parent add_header in nested blocks)
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/nginx.conf` around lines 44 - 48, The static-assets location block
(location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$) currently uses add_header
which overrides parent add_header directives and thus drops the security headers
defined earlier (lines 6-11); fix by adding the same security add_header
directives into this static-assets location (or include a shared file with those
headers) and use the "always" flag where appropriate so headers like
X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy and
Content-Security-Policy are applied to static assets as well.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
backend/.env.example (1)

18-18: ⚠️ Potential issue | 🟠 Major

Update the documented CORS origin to the new frontend port.

Line 18 still allows http://localhost:8080, but this PR moves the local frontend to http://localhost:8081. Any backend instance bootstrapped from this example will reject requests from the compose frontend, breaking local browser flows with CORS failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/.env.example` at line 18, Update the ALLOWED_ORIGINS example to
include the new frontend local port by replacing the old origin; specifically
modify the ALLOWED_ORIGINS entry (the "ALLOWED_ORIGINS" env variable) so it
lists http://localhost:5173 and http://localhost:8081 instead of
http://localhost:8080, ensuring local backend instances will accept requests
from the moved compose frontend.
.env.example (1)

20-28: ⚠️ Potential issue | 🟠 Major

The compose-facing connection URLs still point at localhost.

Lines 20-28 say docker-compose.yml consumes DATABASE_URL and REDIS_URL, but both examples target localhost. Inside the backend container, that resolves to the container itself rather than the Postgres/Redis services, so copying this template into .env can prevent the local stack from starting cleanly unless the compose file overrides both values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 20 - 28, The example .env uses localhost for
DATABASE_URL and REDIS_URL which breaks container-to-container networking;
update the DATABASE_URL and REDIS_URL examples to use the docker service
hostnames (the DB and Redis service names from compose) instead of localhost so
that containers resolve the correct services (i.e., change the host portion
referenced by DATABASE_URL and REDIS_URL to the compose service names rather
than localhost) and include a short note that these values should be overridden
if your compose uses different service names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 46-61: The .env.example currently instructs maintainers to set
repository-level secrets and hardcodes API_URL to production which risks
staging/preview builds hitting prod; update the file to remove or replace the
production default for API_URL, clarify that VERCEL_TOKEN, VERCEL_ORG_ID,
VERCEL_PROJECT_ID, DIGITALOCEAN_ACCESS_TOKEN, DIGITALOCEAN_CLUSTER_NAME and
API_URL must be configured as environment-specific secrets (e.g., production vs
staging/preview) in CI and deployment configs, add placeholder names for staging
(e.g., API_URL_STAGING) and a short note to wire deploy.yml to use the
appropriate environment-specific secret rather than a global production value.

---

Duplicate comments:
In @.env.example:
- Around line 20-28: The example .env uses localhost for DATABASE_URL and
REDIS_URL which breaks container-to-container networking; update the
DATABASE_URL and REDIS_URL examples to use the docker service hostnames (the DB
and Redis service names from compose) instead of localhost so that containers
resolve the correct services (i.e., change the host portion referenced by
DATABASE_URL and REDIS_URL to the compose service names rather than localhost)
and include a short note that these values should be overridden if your compose
uses different service names.

In `@backend/.env.example`:
- Line 18: Update the ALLOWED_ORIGINS example to include the new frontend local
port by replacing the old origin; specifically modify the ALLOWED_ORIGINS entry
(the "ALLOWED_ORIGINS" env variable) so it lists http://localhost:5173 and
http://localhost:8081 instead of http://localhost:8080, ensuring local backend
instances will accept requests from the moved compose frontend.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bcc4d0b6-49d4-4f43-80c3-381521bae881

📥 Commits

Reviewing files that changed from the base of the PR and between 204747a and 1c26ac1.

📒 Files selected for processing (5)
  • .env.example
  • .github/workflows/ci-cd.yml
  • .github/workflows/deploy.yml
  • backend/.env.example
  • frontend/public/health

Comment on lines +46 to +61
# CI/CD (GitHub Actions) - deployment secrets
# ----------------------------
# These values must be set as GitHub repository secrets.

# deploy.yml -> Vercel
VERCEL_TOKEN=your_vercel_token
VERCEL_ORG_ID=your_vercel_org_id
VERCEL_PROJECT_ID=your_vercel_project_id

# deploy.yml -> DigitalOcean Kubernetes
DIGITALOCEAN_ACCESS_TOKEN=your_digitalocean_access_token
DIGITALOCEAN_CLUSTER_NAME=your_digitalocean_cluster_name

# deploy.yml -> frontend build injection
# Used by the `deploy.yml` frontend build step (currently wired into the build environment).
API_URL=https://api.solfoundry.org No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The deployment secret guidance is not environment-safe.

Line 48 tells maintainers to configure these as repository-level secrets, and Line 61 even defaults API_URL to the production API. For a pipeline that now supports both staging and production, that guidance makes it easy for preview/staging deployments to build against production endpoints or infrastructure instead of isolated environment-specific values.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 52-52: [UnorderedKey] The VERCEL_ORG_ID key should go before the VERCEL_TOKEN key

(UnorderedKey)


[warning] 53-53: [UnorderedKey] The VERCEL_PROJECT_ID key should go before the VERCEL_TOKEN key

(UnorderedKey)


[warning] 61-61: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 46 - 61, The .env.example currently instructs
maintainers to set repository-level secrets and hardcodes API_URL to production
which risks staging/preview builds hitting prod; update the file to remove or
replace the production default for API_URL, clarify that VERCEL_TOKEN,
VERCEL_ORG_ID, VERCEL_PROJECT_ID, DIGITALOCEAN_ACCESS_TOKEN,
DIGITALOCEAN_CLUSTER_NAME and API_URL must be configured as environment-specific
secrets (e.g., production vs staging/preview) in CI and deployment configs, add
placeholder names for staging (e.g., API_URL_STAGING) and a short note to wire
deploy.yml to use the appropriate environment-specific secret rather than a
global production value.

@github-actions
Copy link
Copy Markdown

❌ Multi-LLM Code Review — REQUEST CHANGES

Aggregated Score: 6.7/10 (median of 3 models)
Tier: tier-2 | Threshold: 7.0/10

Model Verdicts

Model Raw Score Calibrated Verdict
GPT-5.4 6.4/10 6.4/10 ⚠️
Gemini 2.5 Pro 7.2/10 7.2/10
Grok 4 7.6/10 6.7/10 (x0.88) ⚠️

Category Scores (Median)

Category Score
Quality ███████░░░ 7.7/10
Correctness ██████░░░░ 6.0/10
Security ███████░░░ 7.0/10
Completeness ██████░░░░ 6.1/10
Tests █████░░░░░ 5.7/10
Integration ███████░░░ 7.0/10

Warning: CodeRabbit Critical Issues

CodeRabbit flagged 1 critical issues that appear unresolved.
These must be addressed before approval.

Warning: Bounty Spec Compliance: PARTIAL

This submission partially meets the acceptance criteria. Review the issues above for gaps.

Summary

GPT-5.4: This PR makes a serious attempt at the bounty and adds substantial Docker/CI/CD infrastructure, including multi-stage images, compose orchestration, health checks, image publishing, and a rollback-oriented deploy input. However, it still falls short of T2 production quality because key acceptance criteria are only partially met, the deployment workflow is brittle and environment-assumptive, and test coverage is too shallow for the amount of infrastructure introduced.
Gemini 2.5 Pro: The submission provides a comprehensive and high-quality CI/CD pipeline with excellent containerization practices, addressing nearly all static analysis feedback. However, it suffers from significant correctness and completeness issues, most notably the lack of database migrations for the staging environment and a deviation from the specified deployment targets. While the testing strategy is strong, these gaps prevent it from being a production-ready, T2-level solution without revisions.
Grok 4: This PR implements a solid CI/CD pipeline with Docker support, meeting most bounty requirements with production-ready features like multi-stage builds and health checks. However, some security concerns from static analysis remain partially unaddressed, and test coverage could be more comprehensive for a T2 bounty. Overall, it's a strong submission but needs minor refinements for full production quality.

Issues

  • [GPT-5.4] The bounty requires local Docker Compose for backend + frontend + PostgreSQL + Redis, but the deploy pipeline only provisions Redis and assumes an existing backend deployment/database/Kubernetes resources instead of defining a complete staging stack.
  • [GPT-5.4] Staging auto-deploy is not convincingly complete: frontend goes to Vercel preview while backend targets DigitalOcean Kubernetes, but there is no end-to-end staging URL for the full stack and no proof that the frontend is wired to the staged backend.
  • [GPT-5.4] Production deploy with manual approval gate is only implicit via GitHub environments; the workflow does not clearly enforce or document an approval gate in-repo, and correctness depends on external environment configuration.
  • [GPT-5.4] Rollback support is partial: only backend image_tag rollback is implemented, frontend is explicitly left unchanged, so previous full release rollback is not supported.
  • [GPT-5.4] Tests are too thin for a T2 infra PR: the Postgres integration job runs a single health-format test, and there are no assertions covering deploy logic, image tagging behavior, rollback path, or compose failure modes.
  • [Gemini 2.5 Pro] The migrate-database job is configured to run only for the production environment. Database schema changes must be applied and tested in staging, making this a critical omission in the deployment workflow.
  • [Gemini 2.5 Pro] The new backend-integration-postgres CI job is an excellent addition, but it only runs a single test (test_health_check_format). To be effective, the entire backend test suite should be run against the Postgres service container to catch integration bugs.
  • [Gemini 2.5 Pro] The bounty specified DigitalOcean, Railway, or Fly.io for deployment. The frontend is deployed to Vercel, which was not on the list and may not align with the project's infrastructure strategy.
  • [Gemini 2.5 Pro] The Kubernetes manifest for Redis deploys it without authentication. While it's within a namespace, it's a security risk to have internal services unsecured.
  • [Grok 4] Unresolved major findings: Containers running as root (despite USER directives, static analysis flags it); overly permissive chmod on logs (755 is better but consider 750 for stricter access).
  • [Grok 4] Frontend Dockerfile uses npm ci, but add explicit non-root user confirmation for nginx-unprivileged image to address static analysis flag.
  • [Grok 4] docker-compose.yml exposes database port to host, which is a security risk; consider removing for production-like local dev.
  • [Grok 4] Missing health condition in frontend depends_on; update to wait for backend healthy state.
  • [Grok 4] nginx.conf has some security headers, but add X-Content-Type-Options and timeout settings as suggested.

Suggestions

  • [GPT-5.4] Define a complete, reproducible staging deployment target in code, including backend, Redis, and database dependencies, and expose a single validated staging URL that exercises the integrated system.
  • [GPT-5.4] Tighten workflow conditions so deploy and verification jobs only run when prerequisite build/push jobs succeed, rather than using broad always() semantics.
  • [GPT-5.4] Expand CI coverage with meaningful infrastructure tests: compose startup assertions, migration execution, image tag/rollback path validation, and negative-path checks for missing secrets or unhealthy services.
  • [GPT-5.4] Make the stack secure-by-default by removing unnecessary host port exposure for internal services, documenting required secrets more clearly, and using stronger non-copyable secret placeholders in all .env.example files.
  • [Gemini 2.5 Pro] For production/staging environments, consider using managed database and Redis services (e.g., DigitalOcean Managed PostgreSQL/Redis) instead of deploying them as Kubernetes workloads. This offloads management, backup, and scaling responsibilities.
  • [Gemini 2.5 Pro] Refactor the complex inline shell and Python scripts in ci.yml (e.g., the health check loops) into standalone, executable scripts in a .github/scripts directory to improve workflow readability and reusability.
  • [Gemini 2.5 Pro] The root .env.example file is missing a final newline character, which is a POSIX convention and was flagged by static analysis.
  • [Grok 4] Enhance JWT_SECRET example in all .env files with a stronger random value and explicit generation instructions.
  • [Grok 4] Expand integration tests to cover more edge cases, such as failed health checks and rollback scenarios.
  • [Grok 4] Add explicit rollback testing in CI and document deployment process in CONTRIBUTING.md.

Contributor stats: 4 merged bounty PRs, rep score 32


SolFoundry Multi-LLM Review Pipeline v2.0 — GPT-5.4 + Gemini 2.5 Pro + Grok 4
Scoring: median aggregation | Calibration: Grok x0.88

Next Steps

Please address the issues above and push updated commits. The review will re-run automatically.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: changes requested (see comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant