Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
7a0b671
fix(proxy): durably recover hard HTTP bridge operations (#1657)
shaqman Aug 12, 2026
7c46719
fix(http-bridge): keep idle retirements out of retry circuit (#1677)
leventov Aug 12, 2026
6509dd0
feat(reset-credits): add refresh scheduler enable toggle (#1701)
Soju06 Aug 12, 2026
debd7cf
feat(telemetry): anonymous usage telemetry with informed opt-out cons…
Soju06 Aug 12, 2026
2164b8c
fix(dashboard): pin web asset MIME types against poisoned OS registri…
Soju06 Aug 13, 2026
c3f0c56
docs(openspec): archive 90 landed changes and sync their specs (#1713)
Soju06 Aug 14, 2026
e439043
fix(dashboard): distinguish first-run empty states from filter mismat…
mastertyko Aug 14, 2026
db5776c
fix(chat): omit unset tools on mapped Responses payloads (#1725)
mastertyko Aug 14, 2026
b50cb86
fix(http-bridge): refuse foreign claims on live DRAINING leases (#1722)
mastertyko Aug 14, 2026
64da340
fix(proxy): keep stream idle timeouts account-neutral (#1718)
mastertyko Aug 14, 2026
3093203
fix(proxy): settle compact failover before account health (#1717)
mastertyko Aug 14, 2026
a85f71d
fix(proxy): close non-stream chat collect and map error status (#1712)
mastertyko Aug 14, 2026
3159ebe
fix(proxy): sweep idle bridge sessions without request traffic (#1747)
Soju06 Aug 14, 2026
560fb50
docs(proxy): document cluster-wide account cap partitioning (#1750)
Soju06 Aug 14, 2026
7148810
fix(cache): keep an aborted invalidation bump queued (#1748)
Soju06 Aug 14, 2026
6464e96
feat(db): report SQLite write transactions that outlive the busy time…
Soju06 Aug 14, 2026
2c0dc5b
fix(proxy): fence successor bridge claims against the retiring predec…
Soju06 Aug 14, 2026
0031e3d
fix(proxy): add explicit Daybreak capability routing (#1742)
mastertyko Aug 14, 2026
e34db2d
fix(review): keep Codex review sessions resumable (#1678)
leventov Aug 14, 2026
b43d0c8
fix(accounts): recover Free accounts after reset (#1700)
HulianBuligon Aug 15, 2026
f694c44
fix(proxy): keep file-pin owner on soft 1011 reconnect (#1761)
mastertyko Aug 15, 2026
2cd52e4
fix(proxy): persist file ownership across replicas (#1521)
mastertyko Aug 15, 2026
35bbb00
fix(proxy): scope backend Codex affinity by thread identity (#1703)
leventov Aug 15, 2026
5dc6081
fix(http-bridge): preserve goal-restart recovery across reconnects (#…
leventov Aug 15, 2026
ef9c68e
fix(dashboard): preserve cancelled request count (#1766)
mastertyko Aug 15, 2026
5f2f726
fix(dashboard): show cancelled request logs (#1769)
mastertyko Aug 16, 2026
e359d49
fix(dashboard): surface upstream route metadata (#1767)
mastertyko Aug 16, 2026
8488bc4
fix(models): correct GPT-5.6 context windows (#1691)
kidclone3 Aug 16, 2026
4ace71e
fix(auth): guard the refresh singleflight negative cache by successor…
Komzpa Aug 16, 2026
a60ef9a
test(proxy): add property tests for response payload invariants (#1699)
iqbalmaulana03 Aug 16, 2026
5780a27
fix(http-bridge): dedupe retry circuit failures per send (#1743)
choi138 Aug 16, 2026
17ae866
fix(proxy): abandon unavailable owner on thread-scoped goal restart (…
mastertyko Aug 16, 2026
34ef7b2
fix(proxy): do not rewrite thread locality for a file-pin owner (#1765)
mastertyko Aug 16, 2026
3f66c28
fix(usage): settle live snapshots after account consolidation (#1773)
mastertyko Aug 16, 2026
f92bc90
fix(proxy): normalize single-account warmup failures (#1774)
mastertyko Aug 16, 2026
4e48f35
fix(proxy): settle terminal spool append failures (#1775)
mastertyko Aug 16, 2026
ff89bc4
chore(github): retire codex review label gate in favor of CodeRabbit …
Soju06 Aug 16, 2026
6ff51cd
fix(proxy): hold fenced hard turns through cooldown (#1739)
kevinsslin Aug 16, 2026
57618c8
fix(proxy): stop abandoning an unresolved inflight session-creation f…
Komzpa Aug 16, 2026
5856115
chore(tooling): run Makefile Python scripts through uv (#1741)
kevinsslin Aug 16, 2026
fd97cb8
fix(proxy): separate websocket scope cleanup budget (#1723)
kevinsslin Aug 16, 2026
08b84a9
fix(proxy): route source-owned models off the WebSocket transport (#1…
Borealin Aug 16, 2026
0c8d921
feat(proxy): report websocket cleanup phase (#1726)
kevinsslin Aug 16, 2026
6cf7e61
fix(proxy): complete disconnect cleanup — pool leak, charged reservat…
Komzpa Aug 16, 2026
5d27f7f
fix(dashboard): show cancellation totals in reports (#1772)
mastertyko Aug 16, 2026
f1c8d5c
feat(model-sources): advertise operator-declared reasoning efforts (#…
Borealin Aug 16, 2026
138aa9f
feat(ui): customize dashboard request-log columns (#1503)
BrenticusMaximus Aug 16, 2026
6c97ad6
fix(proxy): keep abrupt eventless websocket drops account-neutral (#1…
Soju06 Aug 17, 2026
9eedb2c
fix(db): bound wedged SQLite session teardown and reclaim the connect…
Soju06 Aug 17, 2026
2e4a580
perf(proxy): disable permessage-deflate on direct-egress upstream web…
Soju06 Aug 17, 2026
94057cc
perf(middleware): convert BaseHTTPMiddleware layers to pure ASGI (#1787)
Soju06 Aug 17, 2026
120af75
fix(compact): recover previous-response-pinned compaction from quota-…
Soju06 Aug 17, 2026
076aab8
perf: coalesce same-owner sticky session TTL refresh upserts (#1790)
Soju06 Aug 17, 2026
6ff22e0
docs(dashboard): clarify routing, sticky affinity, quota thresholds, …
Soju06 Aug 17, 2026
8d265c3
fix(server): serve h2c upgrade offers as plain HTTP/1.1 instead of re…
Soju06 Aug 17, 2026
539cf93
fix(db): add postgres shm_size and raise default pool headroom (#1791)
Soju06 Aug 17, 2026
d4c43ef
perf(dashboard): cap projections bulk usage-history read per account …
Soju06 Aug 17, 2026
c1caa44
perf(accounts): bound the account-listing live tail with a 2h fold la…
Soju06 Aug 17, 2026
d4f9e23
perf(accounts): make account deletion a fast mark + background batch …
Soju06 Aug 17, 2026
66fd103
fix(usage): fence leaked live-usage-ingestor tasks and settle their f…
Soju06 Aug 17, 2026
0a4c0a1
fix(docker): upgrade util-linux family in runtime image for CVE-2026-…
Soju06 Aug 17, 2026
9d9f099
perf(proxy): validate stream payloads only for lifecycle events (#1784)
Soju06 Aug 17, 2026
7dacb04
perf(api-keys,proxy): shape ORM hot-path queries (#1788)
Soju06 Aug 17, 2026
980572e
perf(proxy): relay unmodified SSE frames verbatim (#1785)
Soju06 Aug 17, 2026
8a2d066
perf(api-keys): skip usage reservations when no limit applies (#1789)
Soju06 Aug 17, 2026
b26df41
chore: release v1.24.0-beta.1 (#1704)
Soju06 Aug 17, 2026
4a3832a
chore(ci): bump astral-sh/setup-uv from 9.0.0 to 10.0.1 (#1802)
dependabot[bot] Aug 18, 2026
a4fa123
chore(deps): bump the python-minor-patch group with 10 updates (#1806)
dependabot[bot] Aug 18, 2026
9bca5e5
chore(deps): bump openai from 2.53.0 to 3.0.0 (#1807)
dependabot[bot] Aug 18, 2026
de7b3bc
chore(docker): bump astral-sh/uv from 0.12.3 to 0.12.5 (#1803)
dependabot[bot] Aug 18, 2026
a0406ba
chore(deps): bump the frontend-minor-patch group (#1804)
dependabot[bot] Aug 18, 2026
d1f24e0
chore(ci): bump github/codeql-action/upload-sarif from 4.37.6 to 4.37…
dependabot[bot] Aug 18, 2026
ed31b7d
feat(api-keys): allow per-key reasoning effort policies (#1642)
yshishenya Aug 18, 2026
d57bf3a
test(proxy): keep eventless anchored bridge replay fail-closed (#1736)
kevinsslin Aug 18, 2026
1f65f80
feat(reports): Add API Key Filtering to Reports Dashboard (#1728)
SAKTHIMARAN-VENOM Aug 18, 2026
0481ed9
fix(proxy): compact transport switch + trigger canonicalization (supe…
Soju06 Aug 18, 2026
d522a4d
feat(proxy): support Ultrafast service tier (#1734)
evan-choi Aug 18, 2026
8e7589e
feat(ui): surface reasoning token usage (#1801)
chaoxu Aug 18, 2026
1add104
fix(models): raise GPT-5.6 bootstrap max_context_window to 872k (#1813)
zenasharp Aug 19, 2026
812265d
fix(proxy): drop malformed compact item ids (#1815)
yshishenya Aug 19, 2026
3af2dff
chore(openspec): archive GPT-5.6 context-window changes in dependency…
Soju06 Aug 19, 2026
eeab46a
fix(proxy): classify parameterless previous response errors (#1818)
rknightion Aug 19, 2026
f839952
chore: release v1.24.0-beta.2 (#1810)
Soju06 Aug 19, 2026
3381938
fix(proxy): bind account-bound retries to dispatch owner (#1829)
mastertyko Aug 20, 2026
6ba083d
fix(proxy): reject truncated chat completion streams (#1833)
mastertyko Aug 20, 2026
bd67c64
fix(proxy): retain image reservation recovery ownership (#1822)
mastertyko Aug 20, 2026
68892e7
fix(warmup): warm paid-to-free transitions (#1825)
HulianBuligon Aug 20, 2026
4d0f0ff
feat(model-sources): embeddings source capability (#1776)
Komzpa Aug 20, 2026
8abd507
fix(helm): bind TTFT dashboard SQL datasource (#1827)
mastertyko Aug 20, 2026
028a75c
fix(reports): format full Cost values with grouping separators (#1814)
hanseo0507 Aug 20, 2026
78d63e5
fix(proxy): preserve compact terminal error type (#1824)
mastertyko Aug 20, 2026
d148dd9
feat(config): timeout-invariant linter — validate deadline/TTL inequa…
Komzpa Aug 20, 2026
cab5032
chore(repo): drop root agent-debris files and enforce a root-file all…
Soju06 Aug 20, 2026
c750dcf
fix(models): apply context-window overrides to /v1 input context fiel…
yeongjun-cigro Aug 20, 2026
bffc6d9
test(oauth): make test_oauth_flow order-independent by fencing the sh…
Soju06 Aug 20, 2026
01f089c
fix(http-bridge): classify recovery error frames and poison same-anch…
Soju06 Aug 20, 2026
ed2c94d
fix(proxy): O(1) shared-future admission waits + event-loop lag watch…
Soju06 Aug 20, 2026
1541ee8
feat(telemetry): report consent state and send a decision-time opt-ou…
Soju06 Aug 20, 2026
1ecb51d
chore: release v1.24.0-beta.3 (#1834)
Soju06 Aug 20, 2026
eab7155
feat(frontend): configure model-source reasoning efforts (#1848)
Komzpa Aug 20, 2026
c597226
fix(proxy): absorb replay-safe compaction recovery (#1849)
Komzpa Aug 20, 2026
25d6374
fix(proxy): report suppressed duplicate tool-call terminals (#1706)
Komzpa Aug 20, 2026
b6c217f
fix(db): repair retired identity/warmup migration stamp (#1847)
Komzpa Aug 20, 2026
5e1f568
fix(proxy): demote quarantined bridge reattach keys (#1730)
Komzpa Aug 20, 2026
52092bc
fix(proxy): guard model-transition owner-conflict fork (#1619)
Komzpa Aug 20, 2026
d4b00fd
Revert five squash merges landed outside the merge triage round (#1858)
Komzpa Aug 20, 2026
798203f
fix(proxy): wait on usage-refresh singleflight without asyncio.shield…
Soju06 Aug 24, 2026
b311aea
chore: release v1.24.0-beta.4 (#1851)
Soju06 Aug 24, 2026
cedc05f
chore(deps): bump the python-minor-patch group with 7 updates (#1915)
dependabot[bot] Aug 26, 2026
84fde5a
chore(main): release 1.24.0 (#1692)
Soju06 Aug 26, 2026
80eca0a
chore(upstream): sync stable v1.24.0
hongzexin Aug 26, 2026
4ea1244
docs(contributors): add hongzexin attribution
hongzexin Aug 26, 2026
20aec3c
chore(upstream): defer release-managed version promotion
hongzexin Aug 26, 2026
e4de8fd
fix(security): sanitize upgrade-path diagnostics
hongzexin Aug 26, 2026
6906865
fix(security): close remaining CodeQL findings
hongzexin Aug 26, 2026
90e6fe8
fix(security): document intentional compatibility boundaries
hongzexin Aug 26, 2026
24a5b94
fix(security): bound compact response and image diagnostics
hongzexin Aug 26, 2026
9fdd389
fix(compact): preserve model in normalized responses
hongzexin Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 7 additions & 1 deletion .agents/skills/codex-review-loop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ Launch the adversarial review as a background process.
cat <prompt-file> | bash <skill-dir>/scripts/codex-subagent.sh --uncommitted
```
2. Inform the user the review is running (~20-50 min).
3. The script parses Codex output and returns the final review text.
3. The script parses Codex output and returns the final review text. Review
rollouts intentionally remain persistent. If terminal output is lost, use
`codex resume --include-non-interactive` to locate the review, or
`codex resume <SESSION_ID>` when its ID is known. Do not add
`--ephemeral` to the wrapper. The wrapper also relies on the configured
non-interactive approval/sandbox policy because Codex CLI 0.147.0 removed
the historical `--full-auto` argument from `exec review`.

### Error handling

Expand Down
6 changes: 5 additions & 1 deletion .agents/skills/codex-review-loop/scripts/codex-subagent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ while [[ $# -gt 0 ]]; do
esac
done

CODEX_ARGS+=("--full-auto" "--ephemeral")
# Reviews commonly outlive the invoking terminal or exceed its output cap, so
# do not add `--ephemeral`: the persistent rollout is the recovery path after
# either failure. Also do not restore the historical `--full-auto` argument;
# Codex CLI 0.147.0 removed it from `exec review`, which already uses the
# configured non-interactive approval and sandbox policy.

# --- Model overrides ---
if [[ -n "${CODEX_REVIEW_MODEL:-}" ]]; then
Expand Down
92 changes: 91 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,96 @@
"code"
]
},
{
"login": "kidclone3",
"name": "DuyBui",
"avatar_url": "https://avatars.githubusercontent.com/u/54184969?v=4",
"profile": "https://github.com/kidclone3",
"contributions": [
"code",
"test"
]
},
{
"login": "kevinsslin",
"name": "Kevin Lin",
"avatar_url": "https://avatars.githubusercontent.com/u/86810837?v=4",
"profile": "https://github.com/kevinsslin",
"contributions": [
"code",
"test"
]
},
{
"login": "Borealin",
"name": "Borealin",
"avatar_url": "https://avatars.githubusercontent.com/u/41241077?v=4",
"profile": "https://github.com/Borealin",
"contributions": [
"code",
"test"
]
},
{
"login": "BrenticusMaximus",
"name": "BrenticusMaximus",
"avatar_url": "https://avatars.githubusercontent.com/u/32489248?v=4",
"profile": "https://github.com/BrenticusMaximus",
"contributions": [
"code",
"test"
]
},
{
"login": "sakthimaran-venom",
"name": "Sakthimaran",
"avatar_url": "https://avatars.githubusercontent.com/u/233523816?v=4",
"profile": "https://github.com/sakthimaran-venom",
"contributions": [
"code",
"test"
]
},
{
"login": "evan-choi",
"name": "Evan",
"avatar_url": "https://avatars.githubusercontent.com/u/9690415?v=4",
"profile": "https://github.com/evan-choi",
"contributions": [
"code"
]
},
{
"login": "chaoxu",
"name": "Chao Xu",
"avatar_url": "https://avatars.githubusercontent.com/u/18860?v=4",
"profile": "https://chaoxu.prof/",
"contributions": [
"code",
"test"
]
},
{
"login": "zenasharp",
"name": "zenasharp",
"avatar_url": "https://avatars.githubusercontent.com/u/170236008?v=4",
"profile": "https://github.com/zenasharp",
"contributions": [
"code",
"test",
"doc"
]
},
{
"login": "hanseo0507",
"name": "HanSu Lee",
"avatar_url": "https://avatars.githubusercontent.com/u/56479293?v=4",
"profile": "https://github.com/hanseo0507",
"contributions": [
"code",
"test"
]
},
{
"login": "hongzexin",
"name": "Jason HONG",
Expand All @@ -1231,7 +1321,7 @@
"contributions": [
"code",
"test",
"doc"
"maintenance"
]
}
],
Expand Down
23 changes: 9 additions & 14 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ PR titles must follow the same format — that's the title release-please reads.
2. Make atomic commits with Conventional Commit titles.
3. Run the lint/test gate locally (see above).
4. Open a PR using the template. Link the relevant issue.
5. Codex Review (and a human maintainer) will review. Address feedback by
5. CodeRabbit (and a human maintainer) will review. Address feedback by
pushing follow-up commits — no force-pushing during active review.
6. Once approved and CI is green, a maintainer squash-merges with a clean
Conventional Commits title.
Expand All @@ -215,16 +215,11 @@ Before a PR is squash-merged into `main`:
`CI Required` check is the branch-protection check to require: it
depends on every CI job and also runs for merge queue synthetic merge
groups, so a stale PR head cannot bypass a broken merge result.
2. **`@codex review` must be clean — or its findings addressed — on the
merge-target head.** Every PR triggers `@codex review` at least once
against the head that's about to be merged. Local `codex review
--base origin/main` runs are encouraged but don't substitute for the
cloud review (the cloud `@codex review` reliably catches things the
local run misses).
The `🤖 codex: ok` label is maintained by the trusted
`Codex review labels` workflow from current-head CI and current-head
Codex review evidence. Treat the label as an audit aid, not as a
substitute for branch protection or merge queue checks.
2. **Actionable CodeRabbit findings must be fixed or explicitly addressed
or dismissed in-thread on the merge-target head.** Review the current-head
CodeRabbit findings before merging; no finding may be silently skipped.
Local `codex review --base origin/main` runs remain an encouraged extra
tool, but they are not a merge gate and do not substitute for CodeRabbit.
- **P1 findings**: fix in the PR, or justify in-thread with a short
write-up of why the finding doesn't apply. No silent skipping.
- **P2 findings**: fix in the PR, or open a follow-up issue and link
Expand Down Expand Up @@ -297,7 +292,7 @@ self-merge escape hatch applies:

- If a collaborator's PR has been waiting on a maintainer merge for
**more than 14 days** with **all merge gates met** (CI green,
`@codex review` clean or findings addressed, `mergeable=CLEAN`, no
CodeRabbit findings addressed, `mergeable=CLEAN`, no
outstanding requested-changes review, no objection from any other
active collaborator in the thread), the PR author may self-merge.
- Self-merge under this clause **must** include a comment on the PR
Expand All @@ -311,8 +306,8 @@ self-merge escape hatch applies:

These rules are intentionally lightweight. They don't require:

- A second human reviewer in addition to `@codex review` for every PR.
Codex review + the PR author + a maintainer merge is the baseline.
- A second human reviewer in addition to CodeRabbit for every PR.
CodeRabbit review + the PR author + a maintainer merge is the baseline.
- Squash-merge commit message rewriting beyond the Conventional Commits
title. The PR description ends up in the body; that's enough.
- A formal escalation process for disagreements. If a P1 finding is
Expand Down
2 changes: 1 addition & 1 deletion .github/release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.23.0"
".": "1.24.0"
}
71 changes: 66 additions & 5 deletions .github/scripts/check_simplicity_budgets.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python3
"""Enforce simplicity budgets on README, .env.example, and the dashboard core nav.
"""Enforce simplicity budgets on README, .env.example, the dashboard core nav, and the tracked root tree.

Budgets live in .github/simplicity-budgets.toml and are enforced by
.github/workflows/simplicity-budgets.yml. Intentionally stdlib-only so it runs
on the runner's python3 before project dependencies are installed.
on the runner's python3 before project dependencies are installed; the
[root_files] check additionally shells out to `git ls-tree` against the
checkout's HEAD.

Override: the 'simplicity-budget-approved' PR label (passed in via the
PR_LABELS env var as a JSON array of label names) downgrades violations to
Expand All @@ -12,15 +14,16 @@

Exit codes: 0 = within budget (or overridden), 1 = over budget,
2 = configuration error (the budget config is missing or malformed, a
budgeted file or the nav array is missing, or an ALL-CONTRIBUTORS-LIST
block is opened but never closed).
budgeted file or the nav array is missing, the tracked root tree cannot
be listed, or an ALL-CONTRIBUTORS-LIST block is opened but never closed).
"""

from __future__ import annotations

import json
import os
import re
import subprocess
import sys
import tomllib
from pathlib import Path
Expand Down Expand Up @@ -129,6 +132,34 @@ def count_nav_items(path: Path, array: str) -> int:
return len(re.findall(r"\bto:\s*[\"']", match.group("body")))


def _escape_annotation_value(value: str) -> str:
"""Escape a contributor-controlled value for a workflow-command line ('%' first, per Actions rules)."""
for char, escape in (("%", "%25"), ("\r", "%0D"), ("\n", "%0A"), (":", "%3A"), (",", "%2C")):
value = value.replace(char, escape)
return value


def list_tracked_root_entries() -> list[str]:
"""List tracked repository-root entries from HEAD; exit 2 loudly if git cannot."""
try:
proc = subprocess.run(
["git", "ls-tree", "--name-only", "-z", "HEAD"],
capture_output=True,
encoding="utf-8",
# Non-UTF-8 filename bytes become \x escapes: they can never match
# an allowlist entry, so they surface as a named violation instead
# of a decode crash.
errors="backslashreplace",
check=True,
)
except FileNotFoundError:
_config_error("[root_files] git executable not found; the root-entry budget needs a git checkout")
except subprocess.CalledProcessError as exc:
detail = (exc.stderr or "").strip() or f"exit code {exc.returncode}"
_config_error(f"[root_files] 'git ls-tree --name-only HEAD' failed: {detail}")
return [entry for entry in proc.stdout.split("\0") if entry]


def _override_labels() -> list[str]:
raw = os.environ.get("PR_LABELS") or "[]"
try:
Expand Down Expand Up @@ -164,9 +195,26 @@ def main() -> int:
except (KeyError, TypeError, ValueError) as exc:
_config_error(f"budget config '{CONFIG_PATH}' is missing or has a malformed section/key: {exc!r}")

# [root_files] is optional: absent means the root-entry budget is not
# enforced (older configs keep working), present-but-malformed is a
# config error like any other section.
root_allowed: set[str] | None = None
root_cfg = config.get("root_files")
if root_cfg is not None:
try:
allowed_entries = root_cfg["allowed"]
except (KeyError, TypeError) as exc:
_config_error(f"budget config '{CONFIG_PATH}' has a malformed [root_files] section: {exc!r}")
if not isinstance(allowed_entries, list) or not all(isinstance(entry, str) for entry in allowed_entries):
_config_error(f"budget config '{CONFIG_PATH}' [root_files] 'allowed' must be an array of strings")
root_allowed = set(allowed_entries)

readme_lines = strip_contributors_block(_read_lines(readme_path, "readme"))
env_lines = _read_lines(env_path, "env_example")
nav_items = count_nav_items(nav_path, nav_array)
unexpected_root_entries: list[str] = []
if root_allowed is not None:
unexpected_root_entries = sorted(set(list_tracked_root_entries()) - root_allowed)

metrics: list[tuple[str, Path, int, int]] = [
(
Expand All @@ -192,12 +240,25 @@ def main() -> int:
if actual > budget:
violations.append((name, path, actual, budget))

if not violations:
if root_allowed is not None:
status = "OK" if not unexpected_root_entries else "OVER"
print(f"tracked root entries outside allowlist: {len(unexpected_root_entries)}/0 {status}")

if not violations and not unexpected_root_entries:
return 0

annotation = "warning" if overridden else "error"
for name, path, actual, budget in violations:
print(f"::{annotation} file={path}::simplicity budget exceeded: {name}: {actual} > {budget}")
for entry in unexpected_root_entries:
# Entry names come from the tree, not the trusted config: escape them
# so a crafted filename cannot break or forge workflow-command lines.
shown = _escape_annotation_value(entry)
print(
f"::{annotation} file={shown}::simplicity budget exceeded: tracked root entry '{shown}' is not in "
f"the [root_files] allowlist — add it to {CONFIG_PATH} in the same diff, or a maintainer applies "
f"the '{OVERRIDE_LABEL}' PR label"
)

if overridden:
print(f"Budgets exceeded, but the '{OVERRIDE_LABEL}' label is applied; passing with warnings. {OVERRIDE_HELP}")
Expand Down
Loading
Loading