Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .grype.yaml → .github/.grype.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Grype configuration — CVE ignore list
# Kept in sync with .trivyignore.yaml.
# Kept in sync with .github/.trivyignore.yaml.

ignore:
- vulnerability: CVE-2026-22184
Expand Down
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions .github/release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"release-type": "python",
"package-name": "synthorg",
"include-component-in-tag": false,
"changelog-path": ".github/CHANGELOG.md",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"changelog-sections": [
Expand Down
63 changes: 57 additions & 6 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
contents: read
packages: write
id-token: write
outputs:
digest: ${{ steps.push.outputs.digest }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
Expand Down Expand Up @@ -103,7 +105,7 @@ jobs:
format: table
exit-code: "1"
severity: CRITICAL
trivyignores: .trivyignore.yaml
trivyignores: .github/.trivyignore.yaml

- name: Trivy scan (high — warn only)
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
Expand All @@ -113,15 +115,15 @@ jobs:
format: table
exit-code: "0"
severity: HIGH
trivyignores: .trivyignore.yaml
trivyignores: .github/.trivyignore.yaml

- name: Grype scan
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
with:
image: ${{ steps.scan-ref.outputs.ref }}
fail-build: true
severity-cutoff: critical
config: .grype.yaml
config: .github/.grype.yaml

# Push only after scans pass — prevents publishing vulnerable images.
# NOTE: This is a separate build invocation from the scan step. GHA cache
Expand Down Expand Up @@ -162,6 +164,8 @@ jobs:
contents: read
packages: write
id-token: write
outputs:
digest: ${{ steps.push.outputs.digest }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
Expand Down Expand Up @@ -214,7 +218,7 @@ jobs:
format: table
exit-code: "1"
severity: CRITICAL
trivyignores: .trivyignore.yaml
trivyignores: .github/.trivyignore.yaml

- name: Trivy scan (high — warn only)
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
Expand All @@ -224,15 +228,15 @@ jobs:
format: table
exit-code: "0"
severity: HIGH
trivyignores: .trivyignore.yaml
trivyignores: .github/.trivyignore.yaml

- name: Grype scan
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
with:
image: ${{ steps.scan-ref.outputs.ref }}
fail-build: true
severity-cutoff: critical
config: .grype.yaml
config: .github/.grype.yaml

# Push only after scans pass — prevents publishing vulnerable images.
# NOTE: Separate build invocation; GHA cache ensures deterministic layers,
Expand Down Expand Up @@ -263,3 +267,50 @@ jobs:
exit 1
fi
cosign sign --yes ghcr.io/aureliolo/synthorg-web@${DIGEST}

# Append container image references to the GitHub Release (version tags only)
update-release:
name: Update Release Notes
if: startsWith(github.ref, 'refs/tags/v')
needs: [version, build-backend, build-web]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Append container images to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
VERSION: ${{ needs.version.outputs.app_version }}
BACKEND_DIGEST: ${{ needs.build-backend.outputs.digest }}
WEB_DIGEST: ${{ needs.build-web.outputs.digest }}
run: |
EXISTING=$(gh release view "$TAG" --json body -q .body)

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

gh release view "$TAG" will fail the job if the GitHub Release for that tag doesn’t exist yet (race with release creation, or manual tag pushes). To avoid breaking tag builds, add a small retry/backoff or a guard that creates the release (or skips with a warning) when gh release view returns non-zero.

Suggested change
EXISTING=$(gh release view "$TAG" --json body -q .body)
EXISTING=$(gh release view "$TAG" --json body -q .body 2>/dev/null || true)
if [ -z "$EXISTING" ]; then
echo "Release $TAG not found or has no body; creating an empty release before updating notes."
gh release create "$TAG" --title "$TAG" --notes ""
EXISTING=""
fi

Copilot uses AI. Check for mistakes.
IMAGES=$(cat <<'BLOCK'

## Container Images

| Image | Pull |
|-------|------|
| Backend | `docker pull ghcr.io/aureliolo/synthorg-backend:VERSION_PH` |
| Web | `docker pull ghcr.io/aureliolo/synthorg-web:VERSION_PH` |

**Digests** (for pinning):
- Backend: `ghcr.io/aureliolo/synthorg-backend@BACKEND_DIGEST_PH`
- Web: `ghcr.io/aureliolo/synthorg-web@WEB_DIGEST_PH`

All images are signed with [cosign](https://github.com/sigstore/cosign). Verify with:
```bash
cosign verify ghcr.io/aureliolo/synthorg-backend:VERSION_PH \
--certificate-identity-regexp='github\.com/Aureliolo/synthorg' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```
Comment thread
greptile-apps[bot] marked this conversation as resolved.
BLOCK
)
# Dedent (remove leading 10-space YAML indentation) and substitute placeholders
IMAGES=$(echo "$IMAGES" | sed 's/^ //')
IMAGES=${IMAGES//VERSION_PH/$VERSION}
IMAGES=${IMAGES//BACKEND_DIGEST_PH/$BACKEND_DIGEST}
IMAGES=${IMAGES//WEB_DIGEST_PH/$WEB_DIGEST}
gh release edit "$TAG" --notes "${EXISTING}${IMAGES}"
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

The release-note update appends the “Container Images” section unconditionally (--notes "${EXISTING}${IMAGES}"). If the workflow is re-run for the same tag, this will duplicate the section in the GitHub Release body. Consider making this idempotent by detecting/replacing an existing “## Container Images” block instead of always appending.

Suggested change
gh release edit "$TAG" --notes "${EXISTING}${IMAGES}"
# Replace existing "## Container Images" section if present, otherwise append it
NEW_BODY=$(awk -v new="$IMAGES" '
BEGIN {
printed = 0;
in_block = 0;
}
/^## Container Images[[:space:]]*$/ {
if (!printed) {
print new;
printed = 1;
}
in_block = 1;
next;
}
in_block && /^## / {
in_block = 0;
}
!in_block {
print;
}
END {
if (!printed) {
printf "%s", new;
}
}
' <<< "$EXISTING")
gh release edit "$TAG" --notes "$NEW_BODY"

Copilot uses AI. Check for mistakes.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
86 changes: 86 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: GitHub Pages

on:
push:
branches: [main]
paths:
- "docs/**"
- "site/**"
- "mkdocs.yml"
- "src/ai_company/**"
- ".github/workflows/pages.yml"
workflow_dispatch:
Comment on lines +3 to +12

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.

🧹 Nitpick | 🔵 Trivial

Consider gating deployment on CI success.

The workflow triggers on src/ai_company/** changes to regenerate API docs from docstrings, but doesn't wait for the main CI workflow to pass. This could deploy documentation for code that fails tests or type-checking.

Consider adding a dependency on the CI workflow:

💡 Suggested workflow modification
 on:
   push:
     branches: [main]
     paths:
       - "docs/**"
       - "site/**"
       - "mkdocs.yml"
       - "src/ai_company/**"
       - ".github/workflows/pages.yml"
   workflow_dispatch:
+  workflow_run:
+    workflows: ["CI"]  # or your main CI workflow name
+    types: [completed]
+    branches: [main]

Then add a condition to skip if CI failed:

jobs:
  build:
    # Only run if triggered manually, by push, or if CI succeeded
    if: >
      github.event_name != 'workflow_run' ||
      github.event.workflow_run.conclusion == 'success'
📝 Committable suggestion

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

Suggested change
on:
push:
branches: [main]
paths:
- "docs/**"
- "site/**"
- "mkdocs.yml"
- "src/ai_company/**"
- ".github/workflows/pages.yml"
workflow_dispatch:
on:
push:
branches: [main]
paths:
- "docs/**"
- "site/**"
- "mkdocs.yml"
- "src/ai_company/**"
- ".github/workflows/pages.yml"
workflow_dispatch:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pages.yml around lines 3 - 12, The workflow currently runs
on pushes to src/ai_company/** and may deploy docs even if the main CI failed;
add a workflow_run trigger for the main CI and guard the build job with an if
condition so it only runs when not invoked by a workflow_run or when the
triggering workflow_run concluded successfully: update the top-level on: to
include a workflow_run for the CI workflow name/id, and add an if: condition on
the build job (jobs.build.if) equivalent to "github.event_name != 'workflow_run'
|| github.event.workflow_run.conclusion == 'success'" so docs
regeneration/deploy is skipped when CI failed.


permissions: {}

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
name: Build Site
runs-on: ubuntu-latest
Comment thread
coderabbitai[bot] marked this conversation as resolved.
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

# --- MkDocs (documentation at /docs) ---
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.14"
allow-prereleases: true

- name: Install uv
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1

- name: Install docs dependencies
run: uv sync --group docs --no-dev

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

CI reproducibility: this workflow uses uv sync without --frozen, which can allow dependency resolution drift (and differs from the repo’s setup-python-uv action that runs uv sync --frozen). Consider adding --frozen here (and optionally reusing the composite action) so Pages builds fail fast if uv.lock is out of date.

Suggested change
run: uv sync --group docs --no-dev
run: uv sync --group docs --no-dev --frozen

Copilot uses AI. Check for mistakes.

- name: Build MkDocs
run: uv run mkdocs build --strict --site-dir _site/docs

# --- Astro (landing page at /) ---
- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22"

- name: Install Astro dependencies
working-directory: site
run: npm ci

- name: Build Astro
working-directory: site
run: npm run build

# --- Merge outputs ---
- name: Merge Astro + MkDocs into final output
run: |
# Astro output goes to root
cp -r site/dist/* _site/
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
# MkDocs output is already at _site/docs/ from the build step

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: _site

deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac553fd0d31 # v4
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,13 @@ Thumbs.db
web/node_modules/
web/dist/

# Documentation build output (MkDocs)
_site/

# Astro / Node.js (landing page)
site/node_modules/
site/dist/
site/.astro/
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# uv
.python-version
15 changes: 14 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,20 @@ uv run pytest tests/ -m integration -n auto # integration tests only
uv run pytest tests/ -m e2e -n auto # e2e tests only
uv run pytest tests/ -n auto --cov=ai_company --cov-fail-under=80 # full suite + coverage
uv run pre-commit run --all-files # all pre-commit hooks
uv run mkdocs build --strict # build docs (output: _site/)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
uv run mkdocs serve # local docs preview (http://127.0.0.1:8000)
```

## Documentation

- **Docs source**: `docs/` (MkDocs markdown + mkdocstrings auto-generated API reference)
- **Landing page**: `site/` (Astro, Concept C hybrid design)
- **Config**: `mkdocs.yml` at repo root
- **API reference**: auto-generated from docstrings via mkdocstrings + Griffe (AST-based, no imports)
- **CI**: `.github/workflows/pages.yml` — builds Astro landing + MkDocs docs, merges, deploys to GitHub Pages
- **Architecture decision**: `docs/decisions/ADR-003-documentation-architecture.md`
- **Dependencies**: `docs` group in `pyproject.toml` (`mkdocs-material`, `mkdocstrings[python]`, `griffe-pydantic`)

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.

⚠️ Potential issue | 🟡 Minor

Document how contributors install the new MkDocs toolchain.

Line 52 says the docs stack lives in a separate docs group, but the quick commands only introduce the new mkdocs invocations. As written, the setup path for Lines 40-41 is ambiguous on a fresh checkout.

🛠️ Suggested doc update
 uv sync                                    # install all deps (dev + test)
+uv sync --group docs                       # install MkDocs docs toolchain
 uv run ruff check src/ tests/              # lint
 uv run ruff check src/ tests/ --fix        # lint + auto-fix
 uv run ruff format src/ tests/             # format
 uv run mypy src/ tests/                    # type-check (strict)
🧰 Tools
🪛 LanguageTool

[uncategorized] ~50-~50: The official name of this software platform is spelled with a capital “H”.
Context: ...riffe (AST-based, no imports) - CI: .github/workflows/pages.yml — builds Astro lan...

(GITHUB)

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

In `@CLAUDE.md` around lines 40 - 52, Add explicit install instructions for the
MkDocs toolchain so a fresh checkout can run the shown commands: state that
contributors must install the project dependencies for the docs group
(referencing pyproject.toml's `docs` group) before running `mkdocs build` /
`mkdocs serve`, and give the supported install commands (e.g. Poetry: `poetry
install --with docs` or legacy `poetry install --extras docs`, and pip/editable:
`pip install -e .[docs]`) plus any virtualenv guidance; update the section
around the `uv run mkdocs build` and `uv run mkdocs serve` lines to include this
install step and a note that `mkdocs` is provided by the `docs` dependency group
in `pyproject.toml`.


## Docker

```bash
Expand Down Expand Up @@ -159,7 +171,8 @@ src/ai_company/
## CI

- **Jobs**: lint (ruff) + type-check (mypy src/ tests/) + test (pytest + coverage) run in parallel → ci-pass (gate)
- **Docker**: `.github/workflows/docker.yml` — builds backend + web images, pushes to GHCR, signs with cosign. Scans: Trivy (CRITICAL = hard fail, HIGH = warn-only) + Grype (critical cutoff). CVE triage via `.trivyignore.yaml` and `.grype.yaml`. Images only pushed after scans pass. Triggers on push to main and version tags (`v*`).
- **Pages**: `.github/workflows/pages.yml` — builds Astro landing + MkDocs docs, merges, deploys to GitHub Pages on push to main
- **Docker**: `.github/workflows/docker.yml` — builds backend + web images, pushes to GHCR, signs with cosign. Scans: Trivy (CRITICAL = hard fail, HIGH = warn-only) + Grype (critical cutoff). CVE triage via `.github/.trivyignore.yaml` and `.github/.grype.yaml`. Images only pushed after scans pass. Triggers on push to main and version tags (`v*`).
- **Matrix**: Python 3.14
- **Dependabot**: daily uv + github-actions + docker updates, grouped minor/patch, no auto-merge
- **Secret scanning**: gitleaks workflow on push/PR + weekly schedule
Expand Down
39 changes: 31 additions & 8 deletions DESIGN_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -3214,11 +3214,31 @@ synthorg/
│ ├── unit/
│ ├── integration/
│ └── e2e/
├── mkdocs.yml # MkDocs configuration
├── docs/
│ ├── decisions/
│ │ ├── ADR-001-memory-layer.md
│ │ └── ADR-002-design-decisions-batch-1.md
│ └── getting_started.md
│ ├── index.md # Documentation landing page
│ ├── getting_started.md
│ ├── overrides/ # MkDocs theme overrides
│ ├── architecture/
│ │ ├── index.md # Architecture overview
│ │ └── decisions.md # ADR index
│ ├── api/ # Auto-generated API reference (mkdocstrings)
│ │ ├── index.md # API reference landing
│ │ ├── core.md, engine.md, providers.md, budget.md, ...
│ │ └── tools.md
│ └── decisions/
│ ├── ADR-001-memory-layer.md
│ ├── ADR-002-design-decisions-batch-1.md
│ └── ADR-003-documentation-architecture.md
├── site/ # Astro landing page (synthorg.io root)
│ ├── astro.config.mjs
│ ├── package.json
│ ├── tsconfig.json
│ ├── public/
│ │ └── favicon.svg
│ └── src/
│ ├── layouts/Base.astro
│ └── pages/index.astro
├── docker/
│ ├── backend/
│ │ └── Dockerfile # 3-stage: python:3.14-slim → chainguard/python-dev → chainguard/python (distroless)
Expand All @@ -3238,15 +3258,18 @@ synthorg/
│ │ ├── docker.yml # Build → scan → push → sign (GHCR)
│ │ ├── dependency-review.yml # License allow-list on PRs
│ │ ├── release.yml # Release Please (automated versioning + GitHub Releases)
│ │ └── secret-scan.yml # Gitleaks on push/PR + weekly
│ │ ├── secret-scan.yml # Gitleaks on push/PR + weekly
│ │ └── pages.yml # Build Astro + MkDocs → deploy GitHub Pages
│ ├── actions/
│ │ └── setup-python-uv/ # Composite action: Python + uv install
│ ├── dependabot.yml # uv + github-actions + docker updates
│ ├── CHANGELOG.md # Release changelog (managed by Release Please)
│ ├── CONTRIBUTING.md
│ └── SECURITY.md
│ ├── SECURITY.md
│ ├── .grype.yaml # Grype CVE ignore list (synced with .trivyignore.yaml)
│ └── .trivyignore.yaml # Trivy CVE ignore list (structured YAML format)
├── .dockerignore # Consolidated Docker build context exclusions
├── .grype.yaml # Grype CVE ignore list (synced with .trivyignore.yaml)
├── .trivyignore.yaml # Trivy CVE ignore list (structured YAML format)
├── .gitleaks.toml # Gitleaks config (test file allowlist)
├── DESIGN_SPEC.md # This document
├── README.md
├── pyproject.toml
Expand Down
45 changes: 45 additions & 0 deletions docs/api/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# API Layer

Litestar REST + WebSocket API — controllers, authentication, guards, and channels.

## App

::: ai_company.api.app

## Config

::: ai_company.api.config

## DTOs

::: ai_company.api.dto

## Errors

::: ai_company.api.errors

## Guards

::: ai_company.api.guards

## Middleware

::: ai_company.api.middleware

## Pagination

::: ai_company.api.pagination

## WebSocket Models

::: ai_company.api.ws_models

## Auth

::: ai_company.api.auth.config

::: ai_company.api.auth.models

::: ai_company.api.auth.service

::: ai_company.api.auth.middleware
Loading
Loading