Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ jobs:
'lint-python',
'lint-frontend',
'build',
'test (Python 3.10)',
'test (Python 3.11)',
'test (Python 3.12)',
'test-frontend',
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ jobs:
allow-ghsas: GHSA-w5hq-g745-h8pq
# PR #295: vite/vitest tree (BlueOak-1.0.0, MPL-2.0 lightningcss), type-fest (CC0-1.0),
# rollup@4.x compound SPDX (0BSD AND ISC AND MIT).
# Pillow 13 publishes the permissive MIT-CMU SPDX identifier; yt-dlp publishes
# the public-domain-equivalent Unlicense identifier. Both are direct runtime
# dependencies and are allowed explicitly here rather than excluded from review.
# FSL-1.1-MIT is not a valid SPDX id for allow-licenses — exclude @sentry/* via purls.
# caniuse-lite ships MIT code but a CC-BY-4.0 dataset; the scanner reports the data
# license, which is not code we redistribute. It updates on nearly every browserslist
Expand All @@ -153,7 +156,7 @@ jobs:
# already present, and the patched Sharp release only refreshes their versions.
allow-licenses: >-
MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Python-2.0,
BlueOak-1.0.0, MPL-2.0, CC0-1.0, 0BSD
BlueOak-1.0.0, MPL-2.0, CC0-1.0, 0BSD, MIT-CMU, Unlicense
# Docs/prototype pip deps (not shipped product code): the scanner reports regex with a
# CNRI-Python fragment (upstream misattribution; the package is Apache-2.0) and torch with
# a bundled BSL-1.0 component. Both live under docs/ only. Revisit if they move into
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ npm --workspace=apps/web run type-check
- **Import sorting**: isort (profile: black)
- **Linter**: Ruff (E, W, F, I, B, C4, UP rules; E501 ignored)
- **Type checking**: mypy strict mode (`disallow_untyped_defs = true`)
- Target Python 3.10+ (`pyproject.toml` is authoritative)
- Target Python 3.11–3.12 (`pyproject.toml` is authoritative)
- Config in `pyproject.toml`

### TypeScript/JavaScript
Expand Down
2 changes: 1 addition & 1 deletion GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ npm --workspace=apps/web run type-check
- **Formatter**: Black, 88-char line length
- **Linter**: Ruff (E, W, F, I, B, C4, UP; E501 ignored)
- **Type checking**: mypy strict (`disallow_untyped_defs = true`)
- Target Python 3.10+; `pyproject.toml` is authoritative
- Target Python 3.11–3.12; `pyproject.toml` is authoritative

### TypeScript
- Strict mode TypeScript (`apps/web/tsconfig.json`)
Expand Down
4 changes: 2 additions & 2 deletions MERGE_POLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ The real contexts, as observed on live pull requests:

| Surface | Check-run names |
| --- | --- |
| CI | `validate`, `guards`, `lint-python`, `lint-frontend`, `build`, `test (Python 3.10)`, `test (Python 3.11)`, `test (Python 3.12)`, `test-frontend` |
| CI | `validate`, `guards`, `lint-python`, `lint-frontend`, `build`, `test (Python 3.11)`, `test (Python 3.12)`, `test-frontend` |
| Static analysis | `CodeQL` |
| Security | `Security Scan - python`, `Security Scan - javascript`, `bandit`, `python-safety`, `npm-audit`, `trivy` |
| Secrets | `gitleaks (working tree)` |
| Dependencies | `dependency-review` |

Required for every pull request: `validate`, `guards`, `lint-python`,
`lint-frontend`, `build`, `test (Python 3.10)`, `test (Python 3.11)`,
`lint-frontend`, `build`, `test (Python 3.11)`,
`test (Python 3.12)`, `test-frontend`, `CodeQL`,
`gitleaks (working tree)`, `dependency-review`, `Security Scan - python`,
`Security Scan - javascript`, `bandit`, `python-safety`, `npm-audit`, `trivy`.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Do not restore the retired EventRelay Pro catalog or invent additional products

## Development

Requirements come from [package.json](package.json) and [pyproject.toml](pyproject.toml): Node.js 22+, npm 10.8.0 as the pinned package manager, and Python 3.10+ for the internal backend.
Requirements come from [package.json](package.json) and [pyproject.toml](pyproject.toml): Node.js 22+, npm 10.8.0 as the pinned package manager, and Python 3.11 or 3.12 for the internal backend.

From the repository root:

Expand Down
14 changes: 6 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "youtube_extension"
version = "1.0.0"
description = "UVAI Platform - Revolutionary AI-Powered Video Learning System"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11,<3.13"
license = {text = "MIT"}
authors = [
{name = "UVAI Team", email = "team@uvai.com"},
Expand All @@ -22,8 +22,6 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Internet :: WWW/HTTP",
Expand Down Expand Up @@ -65,7 +63,7 @@ dependencies = [
"python-dotenv>=1.2.2",
"PyYAML>=6.0.0",
"requests>=2.31.0",
"Pillow>=10.0.0",
"Pillow>=13.0.0",
"google-genai>=2.21.0",
"google-generativeai>=0.3.0",
"opentelemetry-distro>=0.40b0",
Expand Down Expand Up @@ -146,7 +144,7 @@ ml = [
"tokenizers>=0.15.0",
"sentence-transformers>=2.2.0",
"openai>=1.3.0",
"anthropic>=0.105.0",
"anthropic>=0.105.0,<2",
"google-generativeai>=0.3.0",
"cohere>=4.37.0",
"numpy>=1.24.0",
Expand Down Expand Up @@ -188,7 +186,7 @@ youtube_extension = ["templates/**/*", "static/**/*", "config/**/*"]

[tool.black]
line-length = 88
target-version = ['py39', 'py310', 'py311', 'py312']
target-version = ['py311', 'py312']
include = '\.pyi?$'
extend-exclude = '''
/(
Expand Down Expand Up @@ -225,7 +223,7 @@ known_third_party = [
]

[tool.mypy]
python_version = "3.9"
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
Expand All @@ -248,7 +246,7 @@ module = [
ignore_errors = true

[tool.ruff]
target-version = "py39"
target-version = "py311"
line-length = 88

[tool.ruff.lint]
Expand Down
2 changes: 1 addition & 1 deletion scripts/utilities/setup_fastvlm_gemini.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ if command -v python3 &> /dev/null; then
PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
print_status "Python $PYTHON_VERSION found"
else
print_error "Python 3 not found. Please install Python 3.10 or higher."
print_error "Python 3 not found. Please install Python 3.11 or 3.12."
exit 1
fi

Expand Down
44 changes: 29 additions & 15 deletions tests/unit/test_gh_aw_workflow_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ def _load_frontmatter(path: Path) -> dict:
return yaml.safe_load(frontmatter)



def test_coverage_workflow_is_authoritative() -> None:
workflow = _load_yaml(ROOT / ".github/workflows/coverage.yml")
job = workflow["jobs"]["coverage"]
steps = job["steps"]
run_step = next(step for step in steps if step.get("name") == "Run tests with coverage")
run_step = next(
step for step in steps if step.get("name") == "Run tests with coverage"
)
artifact_step = next(
step for step in steps if step.get("name") == "Upload coverage artifacts"
)
Expand All @@ -49,9 +50,12 @@ def test_coverage_workflow_is_authoritative() -> None:
assert "--cov-fail-under=84.2" in run_script
assert "--cov-fail-under" not in pytest_addopts
assert "--timeout=120" in run_script
assert ".[dev,youtube]" in next(
step for step in steps if step.get("name") == "Install dependencies"
)["run"]
assert (
".[dev,youtube]"
in next(step for step in steps if step.get("name") == "Install dependencies")[
"run"
]
)
assert 84.1766 <= float(coverage_report["fail_under"]) <= 90
assert int(coverage_report["precision"]) >= 4
for suppression in ("|| true", "set +e"):
Expand All @@ -68,9 +72,7 @@ def test_ci_installs_the_authoritative_python_environment() -> None:
install_script = next(
step for step in steps if step.get("name") == "Install dependencies"
)["run"]
test_script = next(
step for step in steps if step.get("name") == "Run tests"
)["run"]
test_script = next(step for step in steps if step.get("name") == "Run tests")["run"]

assert 'python -m pip install -e ".[dev,youtube]"' in install_script
assert "--timeout=120" in test_script
Expand Down Expand Up @@ -109,11 +111,10 @@ def test_ci_runs_supported_python_matrix_with_immutable_actions() -> None:
assert test_job.get("if") is None
assert test_job["strategy"] == {
"fail-fast": False,
"matrix": {"python-version": ["3.10", "3.11", "3.12"]},
"matrix": {"python-version": ["3.11", "3.12"]},
}
python_versions = test_job["strategy"]["matrix"]["python-version"]
assert python_versions == [
"3.10",
"3.11",
"3.12",
]
Expand All @@ -130,13 +131,20 @@ def test_ci_runs_supported_python_matrix_with_immutable_actions() -> None:
assert test_script == (
"PYTHONPATH=src python -m pytest tests/unit/ -v --timeout=120 "
"--cov=src/youtube_extension --cov-fail-under=0 "
"--ignore=tests/unit/test_transcript_action_workflow.py -k \"not integration\""
'--ignore=tests/unit/test_transcript_action_workflow.py -k "not integration"'
)
assert not test_job.get("continue-on-error", False)
assert not run_tests.get("continue-on-error", False)
for suppression in ("|| true", "set +e", "--collect-only"):
assert suppression not in test_script

project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
assert project["requires-python"] == ">=3.11,<3.13"
assert "Pillow>=13.0.0" in project["dependencies"]

dockerfile = (ROOT / "Dockerfile").read_text()
assert dockerfile.count("FROM python:3.11-slim") == 2

immutable_action = re.compile(r"^[^@\s]+@[0-9a-f]{40}$")
immutable_container = re.compile(r"^docker://[^@\s]+@sha256:[0-9a-f]{64}$")
immutable_image = re.compile(r"^[^@\s]+@sha256:[0-9a-f]{64}$")
Expand Down Expand Up @@ -249,7 +257,6 @@ def test_controller_does_not_claim_an_unavailable_live_lane() -> None:
assert "## Jules reporting requirement" not in source



def test_gh_aw_validation_pins_runtime_version() -> None:
workflow = _load_yaml(ROOT / ".github/workflows/gh-aw-validation.yml")
actions_lock = json.loads((ROOT / ".github/aw/actions-lock.json").read_text())
Expand All @@ -259,7 +266,9 @@ def test_gh_aw_validation_pins_runtime_version() -> None:
setup_cli_entry = actions_lock["entries"]["github/gh-aw-actions/setup-cli@v0.88.7"]
assert setup_entry["sha"] == "5e508589e03a7757a7e05b26e834292f5445bfb6"
assert setup_cli_entry["sha"] == "5e508589e03a7757a7e05b26e834292f5445bfb6"
step_scripts = [step.get("run", "") for step in workflow["jobs"]["validate-gh-aw"]["steps"]]
step_scripts = [
step.get("run", "") for step in workflow["jobs"]["validate-gh-aw"]["steps"]
]
combined = "\n".join(step_scripts)
assert "gh extension install github/gh-aw --pin v0.88.7" in combined
assert "eventrelay-ci-investigator" not in combined
Expand All @@ -282,8 +291,13 @@ def test_pr_iteration_selection_does_not_bypass_ranked_priority() -> None:
workflow_source = (ROOT / ".github/workflows/pr-iteration-loop.md").read_text()

assert 'payload.selected = {\n kind: "issue",' not in workflow_source
assert 'payload.selected = {\n kind: "pull_request",' not in workflow_source
assert "recentFailingRuns[0] || stalePulls[0] || staleIssues[0] || null" in workflow_source
assert (
'payload.selected = {\n kind: "pull_request",' not in workflow_source
)
assert (
"recentFailingRuns[0] || stalePulls[0] || staleIssues[0] || null"
in workflow_source
)


def test_pr_iteration_push_rule_does_not_require_ai_title_prefix() -> None:
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/test_unified_ai_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ async def test_unified_request_uses_anthropic_client(self):
assert result.content == "alpha\nbeta"
assert result.provider == "claude"
assert result.tokens_used == 33
call_kwargs = sdk._anthropic_client.messages.create.await_args.kwargs
assert call_kwargs == {
"model": "claude-opus-4-8",
"max_tokens": 4000,
"messages": [{"role": "user", "content": "Summarize this"}],
"timeout": 60.0,
"thinking": {"type": "adaptive"},
}
assert not {"temperature", "top_p", "top_k"} & call_kwargs.keys()

@pytest.mark.asyncio
@pytest.mark.parametrize("model", ["gemini-2.5-flash", "gemini-3.8-flash"])
Expand Down
Loading
Loading