## Description Makes PR workflow runs more selective by routing docs-only changes to docs validation instead of the full CI workflow, while preserving workflow validation and existing code/e2e/release gates for applicable changes. ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [x] Code refactoring (no functional changes) ## Changes Made - Added `pull_request.paths-ignore` to `.github/workflows/ci.yml` so docs/wiki/markdown-only PRs do not queue the general CI workflow. - Removed `.github/workflows/ci.yml` from the CI internal `code` path filter so CI-only workflow edits can run workflow validation without forcing Python/Rust code jobs. - Added a docs PR validation job to `.github/workflows/docs.yml` for `docs/**`, `wiki/**`, `mkdocs.yml`, and docs workflow changes. - Reduced default docs workflow token permissions to `contents: read`, with `contents: write` scoped only to the deploy job. - Added docs workflow dry-runs to `scripts/validate-workflows.sh` so local/CI workflow validation covers the new PR and manual docs paths. ## Testing - [ ] Unit tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [x] Manual testing performed ### Test Output ```text $ actionlint .github/workflows/ci.yml .github/workflows/docs.yml # no output $ act pull_request -W .github/workflows/docs.yml -n *DRYRUN* [Deploy Documentation/validate] 🏁 Job succeeded $ act workflow_dispatch -W .github/workflows/docs.yml -n *DRYRUN* [Deploy Documentation/deploy] 🏁 Job succeeded $ act pull_request -W .github/workflows/ci.yml -n *DRYRUN* [CI/changes] 🏁 Job succeeded *DRYRUN* [CI/commitlint] 🏁 Job succeeded $ python -m mkdocs build INFO - Documentation built in 1.28 seconds $ bash scripts/validate-workflows.sh # completed successfully; act dry-runs passed. Some unsupported runner-platform matrix entries are skipped by local act, as before. $ git diff --check # no output ``` ## Real Behavior Proof - Environment: Windows local checkout, branch `smart-pr-runs`, `act` 0.2.87, temporary local `actionlint` installed via `go install`. - Exact command / steps: Ran `actionlint` against changed workflows, `act` dry-runs for docs PR/manual paths and CI PR path, actual `python -m mkdocs build`, full `scripts/validate-workflows.sh`, and `git diff --check`. - Observed result: Changed workflows lint cleanly; docs PR and manual docs workflow paths dry-run successfully; CI PR dry-run still covers `changes` and `commitlint`; MkDocs builds; repository workflow validation script completes with the new docs dry-runs included. - Not tested: Full non-dry-run GitHub Actions execution on hosted runners before PR creation. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A. ## Additional Notes - No issue is linked because this PR was not opened for a specific tracked issue. - `mkdocs build` reports existing docs/nav warnings but exits successfully; strict mode currently fails on existing warnings, so the PR validation uses the deploy-compatible non-strict build. - Python unit/lint/type checks are not applicable to this workflow-only change. |
||
|---|---|---|
| .. | ||
| fixtures | ||
| tests | ||
| audit_wheel_glibc_symbols.py | ||
| build_rust_extension.sh | ||
| changelog-gen.py | ||
| export_kompress_v2_onnx.py | ||
| install-git-hooks.sh | ||
| install.ps1 | ||
| install.sh | ||
| pr-governance.py | ||
| README.md | ||
| record_fixtures.py | ||
| refresh_model_limits.sh | ||
| replay_codex_ws_load.py | ||
| repro_codex_replay.py | ||
| smoke_issue_327.py | ||
| sync-plugin-versions.py | ||
| validate-workflows.sh | ||
| verify-versions.py | ||
| version-sync.py | ||
scripts/
Utility scripts bundled with the Headroom repo. Most are one-off operator tools; a few are runnable as part of development workflows.
Reproducing the reconnect storm
repro_codex_replay.py reproduces the multi-agent Codex reconnect/retry storm
against a local Headroom proxy (default http://127.0.0.1:8787), as described
in wiki/plans/2026-04-17-codex-proxy-runtime-analysis.md under "Latest
Correction". Use it to:
- Regression-check that
/livezstays responsive under a cold-start storm. - Empirically tune the Unit 4 pre-upstream semaphore default
(
HEADROOM_ANTHROPIC_PRE_UPSTREAM_CONCURRENCY). - Exercise the Codex WS lifecycle + Anthropic HTTP path simultaneously without needing to replay captured production traffic.
Run
# Default: 8 WS + 4 HTTP clients, 30s storm, p99 /livez must stay <= 500ms.
python scripts/repro_codex_replay.py
# Tighter budget, shorter run:
python scripts/repro_codex_replay.py \
--url http://127.0.0.1:8787 \
--ws-clients 16 \
--anthropic-clients 8 \
--duration 60 \
--livez-threshold-ms 100
# Dump the full summary as JSON for downstream tooling:
python scripts/repro_codex_replay.py --json
Exit code:
0— warmup succeeded (or was skipped), storm ran for the requested duration, and/livezp99 stayed under--livez-threshold-ms.1— soft assertion failed, proxy unreachable, or unhandled exception. Proxy-unreachable is detected and reported within ~5 seconds.
Fixtures
The script loads two hand-crafted, fully synthetic JSON fixtures:
scripts/fixtures/anthropic_replay_body.json— shape of a large agent reconnect replay/v1/messages?beta=truePOST body.scripts/fixtures/codex_response_create_frame.json— first Codex WS frame with the{"type": "response.create", "response": {...}}envelope.
Override via --ws-frame-fixture / --anthropic-body-fixture if you have
captured traffic to replay instead.
Interpretation
/livez p99under threshold means the event loop is not starved during the storm. If it rises with the semaphore unbounded (HEADROOM_ANTHROPIC_PRE_UPSTREAM_CONCURRENCY=10000) and drops back under the default, Unit 4's backpressure is working.Codex WS: openedshould equal--ws-clients.response.completedtypically stays low when upstream auth isn't configured locally — the goal is handshake + relay wiring, not real upstream traffic.Anthropic HTTP: ok_2xx + non_2xx + timed_out + errorsshould roughly equalattempted. Sustained non-zerotimed_outduring the storm is the failure signal the plan targets.
A smoke test at tests/test_scripts/test_repro_codex_replay_smoke.py
exercises the script against a mock FastAPI server on every PR.
Install scripts
install.sh— POSIX installer.install.ps1— Windows PowerShell installer.
These are generated by the release pipeline; edit with care.