Skip to content

Commit 59da976

Browse files
Merge branch 'main' into dependabot/pip/src/agentflow_runtime/processing/flink_jobs/pydantic-gte-2.13.5-and-lt-3
2 parents 2b038ef + 806e19d commit 59da976

184 files changed

Lines changed: 16085 additions & 919 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/CODEOWNERS‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Repository-side ownership for review-sensitive surfaces.
2+
/.github/** @brownjuly2003-code
3+
/infrastructure/** @brownjuly2003-code
4+
/helm/** @brownjuly2003-code
5+
/src/agentflow_runtime/serving/api/auth/** @brownjuly2003-code
6+
/src/agentflow_runtime/serving/api/middleware/** @brownjuly2003-code
7+
/scripts/release.py @brownjuly2003-code
8+
/scripts/evaluate_trivy_policy.py @brownjuly2003-code
9+
/scripts/run_pip_audit_scan.py @brownjuly2003-code
10+
/security/trivy-waivers.json @brownjuly2003-code
11+
/config/project_claims.toml @brownjuly2003-code
12+
/SECURITY.md @brownjuly2003-code
13+
/docs/release-*.md @brownjuly2003-code

‎.github/workflows/ci.yml‎

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,27 +300,57 @@ jobs:
300300
run: |
301301
# Security-critical auth manager (key match/verify, tenant isolation,
302302
# rate-limit/failed-auth windows, rotation grace) and a mutmut target;
303-
# the gate runs its dedicated unit files. Module coverage is 94% so the
303+
# the gate runs its dedicated unit files. Module coverage is 97% so the
304304
# 90% gate keeps a cushion; the remaining gap is the platform-divergent
305-
# SIGHUP handler and bcrypt rotation paths the integration/e2e auth
306-
# suites cover.
305+
# SIGHUP handler, the two read-only-store branches load() reaches only
306+
# when the store flips between probe and write, and the thin
307+
# KeyRotator/usage-table delegations the integration/e2e auth suites
308+
# cover.
309+
#
310+
# This list IS the gate: a dedicated unit file that is not named here
311+
# buys the module nothing. test_key_store_readonly.py arrived with the
312+
# read-only Secret mount work (F-02 B, 2026-08-23) and was never added,
313+
# which by itself took the module from 94% to 82% -- unnoticed, because
314+
# an earlier step in this job was failing and this one never ran.
307315
#
308316
# NOTE: unlike the other per-module gates this uses `coverage run` +
309317
# `coverage report --include`, NOT `pytest --cov=<module>`. The auth
310318
# manager pulls in duckdb (usage table), and pytest-cov's source
311319
# instrumentation of a duckdb-importing module trips duckdb's lazy
312320
# `_duckdb._sqltypes` import at COLLECTION time, both locally and on CI
313321
# runners. `coverage run` imports duckdb normally and avoids the break.
314-
python -m coverage run -m pytest tests/unit/test_auth.py tests/unit/test_auth_manager_pure_logic.py tests/unit/test_auth_manager_memory_bounds.py tests/unit/test_auth_hashed_key_guidance.py tests/unit/test_auth_argon2_lookup.py -p no:schemathesis
322+
python -m coverage run -m pytest \
323+
tests/unit/test_auth.py \
324+
tests/unit/test_auth_manager_pure_logic.py \
325+
tests/unit/test_auth_manager_memory_bounds.py \
326+
tests/unit/test_auth_hashed_key_guidance.py \
327+
tests/unit/test_auth_argon2_lookup.py \
328+
tests/unit/test_auth_key_store_probe.py \
329+
tests/unit/test_auth_manager_key_resolution.py \
330+
tests/unit/test_key_store_readonly.py \
331+
-p no:schemathesis
315332
python -m coverage report --include="*/serving/api/auth/manager.py" --show-missing --fail-under=90
316333
- name: Run key rotation coverage gate
317334
run: |
318335
# Security-critical key-rotation lifecycle (create/rotate/revoke,
319336
# grace-period scheduling, rotation status) and a mutmut target. Like
320337
# the auth manager gate it pulls in duckdb, so it uses coverage run +
321338
# coverage report --include (not pytest --cov) to avoid the
322-
# duckdb _duckdb._sqltypes collection break. Module coverage is 93%.
323-
python -m coverage run -m pytest tests/unit/test_key_rotation.py -p no:schemathesis
339+
# duckdb _duckdb._sqltypes collection break.
340+
#
341+
# Module coverage is 100%. It was 89.6% on test_key_rotation.py alone
342+
# -- passing only because coverage rounds to 90 before comparing, so
343+
# one more defensive line would have turned this gate red without
344+
# anyone changing its behaviour. test_key_rotation.py pins the happy
345+
# lifecycle; the revoke_and_failures file adds what it never reached:
346+
# revoke_key_by_id (the only revoke path the admin router calls since
347+
# F-02 A), the read-only vs merely-broken key-store split, and the
348+
# grace-period cleanup failure that runs on a timer thread with no
349+
# caller left to see it.
350+
python -m coverage run -m pytest \
351+
tests/unit/test_key_rotation.py \
352+
tests/unit/test_key_rotation_revoke_and_failures.py \
353+
-p no:schemathesis
324354
python -m coverage report --include="*/serving/api/auth/key_rotation.py" --show-missing --fail-under=90
325355
- name: Run outbox coverage gate
326356
run: |
@@ -460,6 +490,10 @@ jobs:
460490
- name: Install dependencies (frozen lock profile)
461491
run: bash scripts/ci_sync.sh dev-tools
462492
- uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
493+
with:
494+
version: v3.16.3
495+
- name: Report Helm version
496+
run: helm version --short
463497
- uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
464498
with:
465499
install_only: true
@@ -541,12 +575,12 @@ jobs:
541575

542576
terraform-validate:
543577
runs-on: ubuntu-latest
544-
timeout-minutes: 10
578+
timeout-minutes: 20
545579
steps:
546580
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
547581
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
548582
with:
549-
terraform_version: "1.8.0"
583+
terraform_version: "1.15.4"
550584
- name: Terraform fmt check
551585
run: terraform fmt -check -recursive infrastructure/terraform/
552586
- name: Terraform init
@@ -557,6 +591,18 @@ jobs:
557591
run: |
558592
cd infrastructure/terraform
559593
terraform validate
594+
# h1: hashes in the lock file carry no platform label. This step is the
595+
# guard that linux_amd64 (ubuntu-latest) is actually covered; it uses the
596+
# same three-platform command the module README documents (re-run
597+
# 2026-09-05: no lock rewrite). Three provider zips (~0.7 GB) plus
598+
# terraform init is why this job's timeout-minutes is 20, not 10.
599+
# It has not been observed on a GitHub runner until the owner pushes.
600+
- name: Provider lock covers linux_amd64
601+
run: |
602+
cd infrastructure/terraform
603+
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 -platform=windows_amd64
604+
git ls-files --error-unmatch .terraform.lock.hcl
605+
git diff --exit-code .terraform.lock.hcl
560606
561607
sdk-ts:
562608
# Audit P1-5: typecheck/tests/build for the TypeScript SDK previously ran

‎.github/workflows/security.yml‎

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
run: |
4848
python - <<'PY'
4949
import os
50+
import shutil
5051
import subprocess
5152
import sys
5253
import tempfile
@@ -84,6 +85,22 @@ jobs:
8485
deduped = list(dict.fromkeys(entries))
8586
target.write_text("\n".join(deduped) + "\n", encoding="utf-8")
8687
88+
def copy_locked_requirements(source: Path, target: Path) -> int:
89+
if not source.exists():
90+
raise SystemExit(f"Required dependency lock is missing: {source}")
91+
locked = [
92+
line.strip()
93+
for line in source.read_text(encoding="utf-8").splitlines()
94+
if line and not line[0].isspace() and not line.startswith("#")
95+
]
96+
if not locked:
97+
raise SystemExit(f"Required dependency lock resolved to zero packages: {source}")
98+
if any("==" not in requirement or not requirement.endswith("\\") for requirement in locked):
99+
raise SystemExit(f"Required dependency lock is not fully hash-pinned: {source}")
100+
shutil.copyfile(source, target)
101+
print(f"Copied {len(locked)} locked packages -> {target}")
102+
return len(locked)
103+
87104
def resolve_requirements(name: str, entries: list[str], target: Path) -> int:
88105
if not entries:
89106
raise SystemExit(
@@ -172,13 +189,13 @@ jobs:
172189
)
173190
174191
# The Flink runtime is not an extra (its beam chain can never
175-
# co-install with core pyarrow>=17) — its manifest lives next to
176-
# the Flink image Dockerfile. Scan it as its own bucket.
177-
flink_count = resolve_requirements(
178-
"flink-runtime",
179-
load_requirements(
180-
root / "src" / "agentflow_runtime" / "processing" / "flink_jobs" / "requirements.txt"
181-
),
192+
# co-install with core pyarrow>=17). Keep validating its human-owned
193+
# manifest, but scan the image's hash lock without resolving it again.
194+
load_requirements(
195+
root / "src" / "agentflow_runtime" / "processing" / "flink_jobs" / "requirements.txt"
196+
)
197+
flink_count = copy_locked_requirements(
198+
root / "src/agentflow_runtime/processing/flink_jobs/flink-requirements.lock",
182199
output_dir / "requirements-flink-runtime.txt",
183200
)
184201
@@ -254,14 +271,39 @@ jobs:
254271
# `from apache_beam.io.parquetio import *`, and parquetio's class
255272
# bodies evaluate `pa.Table` annotations with pa=None ->
256273
# AttributeError). The dependency is load-bearing at import time
257-
# even though our jobs never use it.
274+
# even though our jobs never use it. Expires 2026-10-27 (same as
275+
# the Trivy waiver). Disposition is recorded in
276+
# security/trivy-waivers.json (flink-runtime, CVE-2026-25087).
277+
#
278+
# SFTY-20260724-05622 (CVE-2026-59939): unbounded decompression of
279+
# HTTP response bodies encoded with Content-Encoding: gzip or
280+
# deflate in _decompressContent (httplib2/__init__.py), fixed in
281+
# 0.32.0. A malicious or compromised HTTP server can return a
282+
# small compressed payload that expands to an arbitrarily large
283+
# size in memory (MemoryError / OOM-kill). Only the flink-runtime
284+
# bucket resolves an affected httplib2 (apache-flink's beam chain
285+
# caps httplib2<0.23). Why the risk is negligible here:
286+
# 1. The Flink DataStream job has no httplib2 import or HTTP
287+
# client path. Its ingress is Kafka, and it never processes
288+
# attacker-controlled compressed HTTP response bodies.
289+
# Blocked upstream, not by our pin: apache-flink==2.3.0 requires
290+
# apache-beam>=2.54.0,<=2.61.0; apache-beam==2.61.0 requires
291+
# httplib2>=0.8,<0.23.0. Installing httplib2 0.32 into this graph
292+
# is impossible without breaking Beam. Dependabot watches the
293+
# Flink manifest (src/agentflow_runtime/processing/flink_jobs),
294+
# so the next apache-flink release opens a PR — re-check this
295+
# ignore there. Remove this ignore when a supported apache-flink/
296+
# apache-beam dependency set accepts httplib2>=0.32.0, or
297+
# immediately if an httplib2 call path is added. Expires 2026-10-27
298+
# (same as the Trivy waiver).
299+
# Disposition is recorded in security/trivy-waivers.json
300+
# (flink-runtime, CVE-2026-59939).
258301
#
259302
# 88512 (langchain cross-ecosystem false positive) was ignored here
260303
# until 2026-07-20; PyUp corrected the entry (verified: safety
261304
# 2.3.5 reports 0 findings on the pinned langchain stack), so the
262305
# ignore is gone. If it ever resurfaces, it fails this job loudly.
263-
safety check \
264-
--ignore SFTY-20260217-93940 \
306+
python scripts/run_safety_scan.py --waivers security/trivy-waivers.json \
265307
-r .artifacts/security/safety/requirements-main.txt \
266308
-r .artifacts/security/safety/requirements-sdk.txt \
267309
-r .artifacts/security/safety/requirements-integrations.txt \
@@ -287,6 +329,9 @@ jobs:
287329
python-version: "3.11"
288330
- name: Install pip-audit
289331
run: python -m pip install --upgrade pip "pip-audit>=2.7,<3" uv==0.8.23
332+
# Deliberately bare: the production image lock is the one inventory with
333+
# no waiver mechanism reachable at all. A finding here is a release
334+
# blocker, not something to argue about in security/trivy-waivers.json.
290335
- name: Audit the locked production dependency set
291336
run: pip-audit --no-deps -r requirements-docker.lock
292337
# Audit F-03: the production lock alone left uv.lock's dev/test chain
@@ -302,7 +347,17 @@ jobs:
302347
mkdir -p .artifacts/security/pip-audit
303348
uv export --frozen --format requirements-txt --all-extras \
304349
--no-emit-project -o .artifacts/security/pip-audit/requirements-all-profiles.txt
305-
pip-audit --no-deps -r .artifacts/security/pip-audit/requirements-all-profiles.txt
350+
# Audit FB-02: nltk 3.10.3 carries PYSEC-2026-3740 and upstream has
351+
# published no fix, so no bump can close it and this job stayed red
352+
# with nowhere to record why. The suppression lives in
353+
# security/trivy-waivers.json (scope python-profiles), where it is
354+
# validated, expires on 2026-11-01, and fails this job the moment it
355+
# stops matching -- including the day nltk ships a fix. Do not pass
356+
# --ignore-vuln here: a flag in YAML has none of those properties.
357+
python scripts/run_pip_audit_scan.py \
358+
--waivers security/trivy-waivers.json \
359+
--scope python-profiles \
360+
-r .artifacts/security/pip-audit/requirements-all-profiles.txt
306361
307362
npm-audit:
308363
runs-on: ubuntu-latest

‎.github/workflows/terraform-apply.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ jobs:
3232
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3333

3434
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
35+
with:
36+
terraform_version: "1.15.4"
3537

3638
- name: Resolve Terraform tfvars file
3739
id: tfvars
@@ -84,6 +86,8 @@ jobs:
8486
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
8587

8688
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
89+
with:
90+
terraform_version: "1.15.4"
8791

8892
- name: Resolve Terraform tfvars file
8993
id: tfvars
@@ -138,6 +142,8 @@ jobs:
138142
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
139143

140144
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
145+
with:
146+
terraform_version: "1.15.4"
141147

142148
- name: Resolve Terraform tfvars file
143149
id: tfvars

‎.gitignore‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ logs/
3535
.terraform/
3636
*.tfstate
3737
*.tfstate.backup
38-
.terraform.lock.hcl
3938

4039
data/
4140
checkpoints/
@@ -135,10 +134,12 @@ agentflow_demo_api.duckdb*
135134
# Claude Code config/session state and any *_local/ scratch dir are kitchen, never
136135
# product — untracked-but-unignored is one `git add -A` from a public repo. (N-6/P5.2)
137136
/.claude/
137+
/.myflow/
138138
/*_local/
139139
/AGENT_STATE.md
140140
/AUTOPILOT.md
141141
/BACKLOG.md
142+
/SESSION_LOG*.md
142143
/help.md
143144
/res.md
144145
/all-open-questions-closure-plan.md

‎AGENTS.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# DE_project Rules
2+
3+
## Durable State and Autonomous Continuation
4+
- On a new session, resume, compaction, stale context, or contradiction, rebuild from `git status` and these files when present: `AGENT_STATE.md`, `docs/SESSION_HANDOFF.md`, `docs/operations/local-verification-matrix.md`, `AUTOPILOT.md`, `docs/operations/autonomous-compact-safe-process.md`, `BACKLOG.md`, `.autopilot/BLOCKED.md`.
5+
- If remembered work is already finished, impossible, or contradicted by disk state, stop pursuing it and choose another safe atomic item.
6+
- Candidate order is dirty WIP, failed or blocked verification diagnostics, PLAN/Next Work, narrow failing or uncovered tests, then documented operations findings.
7+
- A real external blocker ends that atomic item. Record non-secret evidence and continue only with an independent safe item.
8+
- A single verification or acceptance check may have at most **five full attempts per named slice**, counted `1/5` through `5/5`; the initial run is `1/5`. After each failed or timed-out attempt, another full attempt is allowed only after a new narrowed diagnostic hypothesis or a relevant corrective change. Raw unchanged retries are forbidden. If attempt `5/5` fails or times out, end the slice for the turn and report it.
9+
10+
## Verification Hosts
11+
- Keep local Windows Python processes below 1 GiB. Use narrow checks and rely on `D:\SystemState\PythonMemoryGuard\python-memory-guard.ps1`.
12+
- Run Docker-heavy verification, jobs likely to exceed 1 GiB, and macOS diagnostics through SSH alias `deproject-mac`; the Mac checkout is `/Users/julia/agentflow-docker-check`.
13+
- The sharded Windows suite pins `OPENBLAS_NUM_THREADS=1` and `OMP_NUM_THREADS=1` in every child process; unpinned, OpenBLAS alone accounts for ~550 MiB of the 1 GiB budget. Measurements and the production caveat: `docs/operations/windows-verification.md`.
14+
- Non-secret Mac context belongs in `AGENT_STATE.md`, `docs/SESSION_HANDOFF.md`, and `docs/operations/local-verification-matrix.md`.
15+
- Never print, commit, or copy SSH keys, passwords, tokens, recovery codes, or other secrets into project files, logs, PRs, or chat.

0 commit comments

Comments
 (0)