Skip to content

feat: add container packaging, Docker Compose, and CI pipeline#269

Merged
Aureliolo merged 4 commits intomainfrom
feat/container-packaging-ci
Mar 10, 2026
Merged

feat: add container packaging, Docker Compose, and CI pipeline#269
Aureliolo merged 4 commits intomainfrom
feat/container-packaging-ci

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Backend container: Three-stage Dockerfile (builder → setup → Chainguard Python distroless runtime). CIS Docker Benchmark v1.6.0 hardened: non-root UID 65532, cap_drop: ALL, no-new-privileges, read_only rootfs, no shell/uv/package-manager in final image. All base images version-pinned with Dependabot auto-updates for digests.
  • Web UI scaffold: nginxinc/nginx-unprivileged:1.29.5-alpine with SPA routing, /api/ and /ws reverse proxy to backend, security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy), gzip compression. Placeholder index.html with live backend health status.
  • Docker Compose: CIS-hardened orchestration with named volumes, container-level healthchecks, tmpfs for writable paths, configurable ports via env vars (BACKEND_PORT, WEB_PORT).
  • CI pipeline (.github/workflows/docker.yml): Build → Trivy + Grype vulnerability scan → push to GHCR → cosign signing with SBOM + SLSA provenance. All actions pinned to full-length commit SHAs. Images only published after scans pass.
  • Path-aware CSP middleware: Strict default-src 'self' for API routes; relaxed policy for /docs/ allowing Scalar UI resources from cdn.jsdelivr.net, fonts.scalar.com, proxy.scalar.com.
  • Single root .dockerignore: Consolidated from per-service files (which were ignored by Docker when context is repo root).
  • All GitHub Actions SHA-pinned across docker.yml, ci.yml, dependency-review.yml, secret-scan.yml.
  • Documentation updates: DESIGN_SPEC.md §15.2 (containerization row), §15.3 (project structure), §15.4 (Container Packaging decision). Fixed broken links in CONTRIBUTING.md and getting_started.md after .github/ move.

Test plan

  • docker compose -f docker/compose.yml build — both images build successfully
  • docker compose -f docker/compose.yml up -d — containers start, backend healthy
  • curl http://localhost:8000/api/v1/health — backend responds with version 0.1.0
  • curl http://localhost:3000/api/v1/health — nginx proxies to backend correctly
  • curl -sI http://localhost:3000/ — CSP and security headers present
  • http://localhost:8000/docs/api — Scalar UI renders with relaxed CSP
  • Ruff lint + mypy type-check pass (846 files, 0 issues)
  • 6637 tests pass, 94.80% coverage

Review coverage

Closes #267

Three-stage backend Dockerfile (builder → setup → Chainguard Python
distroless runtime) with CIS Docker Benchmark hardening: non-root
UID 65532, cap_drop ALL, no-new-privileges, read-only rootfs,
no shell/uv/package-manager in final image.

Web UI scaffold with nginx-unprivileged (non-root), SPA routing,
API/WebSocket reverse proxy to backend, security headers.

Docker Compose orchestration with named volumes, healthchecks,
tmpfs for writable paths, and configurable ports via env vars.

GitHub Actions CI workflow: parallel backend + web builds, GHCR push,
version tagging from pyproject.toml, Trivy + Grype vulnerability scans,
cosign image signing, SBOM + SLSA provenance.

Housekeeping: move CONTRIBUTING.md and SECURITY.md to .github/,
delete empty config/ directory, add Dependabot Docker digest updates,
update README.md and CLAUDE.md with Docker documentation.
…CSP, docs

- Pin ALL GitHub Actions to full-length commit SHAs across 4 workflows
- Scan-before-push pattern: build → Trivy/Grype scan → push (prevents
  publishing vulnerable images to GHCR)
- Replace deprecated X-XSS-Protection with Content-Security-Policy header
  in nginx; add path-aware CSP middleware in backend (strict for API,
  relaxed for Scalar docs allowing cdn.jsdelivr.net + fonts.scalar.com)
- Consolidate per-service .dockerignore files into single root .dockerignore
- Fix 6 broken relative links in CONTRIBUTING.md and getting_started.md
  after CONTRIBUTING.md move to .github/
- Update DESIGN_SPEC.md §15.2 (containerization row), §15.3 (add docker/,
  web/, .github/ to project structure), §15.4 (add Container Packaging
  design decision)
- Fix compose.override.yml misleading "auto-merged" comment
- Fix fetch() r.ok check in web/index.html for non-2xx responses
- Fix .env.example copy command path for repo root usage
- Add version extraction error handling in CI
- Add ENTRYPOINT [] explanatory comment in backend Dockerfile
- Update CONTRIBUTING.md and getting_started.md project structure listings
Copilot AI review requested due to automatic review settings March 10, 2026 17:18
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 10, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/checkout de0fac2e4500dabe0009e67214ff5f5447ce83dd 🟢 5.9
Details
CheckScoreReason
Maintained⚠️ 23 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2
Code-Review🟢 10all changesets reviewed
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Packaging⚠️ -1packaging workflow not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Security-Policy🟢 9security policy file detected
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
SAST🟢 8SAST tool detected but not run on all commits

Scanned Files

  • .github/workflows/secret-scan.yml

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly advances the project's deployment and operational capabilities by introducing a robust containerization strategy for both the backend and web UI. It establishes a secure and automated CI/CD pipeline for building and managing container images, while also providing a foundational web interface with secure proxying. These changes enhance reproducibility, security, and ease of deployment for the entire application stack.

Highlights

  • Containerization: Introduced Dockerfiles for both backend (Python, Chainguard distroless, CIS-hardened) and web UI (Nginx unprivileged), along with a Docker Compose setup for orchestrated deployment.
  • Security Hardening: Implemented CIS Docker Benchmark v1.6.0 hardening for containers, including non-root user execution, capability dropping, no-new-privileges, read-only root filesystems, and tmpfs for writable paths.
  • CI/CD Pipeline: Established a GitHub Actions workflow (.github/workflows/docker.yml) for building, vulnerability scanning (Trivy, Grype), pushing to GHCR, and cosign signing with SBOM and SLSA provenance, ensuring images are only published after successful scans.
  • Web UI Scaffold: Provided a basic Nginx-based web UI scaffold with SPA routing, reverse proxying for /api/ and /ws to the backend, gzip compression, and essential security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy).
  • Content Security Policy (CSP): Developed a path-aware CSP middleware for the backend, applying a strict policy for API routes and a relaxed policy for /docs/ to accommodate external Scalar UI resources.
  • Documentation Updates: Updated DESIGN_SPEC.md, CONTRIBUTING.md, getting_started.md, CLAUDE.md, and README.md to reflect the new containerization strategy, CI/CD processes, and project structure, including corrected internal links and new setup instructions.
Changelog
  • .dockerignore
    • Added a consolidated .dockerignore file for repository-wide Docker builds.
  • .github/CONTRIBUTING.md
    • Renamed CONTRIBUTING.md and updated internal links to reflect the new .github directory.
    • Updated the package structure description to include docker/ and web/ directories.
    • Corrected relative links to docs/getting_started.md, CLAUDE.md, and LICENSE.
  • .github/SECURITY.md
    • Renamed SECURITY.md to .github/SECURITY.md.
  • .github/dependabot.yml
    • Configured Dependabot to monitor and update Docker images for backend and web services.
  • .gitignore
    • Updated .gitignore to exclude node_modules and dist directories within the web/ folder.
  • CLAUDE.md
    • Expanded the documentation to include Docker build and run instructions.
    • Updated the CI section with details on the new Docker workflow and Dependabot integration for Docker images.
  • DESIGN_SPEC.md
    • Updated the containerization row in the design table with detailed production packaging information.
    • Added docker/ and web/ directories to the project structure diagram.
    • Included a new section (15.4) detailing the Container Packaging decision, explaining the choice of Chainguard distroless and GHCR.
  • README.md
    • Updated the 'Key Technologies' section to include Docker with Chainguard Python and Nginx.
    • Revised the Docker system requirement to include Docker Compose for full stack operation.
    • Added a new 'Docker Compose (full stack)' section with build and run instructions.
    • Updated the link for 'Contributing' documentation to its new location.
  • docker/.env.example
    • Added an example environment variable file for Docker Compose configurations, including LLM API key, application settings, and container networking ports.
  • docker/backend/Dockerfile
    • Created a multi-stage Dockerfile for the backend service, utilizing python:3.14.3-slim for building and cgr.dev/chainguard/python distroless for the runtime.
    • Implemented CIS Docker Benchmark hardening, including non-root user (UID 65532) and health checks.
    • Added OCI image labels for metadata.
  • docker/compose.override.yml
    • Added a Docker Compose override file for local development, enabling debug logging and providing a commented-out option for mounting the Docker socket.
  • docker/compose.yml
    • Implemented the main Docker Compose configuration for the backend and web services.
    • Configured CIS hardening for both services, including no-new-privileges, cap_drop: ALL, read_only: true, and tmpfs mounts.
    • Defined named volumes for persistent data and health checks for the backend service.
  • docker/web/Dockerfile
    • Created a Dockerfile for the web UI, based on nginxinc/nginx-unprivileged:1.29.5-alpine.
    • Added OCI image labels and configured a health check.
  • docs/getting_started.md
    • Updated the API framework description from FastAPI to Litestar.
    • Revised the CLI description to 'CLI interface (future)'.
    • Added hr/, observability/, and persistence/ to the src/ai_company/ package structure.
    • Included docker/ and web/ in the top-level project structure.
    • Corrected the relative link for CONTRIBUTING.md.
  • src/ai_company/api/app.py
    • Imported CSPMiddleware from ai_company.api.middleware.
    • Removed the static Content-Security-Policy header from the default response headers.
    • Integrated CSPMiddleware into the application's middleware stack.
  • src/ai_company/api/middleware.py
    • Updated the module docstring to reflect the addition of path-aware CSP headers.
    • Implemented a new CSPMiddleware class to dynamically apply Content-Security-Policy headers based on the request path, with strict policies for API routes and relaxed policies for /docs/.
  • web/index.html
    • Added a basic index.html file for the web UI, featuring a placeholder dashboard and a JavaScript-based health status check for the backend API.
  • web/nginx.conf
    • Configured Nginx for the web UI, including listening on port 8080, SPA routing, and proxying /api/ and /ws requests to the backend service.
    • Enabled gzip compression and added essential security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy).
Ignored Files
  • Ignored by pattern: .github/workflows/** (4)
    • .github/workflows/ci.yml
    • .github/workflows/dependency-review.yml
    • .github/workflows/docker.yml
    • .github/workflows/secret-scan.yml
Activity
  • No specific activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 21daad5b-601b-4710-851d-973fdfae92d0

📥 Commits

Reviewing files that changed from the base of the PR and between 53466cc and 3c5c182.

📒 Files selected for processing (6)
  • .github/workflows/docker.yml
  • tests/unit/api/test_middleware.py
  • web/app.js
  • web/index.html
  • web/nginx.conf
  • web/style.css

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Docker Compose support for running the full stack locally.
    • Web dashboard landing page with live backend health checks (auto-refresh).
  • Security

    • Path-aware Content Security Policy (strict for API, relaxed for docs).
    • Hardened container images and runtime (non-root, minimal attack surface); CI now builds, scans, signs images.
  • Documentation

    • Updated getting-started, CI, and contributor docs to include Docker usage and workflows.
  • Maintenance

    • Consolidated Docker ignore rules and added automated Docker dependency updates.
  • Tests

    • Added tests covering CSP middleware behavior.

Walkthrough

Adds production-grade containerization and CI: multi-stage hardened Dockerfiles for backend and web, Docker Compose and env scaffolding, a GitHub Actions workflow to build/scan/push/sign images, Dependabot docker updates, repo ignore updates, web static assets, path-aware CSP middleware, and tests and docs updates.

Changes

Cohort / File(s) Summary
Dockerfiles & container build
docker/backend/Dockerfile, docker/web/Dockerfile, docker/*
New multi-stage, CIS-hardened backend and nginx-unprivileged web Dockerfiles (non-root users, healthchecks, OCI labels, pinned bases).
CI / Image pipeline
.github/workflows/docker.yml, .github/workflows/ci.yml, .github/workflows/dependency-review.yml, .github/workflows/secret-scan.yml
Introduces Docker workflow to build, Trivy/Grype-scan, push SBOM/provenance to GHCR and cosign-sign; pins several GH Actions to commit SHAs across workflows.
Compose & runtime config
docker/compose.yml, docker/compose.override.yml, docker/.env.example
Adds Compose for backend/web with security hardening, shared volume, and a local override; env example provided.
Repository ignores
.dockerignore, .gitignore
Adds root .dockerignore covering backend/web build contexts and common excludes; extends .gitignore with web artifacts.
Web UI & proxy config
web/index.html, web/app.js, web/style.css, web/nginx.conf
Adds static dashboard, periodic backend health-check JS, dark-theme CSS, and nginx config with SPA routing, gzip, security headers, and API/WebSocket proxying.
API middleware & tests
src/ai_company/api/middleware.py, src/ai_company/api/app.py, tests/unit/api/test_middleware.py
Adds path-aware CSPMiddleware (strict for API, relaxed for /docs), integrates it into middleware stack, adjusts typing, and expands unit tests to assert CSP behavior.
Docs & contribution
README.md, DESIGN_SPEC.md, CLAUDE.md, docs/getting_started.md, .github/CONTRIBUTING.md
Extends documentation and design spec to document containerization, CI pipeline, Docker Compose usage, and updates relative links.
Dependabot
.github/dependabot.yml
Adds daily Dependabot entries to update Docker images for backend and web.

Sequence Diagram

sequenceDiagram
    actor Developer
    participant GitHub as GitHub<br/>(repo)
    participant GHA as GitHub Actions
    participant Builder as Docker Build
    participant Trivy as Trivy<br/>(Scanner)
    participant Grype as Grype<br/>(Scanner)
    participant Registry as GHCR<br/>(Registry)
    participant Cosign as Cosign<br/>(Signer)

    Developer->>GitHub: push to main or tag v*
    GitHub->>GHA: trigger Docker workflow

    rect rgba(100, 150, 200, 0.5)
    Note over GHA,Builder: Build Phase
    GHA->>GHA: extract version from pyproject.toml
    GHA->>Builder: build backend image (multi-stage)
    Builder-->>GHA: image artifact
    end

    rect rgba(150, 100, 150, 0.5)
    Note over GHA,Trivy: Scanning Phase
    GHA->>Trivy: scan image (fail on CRITICAL/HIGH)
    Trivy-->>GHA: report
    GHA->>Grype: scan image (fail on HIGH)
    Grype-->>GHA: report
    end

    rect rgba(100, 200, 100, 0.5)
    Note over GHA,Registry: Push & Sign Phase
    alt scans pass
        GHA->>Registry: push image with tags & SBOM
        Registry-->>GHA: return digest
        GHA->>Cosign: sign image using digest
        Cosign-->>Registry: upload signature
    else scans fail
        GHA-->>Developer: report failure
    end
    end

    Note over GHA: same flow repeated for web image
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding container packaging, Docker Compose, and CI pipeline infrastructure to the project.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing backend/web containers, Docker Compose setup, CI pipeline, CSP middleware, documentation updates, and test validation.
Linked Issues check ✅ Passed All requirements from linked issue #267 are met: production-ready backend container with multi-stage build and CIS hardening [backend/Dockerfile], web/proxy container with SPA routing [web/Dockerfile, web/nginx.conf], Docker Compose orchestration [docker/compose.yml], and CI pipeline with scanning and signing [.github/workflows/docker.yml].
Out of Scope Changes check ✅ Passed All changes are scoped to container packaging, Docker Compose setup, CI pipeline implementation, path-aware CSP middleware for docs, and supporting documentation/configuration updates as defined in issue #267.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/container-packaging-ci
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/container-packaging-ci

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

Repository owner deleted a comment from coderabbitai bot Mar 10, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves the project's infrastructure by adding a comprehensive containerization and CI solution, utilizing multi-stage, hardened, distroless Docker images and a security-conscious Docker Compose setup. While the implementation follows many security best practices, a critical vulnerability has been identified: the Content Security Policy (CSP) for both the documentation UI and the placeholder web page currently uses 'unsafe-inline', which significantly weakens protection against Cross-Site Scripting (XSS) attacks. This should be addressed to maintain a high security standard. Additionally, a minor maintainability point involves a workaround in the backend Dockerfile (touch README.md) that could be addressed for better clarity. Overall, this is a very strong contribution that significantly improves the project's infrastructure and production readiness.

web/nginx.conf Outdated
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The Content-Security-Policy header in web/nginx.conf currently uses 'unsafe-inline' for script-src and style-src. This significantly weakens the application's protection against Cross-Site Scripting (XSS) attacks, as it applies to the entire web application. It is strongly recommended to move inline scripts and styles from web/index.html into separate .js and .css files. If inline content must remain, consider using SHA256 hashes for specific blocks to maintain a more secure CSP.

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;

uv sync --frozen --no-dev --no-install-project

# Copy source and install project (dummy README satisfies hatchling metadata)
RUN touch README.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The use of touch README.md appears to be a workaround for a hatchling metadata requirement. While this works, it can be confusing for future maintainers and makes the build process less explicit. It would be more robust to configure the build system (likely in pyproject.toml) to not require README.md for the wheel, or to include a minimal one within the source if it's truly necessary for the package. This would make the build process clearer and less reliant on such side effects.

Comment on lines +28 to +35
_DOCS_CSP = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "
"style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "
"img-src 'self' data: https://cdn.jsdelivr.net; "
"font-src 'self' data: https://cdn.jsdelivr.net https://fonts.scalar.com; "
"connect-src 'self' https://cdn.jsdelivr.net https://proxy.scalar.com"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The Content Security Policy (CSP) for the /docs path includes 'unsafe-inline' for both script-src and style-src. This is a security risk as it allows the execution of arbitrary inline scripts, which can lead to Cross-Site Scripting (XSS) vulnerabilities. While this might be a requirement for the Scalar UI, it's worth investigating if more secure alternatives like nonces or hashes can be used.

For example, if the inline scripts are static, you could use hashes:

script-src 'self' 'sha256-yourScriptHashHere==' https://cdn.jsdelivr.net;

This would provide a much stronger security posture by only allowing specific, known inline scripts to execute.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR introduces a complete containerization and CI/CD pipeline for the AI Company project: a three-stage CIS-hardened backend Dockerfile using Chainguard distroless Python, a non-root nginx web container, Docker Compose orchestration with read-only rootfs and tmpfs mounts, a GitHub Actions pipeline with vulnerability scanning and cosign image signing, and a new path-aware CSPMiddleware for the Litestar backend. The implementation is thorough and well-documented, directly implementing the DESIGN_SPEC §15 containerization spec.

Key findings from the review:

  • Duplicate CSP headers on proxied responses (web/nginx.conf): The server-level add_header Content-Security-Policy directive is inherited by the /api/ and /ws proxy location blocks (since neither defines its own add_header). Combined with the backend's CSPMiddleware, every proxied API response carries two Content-Security-Policy headers. Adding proxy_hide_header Content-Security-Policy; to both proxy location blocks would resolve this.
  • WebSocket proxy missing proxy_buffering off (web/nginx.conf): Without this directive, nginx may buffer data before forwarding WebSocket frames, introducing latency on the /ws proxy.
  • Inconsistent tmpfs hardening (docker/compose.yml): The web service's /var/cache/nginx and /var/run tmpfs mounts lack the noexec,nosuid flags present on /tmp, inconsistent with the stated CIS hardening posture.
  • The CSPMiddleware implementation and test coverage are clean; the path boundary logic (/docs vs /docsearch) is correctly handled.

Confidence Score: 4/5

  • This PR is safe to merge with minor fixes recommended — the duplicate CSP header issue on proxied routes is real but low impact for JSON API responses, and the remaining findings are best-practice hardening gaps.
  • The containerization implementation is solid and well-reviewed: all base images are digest-pinned, the three-stage Dockerfile is correctly structured, the CI pipeline addresses previous scan-tag issues, and the Python middleware is correct with good test coverage. The main concern is the nginx configuration leaving duplicate CSP headers on proxied responses, which — while not security-breaking for JSON API endpoints — is a misconfiguration that should be fixed before the nginx proxy is used for HTML-serving routes. The other findings (WebSocket buffering, tmpfs flags) are best-practice gaps rather than bugs.
  • Pay close attention to web/nginx.conf — the duplicate CSP header issue and missing WebSocket buffering directive are both in this file.

Important Files Changed

Filename Overview
docker/backend/Dockerfile Three-stage CIS-hardened build (builder → Chainguard setup → distroless runtime). All base images are digest-pinned, non-root UID 65532, healthcheck uses exec form (no shell needed). Well structured — bytecode compilation, venv symlink fix for Chainguard, and OCI labels are all correct.
docker/web/Dockerfile Single-stage nginx-unprivileged image, digest-pinned, copies static web assets and nginx config. Minimal and correct; healthcheck uses wget spider form appropriate for the unprivileged image.
web/nginx.conf SPA routing with API/WebSocket reverse proxy and security headers. Two issues: (1) the server-level CSP add_header is inherited by the proxied location blocks, causing duplicate Content-Security-Policy headers on every backend response; (2) the WebSocket location block is missing proxy_buffering off, which can introduce frame-forwarding latency.
docker/compose.yml CIS-hardened orchestration with named volume, read-only rootfs, cap_drop ALL, and no-new-privileges. Minor: /var/cache/nginx and /var/run tmpfs mounts lack the noexec,nosuid flags that /tmp has, inconsistent with the stated hardening posture.
.github/workflows/docker.yml Build → Trivy + Grype scan → push → cosign sign CI pipeline. All actions SHA-pinned. The scan/push dual-build concern and the file-handle-close issue raised in previous reviews are already acknowledged in code comments. The scan-ref step now uses sha-${GITHUB_SHA::7} which is always produced, resolving the previous version-tag-only scan failure.
src/ai_company/api/middleware.py New CSPMiddleware class with correct path-aware CSP injection (strict for API routes, relaxed for /docs/). RequestLoggingMiddleware is unchanged. Path boundary logic (path == "/docs" or path.startswith("/docs/")) correctly avoids false matches on /docsearch, /documents, etc.
tests/unit/api/test_middleware.py Good test coverage for CSPMiddleware: boundary path parametrization, non-HTTP scope pass-through, and integration via TestClient. Async tests in the class use asyncio_mode = "auto" correctly (no manual @pytest.mark.asyncio needed).

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Nginx as nginx (web :3000)
    participant Backend as Backend (uvicorn :8000)

    Browser->>Nginx: GET /api/v1/health
    Nginx->>Backend: proxy_pass /api/v1/health
    Note over Backend: CSPMiddleware injects CSP header
    Backend-->>Nginx: 200 OK + CSP header from middleware
    Note over Nginx: server-block add_header ALSO injects CSP
    Nginx-->>Browser: 200 OK + duplicate CSP headers warning

    Browser->>Nginx: GET / (SPA route)
    Note over Nginx: No proxy, no upstream CSP
    Nginx-->>Browser: 200 index.html + single CSP header (correct)

    Browser->>Nginx: WS /ws upgrade
    Nginx->>Backend: proxy_pass with HTTP Upgrade
    Note over Nginx: No proxy_buffering off set
    Backend-->>Nginx: 101 Switching Protocols
    Nginx-->>Browser: 101 WebSocket tunnel (potential frame buffering latency)

    Browser->>Backend: GET /docs/api (direct port 8000)
    Note over Backend: CSPMiddleware sends relaxed CSP for Scalar UI
    Backend-->>Browser: 200 Scalar UI + relaxed CSP (no nginx duplication)
Loading

Last reviewed commit: 3c5c182

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds production-oriented container packaging and deployment scaffolding for the AI Company stack, including hardened Docker images, Compose orchestration, and a supply-chain-focused CI pipeline.

Changes:

  • Introduces backend and web container images (Chainguard distroless Python runtime; unprivileged nginx web tier) plus a CIS-hardened Docker Compose setup and .env template.
  • Adds a Docker CI workflow to build, scan (Trivy/Grype), publish to GHCR, and cosign-sign images; pins GitHub Actions to commit SHAs.
  • Adds path-aware CSP handling for backend docs vs API routes and updates docs to reflect the new structure and workflows.

Reviewed changes

Copilot reviewed 20 out of 23 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
web/nginx.conf nginx SPA routing, API/WS reverse proxy, gzip, and security headers for the web tier
web/index.html Placeholder dashboard page with periodic backend health check
src/ai_company/api/middleware.py Adds CSP middleware (path-aware) alongside request logging middleware
src/ai_company/api/app.py Wires CSP middleware into Litestar app and removes static CSP response header
docs/getting_started.md Updates project structure and links after .github/ move and containerization additions
docker/web/Dockerfile Builds the web nginx image with healthcheck
docker/compose.yml CIS-hardened Compose stack for backend + web with volumes/tmpfs and health dependencies
docker/compose.override.yml Local-only overrides (debug log level; documented docker socket option)
docker/backend/Dockerfile Multi-stage backend image build (uv builder → setup → Chainguard distroless runtime)
docker/.env.example Environment variable template for running the stack via Compose
config/.gitkeep Keeps the config/ directory tracked in git
README.md Adds Docker Compose quickstart and updates contributing link
DESIGN_SPEC.md Updates containerization/packaging sections and project structure diagram
CLAUDE.md Adds Docker usage and CI notes for contributors/assistants
.gitignore Ignores future web UI build artifacts (node_modules/dist)
.github/workflows/secret-scan.yml Pins actions/checkout to a full commit SHA
.github/workflows/docker.yml New workflow: build → scan → push → sign for backend + web images
.github/workflows/dependency-review.yml Pins actions to full commit SHAs
.github/workflows/ci.yml Pins checkout and upload-artifact actions to full commit SHAs
.github/dependabot.yml Adds docker ecosystem updates for backend/web Dockerfiles
.github/SECURITY.md Adds repository security policy and reporting guidance
.github/CONTRIBUTING.md Fixes links after moving under .github/ and updates structure docs
.dockerignore Adds a consolidated root .dockerignore for root-context Docker builds

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +17
FROM python:3.14.3-slim AS builder

COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /uvx /bin/
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

Several image references are pinned only by tag (e.g., python:3.14.3-slim, ghcr.io/astral-sh/uv:0.10.9). Tags can be retargeted upstream, which reduces build reproducibility and supply-chain hardening. If the intent is fully pinned builds, consider pinning these to digests as well (and letting Dependabot update them).

Suggested change
FROM python:3.14.3-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:0.10.9 /uv /uvx /bin/
FROM python:3.14.3-slim@sha256:1111111111111111111111111111111111111111111111111111111111111111 AS builder
COPY --from=ghcr.io/astral-sh/uv:0.10.9@sha256:2222222222222222222222222222222222222222222222222222222222222222 /uv /uvx /bin/

Copilot uses AI. Check for mistakes.
# AI Company Web — CIS-hardened nginx container (non-root)
# =============================================================================

FROM nginxinc/nginx-unprivileged:1.29.5-alpine
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The nginx base image is tag-pinned but not digest-pinned. If you want fully reproducible / supply-chain-hardened builds, consider pinning nginxinc/nginx-unprivileged:1.29.5-alpine by digest too (and relying on Dependabot for updates).

Suggested change
FROM nginxinc/nginx-unprivileged:1.29.5-alpine
FROM nginxinc/nginx-unprivileged:1.29.5-alpine@sha256:REPLACE_WITH_ACTUAL_DIGEST

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +55
class CSPMiddleware:
"""ASGI middleware that applies path-aware Content-Security-Policy.

API routes get a strict policy (self-origin only). The ``/docs/``
path gets a relaxed policy that allows Scalar UI resources from
``cdn.jsdelivr.net``.
"""

def __init__(self, app: ASGIApp) -> None:
self.app = app

async def __call__(
self,
scope: Scope,
receive: Receive,
send: Send,
) -> None:
"""Inject the appropriate CSP header based on request path."""
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

New CSPMiddleware behavior isn’t covered by tests. Since middleware behavior is security-sensitive, add unit tests (similar to existing middleware tests) asserting the Content-Security-Policy header value differs for /docs vs non-doc paths.

Copilot uses AI. Check for mistakes.
return

path: str = scope.get("path", "")
is_docs = path.startswith("/docs")
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

is_docs = path.startswith("/docs") will also match unrelated paths like /docs-foo, unintentionally applying the relaxed CSP to non-doc routes. Consider using an exact match for /docs plus a /docs/ prefix check (path boundary) so only the documentation UI gets the relaxed policy.

Suggested change
is_docs = path.startswith("/docs")
is_docs = path == "/docs" or path.startswith("/docs/")

Copilot uses AI. Check for mistakes.
web/nginx.conf Outdated
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The CSP here allows 'unsafe-inline' for both scripts and styles, which substantially weakens XSS protections (any injected inline script/style will run). If possible, move the inline JS/CSS to separate files or use a nonce/hash-based policy so you can keep unsafe-inline disabled.

Suggested change
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;

Copilot uses AI. Check for mistakes.
server {
listen 8080;
server_name _;

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

Consider disabling server_tokens to avoid disclosing the nginx version in error pages and response headers (minor information disclosure hardening).

Suggested change
server_tokens off;

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +77
type=raw,value=${{ needs.version.outputs.app_version }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

This workflow publishes a mutable app_version tag (from pyproject.toml) on every push to main. If the version isn't bumped on each merge, the same tag will be overwritten with different image contents, which is confusing for consumers and makes rollbacks harder. Consider only publishing version/semver tags on v* git tag events and using commit-SHA tags for main pushes.

Suggested change
type=raw,value=${{ needs.version.outputs.app_version }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
# Publish human-readable version tags only on v* git tags
type=raw,value=${{ needs.version.outputs.app_version }},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
# Use immutable SHA tags for main branch pushes
type=sha,prefix=,enable=${{ github.ref == 'refs/heads/main' }}

Copilot uses AI. Check for mistakes.
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 @.github/workflows/docker.yml:
- Around line 73-77: The SHA-based image tag currently uses "type=sha,prefix="
which yields bare short SHAs that can collide with semver-like tags; change the
tag entry to use "type=sha,prefix=sha-" so tags become "sha-<shortsha>" and
update the identical "type=sha,prefix=" occurrence for the web image as well
(the entries containing the literal string type=sha,prefix=).

In `@DESIGN_SPEC.md`:
- Line 2742: Update the Containerization table entry that currently claims "zero
CVEs" (the row labeled **Containerization**) to remove the absolute guarantee
and instead state the continuous remediation posture; replace "zero CVEs" with
wording such as "minimal attack surface, continuously scanned in CI with rapid
remediation" and mirror this change for the second occurrence noted (the other
similar entry at the later mention). Ensure the new phrasing retains references
to Chainguard Python distroless, non-root UID 65532,
nginxinc/nginx-unprivileged, GHCR, cosign, Trivy + Grype, SBOM and SLSA
provenance while removing the fixed "zero CVEs" claim.

In `@README.md`:
- Around line 87-94: Update the README service URL lines and example commands to
indicate the ports are defaults and configurable via BACKEND_PORT and WEB_PORT
in docker/compose.yml; change the "Backend API: http://localhost:8000" and "Web
Dashboard: http://localhost:3000" text to note these are default values (e.g.,
"default BACKEND_PORT" / "default WEB_PORT"), and update the curl comment to
append a note like "default BACKEND_PORT; use your configured value if changed"
while keeping the docker compose stop command unchanged.

In `@src/ai_company/api/middleware.py`:
- Around line 60-62: The current check uses path.startswith("/docs") which
incorrectly matches prefixes like "/documents"; update the is_docs detection in
the middleware to only treat the exact docs routes as docs (for example match
"/docs" and "/docs/" or use exact equality and a normalized trailing slash)
before selecting _DOCS_CSP vs _API_CSP; modify the logic around
scope.get("path") / path and the is_docs variable so only the intended OpenAPI
docs endpoints receive _DOCS_CSP.

In `@web/nginx.conf`:
- Line 21: The Content-Security-Policy header added in the nginx config includes
'unsafe-inline' for script-src and style-src which weakens XSS protection;
update the Content-Security-Policy add_header directive (the line adding
Content-Security-Policy) to remove 'unsafe-inline' from script-src (and consider
removing it from style-src), and if inline scripts/styles are required for the
placeholder index.html replace them with nonce- or hash-based allowances
(generate and inject nonces or compute script/style hashes) so the policy can
stay strict while permitting necessary inline content.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e276b6dd-b888-4258-b613-e3092bee6de6

📥 Commits

Reviewing files that changed from the base of the PR and between 3274a86 and 4eff85b.

📒 Files selected for processing (23)
  • .dockerignore
  • .github/CONTRIBUTING.md
  • .github/SECURITY.md
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .github/workflows/dependency-review.yml
  • .github/workflows/docker.yml
  • .github/workflows/secret-scan.yml
  • .gitignore
  • CLAUDE.md
  • DESIGN_SPEC.md
  • README.md
  • config/.gitkeep
  • docker/.env.example
  • docker/backend/Dockerfile
  • docker/compose.override.yml
  • docker/compose.yml
  • docker/web/Dockerfile
  • docs/getting_started.md
  • src/ai_company/api/app.py
  • src/ai_company/api/middleware.py
  • web/index.html
  • web/nginx.conf
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax (no parentheses) for PEP 758 except syntax — ruff enforces this on Python 3.14
Include type hints on all public functions; use mypy strict mode
Use Google-style docstrings on all public classes and functions; enforce via ruff D rules
Create new objects and never mutate existing ones; for non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models with model_copy(update=...) for runtime state that evolves (e.g. agent execution state, task progress); never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls); prefer structured concurrency over bare create_task
Maintain 88-character line length; enforce via ruff
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)

Files:

  • src/ai_company/api/middleware.py
  • src/ai_company/api/app.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Every module with business logic must include: from ai_company.observability import get_logger then logger = get_logger(name); never use import logging / logging.getLogger() / print() in application code; always use variable name logger (not _logger, not log)
Always use event name constants from domain-specific modules under ai_company.observability.events (e.g. PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget); import directly: from ai_company.observability.events. import EVENT_CONSTANT
Use structured logging via logger.info(EVENT, key=value) syntax; never use logger.info('msg %s', val) formatting
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO level
DEBUG level logging is for object creation, internal flow, and entry/exit of key functions; pure data models, enums, and re-exports do not need logging

Files:

  • src/ai_company/api/middleware.py
  • src/ai_company/api/app.py
{src,tests}/**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases. Vendor names may only appear in: (1) DESIGN_SPEC.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths/module names. Tests must use test-provider, test-small-001, etc.

Files:

  • src/ai_company/api/middleware.py
  • src/ai_company/api/app.py
🧠 Learnings (3)
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: Use branch naming format <type>/<slug> derived from main; pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks

Applied to files:

  • CLAUDE.md
  • .github/CONTRIBUTING.md
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: Always read DESIGN_SPEC.md before implementing any feature or planning any issue; use the design spec as the starting point for architecture, data models, and behavior

Applied to files:

  • docs/getting_started.md
  • DESIGN_SPEC.md
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: When a spec section is referenced, read that section verbatim before coding; when approved deviations occur, update DESIGN_SPEC.md to reflect the new reality

Applied to files:

  • docs/getting_started.md
🧬 Code graph analysis (1)
src/ai_company/api/app.py (1)
src/ai_company/api/middleware.py (2)
  • CSPMiddleware (38-76)
  • RequestLoggingMiddleware (79-139)
🪛 Checkov (3.2.334)
docker/web/Dockerfile

[low] 1-22: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)
docker/backend/Dockerfile

[warning] 46-46: Last USER should not be root

(DL3002)

🪛 LanguageTool
CLAUDE.md

[uncategorized] ~59-~59: The official name of this software platform is spelled with a capital “H”.
Context: ...iles, compose, .env.example - CI: .github/workflows/docker.yml — build → scan → ...

(GITHUB)

docs/getting_started.md

[uncategorized] ~153-~153: The official name of this software platform is spelled with a capital “H”.
Context: ...ette. ## Next Steps - CONTRIBUTING.md — branch, commit, and ...

(GITHUB)

README.md

[typographical] ~100-~100: To join two clauses or introduce examples, consider using an em dash.
Context: ...etting Started](docs/getting_started.md) - Setup and installation guide - [Contribu...

(DASH_RULE)


[uncategorized] ~101-~101: The official name of this software platform is spelled with a capital “H”.
Context: ...p and installation guide - Contributing - Branch, commit, and ...

(GITHUB)


[typographical] ~101-~101: To join two clauses or introduce examples, consider using an em dash.
Context: ... Contributing - Branch, commit, and PR workflow - [CLAUD...

(DASH_RULE)

🔇 Additional comments (24)
.gitignore (1)

49-52: LGTM!

The Web UI ignore patterns are correctly structured to exclude Node.js dependencies and build artifacts from version control.

docker/web/Dockerfile (1)

1-22: Well-structured security-hardened web container.

The Dockerfile correctly leverages nginxinc/nginx-unprivileged, which already runs as a non-root user (UID 101), addressing CIS container hardening requirements. The static analysis hint about container user (CKV_DOCKER_3) is a false positive for this base image.

The implementation includes proper OCI labels, version-pinned base image for reproducibility, and a functional healthcheck.

.github/dependabot.yml (1)

38-64: LGTM!

The Dependabot Docker configurations are well-structured, enabling automated base image digest updates for both backend and web containers. The daily schedule and configuration align with existing dependency update patterns in this file.

.github/workflows/dependency-review.yml (1)

17-22: LGTM!

Both actions are correctly pinned to full-length commit SHAs with version comments, consistent with the PR's supply chain security improvements.

.github/workflows/ci.yml (1)

23-23: LGTM!

All GitHub Actions are consistently pinned to full-length commit SHAs across lint, type-check, and test jobs. The uniform checkout SHA ensures consistent behavior, and upload-artifact is similarly pinned for supply chain security.

Also applies to: 40-40, 57-57, 86-86

.dockerignore (1)

1-64: LGTM!

Well-documented consolidated .dockerignore that properly excludes development artifacts, sensitive files, and unnecessary content from Docker build contexts. The inline comments clearly explain what each service requires, aiding maintainability.

web/nginx.conf (1)

1-49: Well-configured nginx reverse proxy with good security posture.

The configuration correctly implements:

  • Non-privileged port (8080) compatible with nginx-unprivileged
  • Comprehensive security headers with helpful inheritance warning
  • Proper SPA routing with static file fallback
  • API proxying with standard forwarded headers
  • WebSocket support with appropriate upgrade headers and extended timeout

The documented note about add_header inheritance (lines 14-16) is excellent for maintainability.

.github/workflows/secret-scan.yml (1)

22-22: Good: SHA-pinned checkout action for supply chain security.

Pinning to exact commit SHA protects against tag mutation attacks. The inline version comment (# v6) aids maintainability.

docker/backend/Dockerfile (1)

1-104: LGTM! Well-structured multi-stage Dockerfile with proper CIS hardening.

The three-stage build is correctly implemented:

  • Stage 1 (builder): Compiles dependencies with layer caching
  • Stage 2 (setup): Fixes symlinks and sets ownership (ephemeral, needs shell)
  • Stage 3 (runtime): Distroless, runs as non-root UID 65532

The Hadolint DL3002 warning about "Last USER should not be root" at line 46 is a false positive — it flags the ephemeral setup stage, but the final runtime stage correctly sets USER 65532 at line 92. The production image runs non-root as intended.

web/index.html (1)

68-92: LGTM! Clean health-check implementation.

The fetch logic correctly:

  • Checks r.ok before parsing JSON (line 76)
  • Gracefully handles failures in the catch block
  • Updates UI state appropriately for both connected and disconnected states
src/ai_company/api/middleware.py (1)

38-76: LGTM! Well-implemented path-aware CSP middleware.

The middleware correctly:

  • Delegates non-HTTP scopes without modification
  • Injects CSP headers by wrapping the send callable
  • Uses separate policies for API vs documentation routes
src/ai_company/api/app.py (2)

28-28: LGTM! Correct import of CSPMiddleware.


365-372: LGTM! Appropriate middleware ordering.

CSPMiddleware is correctly positioned as the first (outermost) middleware, ensuring CSP headers are applied regardless of rate limiting or request logging outcomes. Moving CSP from static response_headers to middleware enables the path-aware behavior needed for /docs/.

.github/workflows/docker.yml (1)

79-134: LGTM! Correct scan-before-push pattern with graceful signing fallback.

The workflow correctly:

  • Builds locally first without pushing
  • Runs both Trivy and Grype scans
  • Only pushes after scans pass
  • Handles missing digest gracefully in the signing step
CLAUDE.md (2)

42-63: LGTM! Clear and comprehensive Docker documentation.

The section provides:

  • Correct compose commands with -f docker/compose.yml
  • Health check verification commands for both direct and proxied access
  • Accurate descriptions of the hardened container setup

The LanguageTool hint about "GitHub" capitalization is a false positive — .github/ is a directory path convention, not a reference to the company name.


162-164: LGTM! CI section correctly updated.

The additions accurately describe the new Docker workflow capabilities and Dependabot configuration.

docker/compose.override.yml (1)

1-12: LGTM! Well-documented development override.

The file correctly:

  • Documents that explicit -f is required (compose v2 doesn't auto-merge .override.yml when using -f)
  • Sets debug logging for development
  • Keeps the Docker socket mount commented with an appropriate security warning
docker/.env.example (1)

1-31: LGTM! Well-structured environment template.

The file provides:

  • Clear section organization with descriptive comments
  • Sensible defaults for non-sensitive values
  • Correct container paths (/data/) matching the Dockerfile
  • Sensitive values (LLM_API_KEY) left empty as expected
  • Optional configurations appropriately commented
docs/getting_started.md (1)

103-124: Docs layout and navigation stay aligned with the new repo structure.

The new docker/ / web/ entries and the moved CONTRIBUTING link are consistent with the rest of the documentation changes.

Also applies to: 153-153

.github/CONTRIBUTING.md (1)

11-11: Relative link fixes and structure updates look correct.

These paths now resolve from .github/CONTRIBUTING.md, and the added docker/ / web/ entries match the documented repo layout.

Also applies to: 110-115, 126-145

README.md (1)

57-65: The Docker-first onboarding split is clear.

This keeps local Python setup intact while adding container prerequisites and the moved contributing guide.

Also applies to: 101-101

docker/compose.yml (2)

16-24: The container hardening profile looks solid.

no-new-privileges, cap_drop: ALL, read-only root filesystems, and scoped tmpfs mounts are strong defaults for both services.

Also applies to: 35-44


32-34: Backend readiness gate is properly backed by HEALTHCHECK.

The service_healthy condition in compose.yml is correctly supported: docker/backend/Dockerfile defines a HEALTHCHECK at line 97 that checks http://localhost:8000/api/v1/health. No action needed.

DESIGN_SPEC.md (1)

3209-3230: Spec structure updates are in sync with the repo layout.

Adding docker/, web/, and the GitHub workflow/action entries keeps §15.3 aligned with the artifacts introduced by this PR.

…iewers

Security: fail cosign signing on empty digest, fix CSP path matching
(/docs prefix too broad), remove unsafe-inline from nginx CSP by
extracting inline JS/CSS to external files, add server_tokens off,
digest-pin all Docker base images (python, uv, nginx).

CI: limit version/semver tags to v* git tags only, use sha- prefix
for SHA tags, fix file handle leak in version extraction, document
scan-vs-push artifact difference, document Codecov best-effort rationale.

Code quality: fix duplicate Callable import, improve _build_middleware
return type (list[Middleware] instead of list[object]), add Final[str]
to CSP constants, add 7 CSPMiddleware unit tests.

Docs: add docker/sandbox/ and .dockerignore to DESIGN_SPEC project
structure, replace "zero CVEs" with "continuously scanned" language,
note configurable ports in README, fix mypy command in getting_started,
clarify CIS reference numbers in compose.yml, fix Dockerfile comments
(ENTRYPOINT, touch README, CIS-hardened claim), improve healthcheck
to validate response body status.
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 @.github/workflows/docker.yml:
- Around line 188-201: The Trivy and Grype steps reference a non-existent tag
${{ needs.version.outputs.app_version }}; update both the image-ref and image
fields to use the same build/job output the backend scans use (the actual image
tag output from the image-build job, e.g. replace ${{
needs.version.outputs.app_version }} with the build job's image tag output such
as ${{ needs.build.outputs.image_tag }} or whatever output name the backend scan
uses) so the scans point at the real image produced by the workflow.
- Around line 94-107: The Trivy and Grype steps currently reference the tag
variable app_version which may not exist on main branch pushes; update both the
"Trivy scan" and "Grype scan" steps to reference the image produced by the
build/metadata steps instead of app_version — e.g. use the
docker/metadata-action output (steps.meta.outputs.tags) to pick the first tag
(split the CSV/JSON and use the first element) or reference the built image by
digest/output from the build step (e.g. steps.build-image.outputs.digest or the
build step’s image output) and substitute that expression for
ghcr.io/aureliolo/ai-company-backend:${{ needs.version.outputs.app_version }} in
both image-ref and image fields.

In `@docker/web/Dockerfile`:
- Around line 21-22: The current HEALTHCHECK only probes "/", which verifies
nginx but not the proxied backend; update the Dockerfile HEALTHCHECK to query
the proxied backend health endpoint ("/api/v1/health") and fail unless it
returns a 200/healthy response so the container health reflects the backend
contract used by web/app.js and docker/backend/Dockerfile; modify the
HEALTHCHECK command (the existing HEALTHCHECK line) to perform an HTTP GET
against "http://localhost:8080/api/v1/health" and treat non-2xx responses or
timeouts as unhealthy.

In `@tests/unit/api/test_middleware.py`:
- Around line 62-103: The three tests
test_unrelated_docs_prefix_gets_strict_csp,
test_docsearch_prefix_gets_strict_csp, and test_docs_subpath_gets_relaxed_csp
all repeat the same setup/assertion; refactor them to a single parametrized test
using pytest.mark.parametrize that iterates over (path, expected_csp) pairs,
call CSPMiddleware(_fake_app) once per case, use the same capture_send and await
middleware(_make_scope(path), None, capture_send), then assert
headers[b"content-security-policy"] == expected_csp.encode(); reference
CSPMiddleware, _make_scope, _API_CSP and _DOCS_CSP when building the parameter
list.

In `@web/app.js`:
- Around line 11-18: The then-handler currently sets el.className = "status
status-connected" for any 200 response; change it to honor the returned health
status by checking the payload (use data.data && data.data.status or the local
variable s) and only set status-connected when s === "healthy" (otherwise set
status-disconnected). Also update text.textContent to include the returned
status (e.g., show the version and the actual s or "unhealthy") so the UI
matches the backend's health logic referenced by the Dockerfile; adjust the
.catch behavior to remain as the fallback for network errors.

In `@web/index.html`:
- Around line 13-16: The status container with id="status" (and child
id="status-text", class "status-checking") must be exposed as a live region so
assistive tech is notified when backend state changes; update the element to
include appropriate ARIA live-region semantics (e.g., add role="status" or
aria-live="polite" and aria-atomic="true") on the div#status so screen readers
announce changes to `#status-text` when you update it.

In `@web/nginx.conf`:
- Around line 29-36: Add explicit proxy timeouts to the API proxy block
(location /api/) similar to the WebSocket block: set proxy_connect_timeout,
proxy_send_timeout and proxy_read_timeout (e.g., 30s/30s/60s or adjust to
desired SLAs) and optionally proxy_buffering off if streaming is expected;
update the location /api/ configuration to include these directives so that the
API proxy uses explicit timeouts instead of nginx defaults.

In `@web/style.css`:
- Around line 21-23: Add the standard background-clip property alongside the
vendor-prefixed one so browsers that support the unprefixed spec can apply the
text clipping; update the same rule that currently has "-webkit-background-clip:
text" (in the selector containing the linear-gradient background and
-webkit-text-fill-color) to also include "background-clip: text" immediately
before or after the vendor-prefixed declaration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 639d7f1e-ed84-4d34-93d6-0c4a67ef15c5

📥 Commits

Reviewing files that changed from the base of the PR and between 4eff85b and 53466cc.

📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • .github/workflows/docker.yml
  • DESIGN_SPEC.md
  • README.md
  • docker/backend/Dockerfile
  • docker/compose.yml
  • docker/web/Dockerfile
  • docs/getting_started.md
  • src/ai_company/api/app.py
  • src/ai_company/api/middleware.py
  • tests/unit/api/test_middleware.py
  • web/app.js
  • web/index.html
  • web/nginx.conf
  • web/style.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax (no parentheses) for PEP 758 except syntax — ruff enforces this on Python 3.14
Include type hints on all public functions; use mypy strict mode
Use Google-style docstrings on all public classes and functions; enforce via ruff D rules
Create new objects and never mutate existing ones; for non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement
For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models with model_copy(update=...) for runtime state that evolves (e.g. agent execution state, task progress); never mix static config fields with mutable runtime fields in one model
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls); prefer structured concurrency over bare create_task
Maintain 88-character line length; enforce via ruff
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)

Files:

  • tests/unit/api/test_middleware.py
  • src/ai_company/api/app.py
  • src/ai_company/api/middleware.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest markers @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow for test categorization
Maintain minimum 80% test coverage; enforce in CI
Use asyncio_mode = 'auto' for async tests — no manual @pytest.mark.asyncio needed
Set 30-second timeout per test
Prefer @pytest.mark.parametrize for testing similar cases in test files

Files:

  • tests/unit/api/test_middleware.py
{src,tests}/**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small as aliases. Vendor names may only appear in: (1) DESIGN_SPEC.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths/module names. Tests must use test-provider, test-small-001, etc.

Files:

  • tests/unit/api/test_middleware.py
  • src/ai_company/api/app.py
  • src/ai_company/api/middleware.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Every module with business logic must include: from ai_company.observability import get_logger then logger = get_logger(name); never use import logging / logging.getLogger() / print() in application code; always use variable name logger (not _logger, not log)
Always use event name constants from domain-specific modules under ai_company.observability.events (e.g. PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget); import directly: from ai_company.observability.events. import EVENT_CONSTANT
Use structured logging via logger.info(EVENT, key=value) syntax; never use logger.info('msg %s', val) formatting
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO level
DEBUG level logging is for object creation, internal flow, and entry/exit of key functions; pure data models, enums, and re-exports do not need logging

Files:

  • src/ai_company/api/app.py
  • src/ai_company/api/middleware.py
🧠 Learnings (4)
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: Always read DESIGN_SPEC.md before implementing any feature or planning any issue; use the design spec as the starting point for architecture, data models, and behavior

Applied to files:

  • docs/getting_started.md
  • DESIGN_SPEC.md
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: When a spec section is referenced, read that section verbatim before coding; when approved deviations occur, update DESIGN_SPEC.md to reflect the new reality

Applied to files:

  • docs/getting_started.md
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: Applies to tests/**/*.py : Maintain minimum 80% test coverage; enforce in CI

Applied to files:

  • .github/workflows/ci.yml
📚 Learning: 2026-03-10T13:31:23.930Z
Learnt from: CR
Repo: Aureliolo/ai-company PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T13:31:23.930Z
Learning: Use branch naming format <type>/<slug> derived from main; pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks

Applied to files:

  • .github/workflows/docker.yml
🧬 Code graph analysis (2)
tests/unit/api/test_middleware.py (1)
src/ai_company/api/middleware.py (1)
  • CSPMiddleware (39-78)
src/ai_company/api/app.py (1)
src/ai_company/api/middleware.py (2)
  • CSPMiddleware (39-78)
  • RequestLoggingMiddleware (81-141)
🪛 Checkov (3.2.334)
docker/web/Dockerfile

[low] 1-22: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)
docker/backend/Dockerfile

[warning] 49-49: Last USER should not be root

(DL3002)

🪛 LanguageTool
README.md

[typographical] ~100-~100: To join two clauses or introduce examples, consider using an em dash.
Context: ...etting Started](docs/getting_started.md) - Setup and installation guide - [Contribu...

(DASH_RULE)


[uncategorized] ~101-~101: The official name of this software platform is spelled with a capital “H”.
Context: ...p and installation guide - Contributing - Branch, commit, and ...

(GITHUB)


[typographical] ~101-~101: To join two clauses or introduce examples, consider using an em dash.
Context: ... Contributing - Branch, commit, and PR workflow - [CLAUD...

(DASH_RULE)

docs/getting_started.md

[uncategorized] ~153-~153: The official name of this software platform is spelled with a capital “H”.
Context: ...ette. ## Next Steps - CONTRIBUTING.md — branch, commit, and ...

(GITHUB)

🔇 Additional comments (15)
docs/getting_started.md (3)

123-124: LGTM! Docker and web directories correctly documented.

These additions accurately reflect the containerization deliverables: the docker/ directory with Dockerfiles and Compose configuration, and the web/ directory with the nginx-based UI scaffold. Both are explicitly mentioned in the PR objectives.


153-153: LGTM! Path correctly updated after .github/ reorganization.

The link update to ../.github/CONTRIBUTING.md is explicitly mentioned in the PR objectives as part of fixing documentation links after the .github/ directory restructuring.


103-103: The documentation accurately reflects the actual codebase. The hr/, observability/, and persistence/ directories exist in the project, Litestar is the active framework (with no FastAPI dependencies), and the CLI structure is in place. These updates are not scope creep or documentation drift—they are correct descriptions of the real project structure.

.github/workflows/ci.yml (4)

76-77: Clear documentation of Codecov best-effort behavior.

The comments accurately explain why fail_ci_if_error: false is set and that coverage enforcement is handled by --cov-fail-under. This prevents CI failures due to Codecov outages while still enforcing coverage thresholds locally via pytest.

Also applies to: 78-84


68-74: Coverage enforcement satisfies the 80% threshold requirement.

The --cov-fail-under=80 flag ensures the CI fails if coverage drops below 80%, which aligns with project requirements. Based on learnings: "Maintain minimum 80% test coverage; enforce in CI".


23-23: SHA pinning for actions/checkout is a good supply chain security practice. Pinning GitHub Actions to full-length commit SHAs prevents supply chain attacks via compromised tags. The SHA de0fac2e4500dabe0009e67214ff5f5447ce83dd correctly corresponds to the v6 release. The consistent pinning across all jobs is correct.


88-88: The actions/upload-artifact SHA is correct for v7. The pinned SHA bbbca2ddaa5d8feaa63e36b76fdaad77386f024f matches the v7 release.

README.md (2)

57-58: LGTM!

Tech stack additions clearly document the Docker and nginx components with their security characteristics (CIS-hardened, non-root, unprivileged).


79-96: LGTM!

Docker Compose section is well-documented with clear instructions. The port configurability note and default port clarification address previous review feedback.

web/nginx.conf (1)

14-22: LGTM!

Security headers are well-configured: CSP without unsafe-inline, server_tokens off, and the helpful comment about add_header inheritance behavior. The previous review concern about unsafe-inline has been addressed.

docker/compose.yml (3)

1-24: LGTM!

Backend service configuration is solid: CIS-hardened security options, configurable port mapping, proper volume mount for persistent data, and tmpfs for /tmp to support read-only rootfs.


26-47: LGTM!

Web service configuration properly mirrors backend hardening, with appropriate tmpfs mounts for nginx's runtime directories (/var/cache/nginx, /var/run).


32-34: Backend and web services have HEALTHCHECK instructions defined.

The web service's depends_on with condition: service_healthy correctly requires the backend to have a healthcheck. Both the backend Dockerfile (line 100) and web Dockerfile (line 21) define the necessary HEALTHCHECK instructions, so the dependency configuration is properly supported.

.github/workflows/docker.yml (2)

29-44: LGTM!

Version extraction is well-implemented: uses tomllib with proper file handling (with statement), clear error messages to stderr, and exits with non-zero code on failure.


130-138: LGTM!

Good defensive check for empty digest before signing. This prevents silent failures where cosign would sign an invalid reference.

Comment on lines +21 to +22
HEALTHCHECK --interval=10s --timeout=3s --retries=3 --start-period=5s \
CMD ["wget", "--spider", "--quiet", "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

Validate the proxied backend health, not just the static page.

This only proves nginx can serve /. A broken /api reverse proxy or unhealthy backend still leaves the container healthy, even though web/app.js depends on /api/v1/health and docker/backend/Dockerfile (Lines 100-101) already treats status == healthy as the health contract.

🩺 Suggested healthcheck
-HEALTHCHECK --interval=10s --timeout=3s --retries=3 --start-period=5s \
-    CMD ["wget", "--spider", "--quiet", "http://localhost:8080/"]
+HEALTHCHECK --interval=10s --timeout=3s --retries=3 --start-period=5s \
+    CMD-SHELL wget -qO- http://localhost:8080/api/v1/health | grep -Eq '"status"[[:space:]]*:[[:space:]]*"healthy"'
🧰 Tools
🪛 Checkov (3.2.334)

[low] 1-22: Ensure that a user for the container has been created

(CKV_DOCKER_3)

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

In `@docker/web/Dockerfile` around lines 21 - 22, The current HEALTHCHECK only
probes "/", which verifies nginx but not the proxied backend; update the
Dockerfile HEALTHCHECK to query the proxied backend health endpoint
("/api/v1/health") and fail unless it returns a 200/healthy response so the
container health reflects the backend contract used by web/app.js and
docker/backend/Dockerfile; modify the HEALTHCHECK command (the existing
HEALTHCHECK line) to perform an HTTP GET against
"http://localhost:8080/api/v1/health" and treat non-2xx responses or timeouts as
unhealthy.

…y timeouts

- Use SHA tag (always exists) for Trivy/Grype scan image-ref instead of
  app_version which is only present on v* tag pushes (#29, #30)
- Check data.status === "healthy" before showing green badge in web UI (#31)
- Add aria-live="polite" to status div for screen reader accessibility (#32)
- Parametrize direct-ASGI CSP boundary tests (#33)
- Add explicit proxy_connect/send/read_timeout to nginx API location (#34)
- Add standard background-clip: text alongside -webkit- prefix (#35)
Copilot AI review requested due to automatic review settings March 10, 2026 18:48
@Aureliolo Aureliolo merged commit 435bdfe into main Mar 10, 2026
8 checks passed
@Aureliolo Aureliolo deleted the feat/container-packaging-ci branch March 10, 2026 18:48
Comment on lines +30 to +39
location /api/ {
proxy_pass http://backend:8000;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
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;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate CSP headers on proxied responses

The add_header Content-Security-Policy directive in the server block (line 22) applies to all location blocks — including /api/ and /ws — because neither of those location blocks defines its own add_header. At the same time, the backend's CSPMiddleware injects its own Content-Security-Policy header for every HTTP response.

This means every proxied API response includes two Content-Security-Policy headers: nginx's strict server-level one (default-src 'self'; script-src 'self'; ...) and the backend middleware's (default-src 'self'; script-src 'self'). Per the CSP spec, when multiple headers are present, the browser enforces the intersection — effectively the more restrictive policy. While both policies agree for API (JSON) responses, this is a subtle misconfiguration that could break future endpoints needing different CSP treatment and creates confusing redundancy.

Add proxy_hide_header Content-Security-Policy; to both proxied location blocks to suppress the upstream header so only nginx's server-level CSP is returned:

Suggested change
location /api/ {
proxy_pass http://backend:8000;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
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;
}
# API proxy to backend service
location /api/ {
proxy_pass http://backend:8000;
proxy_hide_header Content-Security-Policy;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
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;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/nginx.conf
Line: 30-39

Comment:
**Duplicate CSP headers on proxied responses**

The `add_header Content-Security-Policy` directive in the `server` block (line 22) applies to **all** location blocks — including `/api/` and `/ws` — because neither of those location blocks defines its own `add_header`. At the same time, the backend's `CSPMiddleware` injects its own `Content-Security-Policy` header for every HTTP response.

This means every proxied API response includes **two** `Content-Security-Policy` headers: nginx's strict server-level one (`default-src 'self'; script-src 'self'; ...`) and the backend middleware's (`default-src 'self'; script-src 'self'`). Per the CSP spec, when multiple headers are present, the browser enforces the intersection — effectively the more restrictive policy. While both policies agree for API (JSON) responses, this is a subtle misconfiguration that could break future endpoints needing different CSP treatment and creates confusing redundancy.

Add `proxy_hide_header Content-Security-Policy;` to both proxied location blocks to suppress the upstream header so only nginx's server-level CSP is returned:

```suggestion
    # API proxy to backend service
    location /api/ {
        proxy_pass http://backend:8000;
        proxy_hide_header Content-Security-Policy;
        proxy_connect_timeout 5s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
        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;
    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +42 to +52
location /ws {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400s;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WebSocket proxy missing proxy_buffering off

For WebSocket connections, nginx should disable response buffering to ensure frames are forwarded immediately without being held in nginx's buffer pool. Without proxy_buffering off;, nginx may buffer partial WebSocket responses before flushing them to the client, adding latency and potentially stalling the stream.

Suggested change
location /ws {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400s;
}
# WebSocket proxy
location /ws {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400s;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/nginx.conf
Line: 42-52

Comment:
**WebSocket proxy missing `proxy_buffering off`**

For WebSocket connections, nginx should disable response buffering to ensure frames are forwarded immediately without being held in nginx's buffer pool. Without `proxy_buffering off;`, nginx may buffer partial WebSocket responses before flushing them to the client, adding latency and potentially stalling the stream.

```suggestion
    # WebSocket proxy
    location /ws {
        proxy_pass http://backend:8000;
        proxy_http_version 1.1;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
        proxy_read_timeout 86400s;
    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +40 to +44
tmpfs:
- /tmp:noexec,nosuid,size=16m
- /var/cache/nginx:size=32m
- /var/run:size=1m
restart: unless-stopped
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistent noexec,nosuid flags on web tmpfs mounts

The web service's /tmp tmpfs correctly carries noexec,nosuid, but /var/cache/nginx and /var/run do not. For consistency with the stated CIS hardening posture, these paths should carry the same mount flags — nginx doesn't need to execute files from its cache, and /var/run (used only for PID files) certainly doesn't:

Suggested change
tmpfs:
- /tmp:noexec,nosuid,size=16m
- /var/cache/nginx:size=32m
- /var/run:size=1m
restart: unless-stopped
tmpfs:
- /tmp:noexec,nosuid,size=16m
- /var/cache/nginx:noexec,nosuid,size=32m
- /var/run:noexec,nosuid,size=1m
Prompt To Fix With AI
This is a comment left during a code review.
Path: docker/compose.yml
Line: 40-44

Comment:
**Inconsistent `noexec,nosuid` flags on `web` tmpfs mounts**

The `web` service's `/tmp` tmpfs correctly carries `noexec,nosuid`, but `/var/cache/nginx` and `/var/run` do not. For consistency with the stated CIS hardening posture, these paths should carry the same mount flags — nginx doesn't need to execute files from its cache, and `/var/run` (used only for PID files) certainly doesn't:

```suggestion
    tmpfs:
      - /tmp:noexec,nosuid,size=16m
      - /var/cache/nginx:noexec,nosuid,size=32m
      - /var/run:noexec,nosuid,size=1m
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 26 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +22 to +25
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;

# SPA routing — try static files, fall back to index.html
location / {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The server-level Content-Security-Policy header will also be added to proxied /api/ and /ws responses, while the backend now injects its own CSP header via CSPMiddleware. This results in duplicate CSP headers (and potentially conflicting policies) when accessing the API through the web container, which can lead to unexpectedly restrictive effective CSP behavior in user agents and makes headers harder to reason about. Consider scoping the CSP add_header to location / (static content only) and/or hiding/passing through upstream CSP on /api/ + /ws (e.g., proxy_hide_header Content-Security-Policy) so there is a single authoritative CSP per response.

Suggested change
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;
# SPA routing — try static files, fall back to index.html
location / {
# SPA routing — try static files, fall back to index.html
location / {
# Security headers for static content, including CSP.
# Note: add_header in this location overrides server-level add_header,
# so we repeat all headers here.
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; font-src 'self'" always;

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +79
- name: Extract metadata
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: ghcr.io/aureliolo/ai-company-backend
tags: |
type=raw,value=${{ needs.version.outputs.app_version }},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=sha,prefix=sha-

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The workflow tags images using both the version extracted from pyproject.toml and semver tags derived from the git ref. If a v* tag is pushed while pyproject.toml still contains a different version, the image will be published under a misleading raw version tag as well as the semver tag from the ref. To prevent mismatched releases, either validate that the git tag matches the extracted version (and fail otherwise) or choose a single source of truth for version tagging (pyproject-only or ref-only).

Copilot uses AI. Check for mistakes.
Aureliolo added a commit that referenced this pull request Mar 10, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.1](ai-company-v0.1.0...ai-company-v0.1.1)
(2026-03-10)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Aureliolo added a commit that referenced this pull request Mar 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.0](v0.0.0...v0.1.0)
(2026-03-11)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add mandatory JWT + API key authentication
([#256](#256))
([c279cfe](c279cfe))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable output scan response policies
([#263](#263))
([b9907e8](b9907e8))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement AuditRepository for security audit log persistence
([#279](#279))
([94bc29f](94bc29f))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* resolve circular imports, bump litellm, fix release tag format
([#286](#286))
([a6659b5](a6659b5))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* bump anchore/scan-action from 6.5.1 to 7.3.2
([#271](#271))
([80a1c15](80a1c15))
* bump docker/build-push-action from 6.19.2 to 7.0.0
([#273](#273))
([dd0219e](dd0219e))
* bump docker/login-action from 3.7.0 to 4.0.0
([#272](#272))
([33d6238](33d6238))
* bump docker/metadata-action from 5.10.0 to 6.0.0
([#270](#270))
([baee04e](baee04e))
* bump docker/setup-buildx-action from 3.12.0 to 4.0.0
([#274](#274))
([5fc06f7](5fc06f7))
* bump sigstore/cosign-installer from 3.9.1 to 4.1.0
([#275](#275))
([29dd16c](29dd16c))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* **main:** release ai-company 0.1.1
([#282](#282))
([2f4703d](2f4703d))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Signed-off-by: Aurelio <19254254+Aureliolo@users.noreply.github.com>
This was referenced Mar 15, 2026
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.

feat: add container packaging, Docker Compose, and CI pipeline

2 participants