Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d6de01a
fix(search): enforce the entity allowlist before scoring (P0-4)
JuliaEdom Jul 11, 2026
47f7815
feat(serving): provisioning is a writer privilege, not a boot side ef…
JuliaEdom Jul 11, 2026
e8664b2
fix(serving): route every read surface and health through the backend…
JuliaEdom Jul 11, 2026
d085a9d
fix(serving): make the tenant boundary physical, on both stores (P0-1)
JuliaEdom Jul 11, 2026
0854355
fix(serving): prove the tenant boundary, and delete the one that was …
JuliaEdom Jul 11, 2026
e425a32
fix(release): keep secrets out of the image, and stop calling a fixtu…
JuliaEdom Jul 11, 2026
cbfc126
ci: lint what ships, time out every job, gate the TS SDK (P1-3, P1-5)
JuliaEdom Jul 11, 2026
e29959e
chore: keep the session handoff out of the repo
JuliaEdom Jul 11, 2026
09b3c03
build(deps): lock the dependency set, and delete the extra that could…
JuliaEdom Jul 12, 2026
bdd3b29
feat(control-plane): a connection budget, one-transaction batches, ve…
JuliaEdom Jul 12, 2026
e18d36a
perf(search): maintenance reads the journal, not the world (P1-6)
JuliaEdom Jul 12, 2026
8e53dad
fix(slo): report SLIs, not rescaled aggregates (P2-2)
JuliaEdom Jul 12, 2026
24f6b5a
docs(ssot): one version source, honest claims, a gate that keeps them…
JuliaEdom Jul 12, 2026
691db8a
feat(security): the production profile refuses plaintext, TLS is firs…
JuliaEdom Jul 12, 2026
9c8cc17
fix(terraform): one state object per environment, honest reference sc…
JuliaEdom Jul 12, 2026
8520366
fix(load): the seeder builds the canonical tenant schema, not its own
JuliaEdom Jul 12, 2026
0ef2d5a
fix(test): assert helm rejects undeclared tls keys, not the wording
JuliaEdom Jul 12, 2026
c47ef01
fix(security): make declared scan filters real, pin the one unfixable…
JuliaEdom Jul 12, 2026
1bb2059
test(query): cover the index scans and the stuck-orders read in the p…
JuliaEdom Jul 12, 2026
790742a
fix(bench): fixture inserts name their columns against the tenant schema
JuliaEdom Jul 12, 2026
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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
.env.*
*.pem
*.key
# Secret-bearing config: mirrors pyproject.toml's sdist exclude and
# scripts/check_release_artifacts.py's FORBIDDEN_MEMBER_PATTERNS (audit
# P1-4). Dockerfile.api does `COPY config /app/config`; without these entries
# that copy would bake credential material (bcrypt key hashes, webhook
# secrets) into an immutable image layer. Runtime supplies the real files via
# a docker-compose bind mount or a Helm Secret mount, never from the image.
config/api_keys.yaml
config/webhooks.yaml
config/tenants.yaml
*.duckdb
*.duckdb.*
.artifacts
Expand Down
14 changes: 11 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ version: 2
# matches CONTRIBUTING.md, and a grouped minor+patch PR so the review
# queue does not get spammed. Major bumps stay individual.
#
# Security advisories from GitHub bypass the schedule and open
# immediately regardless of group / interval — that is Dependabot
# default and is what we want.
# Dependabot version updates (the schedule below) are a different feature
# from Dependabot SECURITY updates. Security updates are a separate
# repo-level toggle, and audit P1-3 (2026-07-11) confirmed via the GitHub
# API that it is currently DISABLED on this repo — an advisory on any
# ecosystem below waits for the next scheduled run like any other bump; it
# does not open a PR "immediately" as an earlier version of this comment
# claimed. Enabling it is a repo-settings change gated on the repo owner:
#
# gh api --method PUT repos/brownjuly2003-code/agentflow/automated-security-fixes
#
# This file cannot flip that toggle by itself.

updates:
# ── Python: root runtime (agentflow-runtime) ───────────────────────
Expand Down
67 changes: 33 additions & 34 deletions .github/workflows/backup.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: Nightly Backup
name: Backup/Restore Regression Test

# This does not back up a deployed environment. It builds synthetic DuckDB
# fixtures on an ephemeral runner, backs *those* up, verifies the manifest,
# and restores into a scratch directory — a regression test for
# scripts/backup.py, scripts/verify_backup.py and scripts/restore.py, not a
# disaster-recovery control (audit P1-2). See docs/disaster-recovery.md for
# what backup coverage actually exists today.
on:
schedule:
- cron: "0 2 * * *"
Expand All @@ -22,11 +28,12 @@ jobs:
- name: Prepare backup fixture
run: |
python - <<'PY'
import re
from pathlib import Path

import duckdb

from src.serving.backends.duckdb_backend import DuckDBBackend

pipeline_db = Path("agentflow_demo.duckdb")
usage_db = Path("agentflow_api.duckdb")
for path in (pipeline_db, usage_db):
Expand All @@ -36,36 +43,28 @@ jobs:
if wal_path.exists():
wal_path.unlink()

tenants_content = Path("config/tenants.yaml").read_text(encoding="utf-8")
tenant_schemas = re.findall(
r'^\s*duckdb_schema:\s*["\']?([^"\']+)["\']?\s*$',
tenants_content,
flags=re.MULTILINE,
)

conn = duckdb.connect(str(pipeline_db))
# The product's own DDL, not a copy of it. This step used to hand-write
# a `CREATE SCHEMA` per tenant, parsed out of the `duckdb_schema` field
# of config/tenants.yaml -- the schema-per-tenant model that ADR-004
# retired. Nothing in src/ ever created those schemas, so the fixture
# was fabricating the very thing the restore smoke test then went
# looking for. Two tenants now share one table, keyed by `tenant_id`,
# which is what a restored store actually has to bring back.
backend = DuckDBBackend(db_path=str(pipeline_db))
backend.ensure_schema()
conn = backend.connection
try:
for schema in tenant_schemas:
conn.execute(f'CREATE SCHEMA IF NOT EXISTS "{schema}"')
conn.execute(
f'''
CREATE TABLE IF NOT EXISTS "{schema}".orders_v2 (
order_id VARCHAR PRIMARY KEY,
user_id VARCHAR,
status VARCHAR,
total_amount DECIMAL(10,2),
currency VARCHAR,
created_at TIMESTAMP
)
'''
)
conn.execute(
f'''
INSERT INTO "{schema}".orders_v2 VALUES
(?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
''',
[f"ORD-{schema.upper()}-001", f"USR-{schema.upper()}", "confirmed", 42.0, "RUB"],
)
conn.execute(
"""
INSERT OR REPLACE INTO orders_v2
(tenant_id, order_id, user_id, status, total_amount, currency, created_at)
VALUES
('acme-corp', 'ORD-ACME-001', 'USR-ACME', 'confirmed', 42.00, 'RUB',
CURRENT_TIMESTAMP),
('demo', 'ORD-DEMO-001', 'USR-DEMO', 'confirmed', 17.00, 'RUB',
CURRENT_TIMESTAMP)
"""
)
finally:
conn.close()

Expand All @@ -90,7 +89,7 @@ jobs:
finally:
conn.close()
PY
- name: Create backup archive
- name: Create backup archive (synthetic fixture)
run: python scripts/backup.py --output backup-artifacts
- name: Resolve backup path
id: backup
Expand All @@ -105,9 +104,9 @@ jobs:
python scripts/restore.py \
--backup "${{ steps.backup.outputs.archive_path }}" \
--target-root /tmp/agentflow-restore
- name: Upload backup artifact
- name: Upload regression-test archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: agentflow-nightly-backup
name: agentflow-backup-restore-regression-fixture
path: ${{ steps.backup.outputs.archive_path }}
retention-days: 7
67 changes: 65 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ concurrency:
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
Expand All @@ -24,15 +25,46 @@ jobs:
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Ruff check
run: ruff check src/ tests/ scripts/ sdk/
run: ruff check src/ tests/ scripts/ sdk/ integrations/ warehouse/
- name: Ruff format check
run: ruff format --check src/ tests/ scripts/ sdk/
run: ruff format --check src/ tests/ scripts/ sdk/ integrations/ warehouse/
- name: Type check
run: mypy src/ --ignore-missing-imports

# Audit P1-3: the lock chain stays honest end to end — uv.lock matches
# pyproject.toml, requirements-docker.lock (what Dockerfile.api actually
# installs) matches uv.lock, and a fresh hash-verified install from it is
# a consistent environment per pip check. Runs independently of lint: a
# dependency drift should be visible even while lint is red.
lock-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
- name: Install uv
# Exact pin: a newer uv may rewrite the lock format and turn this
# gate into noise. Bump deliberately, together with uv.lock.
run: pip install uv==0.8.23
- name: uv.lock matches pyproject.toml
run: uv lock --check
- name: requirements-docker.lock matches uv.lock
run: |
uv export --format requirements-txt --extra cloud --extra postgres \
--no-emit-project -o requirements-docker.lock
git diff --exit-code requirements-docker.lock
- name: Locked install is consistent
run: |
python -m venv /tmp/lockenv
/tmp/lockenv/bin/pip install --quiet --require-hashes -r requirements-docker.lock
/tmp/lockenv/bin/pip check

schema-check:
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
Expand All @@ -48,6 +80,7 @@ jobs:
test-unit:
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 25
permissions:
contents: read
id-token: write
Expand Down Expand Up @@ -161,6 +194,7 @@ jobs:
test-integration:
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 25
services:
kafka:
image: confluentinc/cp-kafka:7.7.0
Expand Down Expand Up @@ -320,6 +354,7 @@ jobs:

terraform-validate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
Expand All @@ -335,3 +370,31 @@ jobs:
run: |
cd infrastructure/terraform
terraform validate

sdk-ts:
# Audit P1-5: typecheck/tests/build for the TypeScript SDK previously ran
# only in publish-npm.yml (tag pushes) and mutation.yml; every PR only got
# `npm audit` (security.yml). This job gives PRs the same build-shaped
# gate the publish workflow already trusts, so a broken SDK cannot merge.
# Branch protection required-context wiring is a repo-settings change the
# owner still has to make (gh api repos/{owner}/{repo}/branches/main/protection).
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: sdk-ts
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "20"
- name: Install dependencies from lockfile
run: npm ci
- name: Typecheck
run: npm run typecheck
- name: Test
run: npm test
- name: Build
run: npm run build
- name: Verify package is publishable
run: npm pack --dry-run
3 changes: 3 additions & 0 deletions .github/workflows/container-attestation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
# never blocks docker-free PRs.
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
Expand Down Expand Up @@ -89,6 +90,7 @@ jobs:
build-push-sign-attest:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.confirm == 'SIGN' && inputs.mode == 'build-and-sign' }}
runs-on: ubuntu-latest
timeout-minutes: 25
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -133,6 +135,7 @@ jobs:
attest-and-sign:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.confirm == 'SIGN' && inputs.mode == 'sign-existing-digest' }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
packages: write
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
contract:
name: contract
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/dora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ permissions:
jobs:
dora-report:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ concurrency:
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ permissions:
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ permissions:
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
environment: pypi
permissions:
id-token: write
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
# Upload the result SARIF to the code-scanning dashboard.
security-events: write
Expand Down
Loading