Phase 6: enforce the quality gate in CI and export the V4.7 review log
Quality Gate / gate (push) Successful in 33s

Adds .github/workflows/quality-gate.yml, running the gate on push and pull
request. CI invokes `pre-commit run --all-files` rather than restating the
`ruff check` and `ty check` commands, so the checks keep a single definition
in .pre-commit-config.yaml and local and CI cannot drift (plan task 2).

The workflow writes a .env file rather than exporting an environment
variable. The two are not equivalent here: Settings reads the .env file,
while the external-test skip guard reads os.getenv, so an exported variable
un-skips the external tests and sends them to the network. Measured in CI:
no .env gave 115 failures and 18 errors, an exported dummy key gave 3
failures, and a written .env file reproduced the local baseline exactly.

Negative-tested on a scratch branch: a deliberate lint error failed the run
at `ruff check` with exactly the planted errors, confirming the gate blocks
rather than merely reporting (plan task 4). The subsequent clean run passed
ruff and ty and reported 295 passed, 4 skipped, matching local and
confirming the four credential-gated tests skip cleanly (plan task 3).

That first green run caught a real platform-dependent defect. PromptStore
rejected non-direct-child names via `Path(name).name != name`, which is
platform-dependent: on POSIX a backslash is an ordinary filename character,
so "nested\prompt.md" passed the guard and failed later as NOT_FOUND rather
than VALIDATION. Windows cannot reproduce it. No traversal was possible,
since the path.parent != root check still held, so the impact was a wrong
error category and a red gate. Both separators are now rejected explicitly,
matching the ^[^/\\]+$ pattern config.PromptFilename already used.

Also exports docs/ver4.7/review_log_v4_7.md, the working record kept across
all six phases: 50 entries, 1 still open. The open entry is a pre-existing
/ui redirect defect found during the Phase 3 UI walk and deliberately left
unfixed as outside the V4.7 scope boundary.

Co-authored-by: Copilot App <[email protected]>
This commit is contained in:
zoltan57
2026-08-18 23:05:52 -05:00
co-authored by Copilot App
parent fca959fa5d
commit edcfba9cb2
3 changed files with 463 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
name: Quality Gate
# V4.7 Phase 6 / review log [40]. Before this, ruff, ty and pytest were enforced
# only by .pre-commit-config.yaml, and only for developers who had actually run
# `pre-commit install`.
on:
push:
pull_request:
jobs:
gate:
runs-on: ubuntu-latest
steps:
- name: Check out the commit under test
uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Install dependencies from the lockfile
# --locked fails if uv.lock has drifted from pyproject.toml, so a stale
# lockfile is caught here rather than producing an untested dependency set.
run: uv sync --locked
- name: Write placeholder configuration
# Settings requires openrouter_api_key and 115 tests cannot construct
# Settings without it. This is written to a .env file rather than exported
# as an environment variable on purpose: the external tests guard on
# os.getenv("OPENROUTER_API_KEY"), which reads the process environment and
# not the file, so writing the file reproduces the local result exactly -
# the 4 external tests skip instead of running against a fake key and
# failing. Exporting it instead produces 3 failures.
run: echo "OPENROUTER_API_KEY=ci-placeholder-not-a-real-key" > .env
- name: Lint and type check
# Runs the hooks defined in .pre-commit-config.yaml instead of repeating
# "ruff check" and "ty check" here. The commands then have one definition,
# so the local and CI gates cannot drift apart.
run: uv run pre-commit run --all-files --show-diff-on-failure
- name: Tests
run: uv run pytest