ci(*): accept the deps-dev scope and pin dependabot's commit prefix - #441
Conversation
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: move the change to a source branch whose type is allowed by AGENTS.md section 2.1.
The code change itself looks sound. The source branch is ci/dependabot_commit_prefix_scope, but the repository's hard branch-naming rule permits only feat, fix, refactor, perf, chore, docs, and test as branch types. This is not line-anchorable; the exact compliant branch type is the author's choice.
I covered the full github/main...HEAD diff after refreshing github/main, the commitlint callers and Dependabot configuration, relevant history, all five tracked npm lockfile roots, backward compatibility, test-strength changes (none), AGENTS.md/CLAUDE.md and CONTEXT routing, and the runtime/TUI architecture constraints (not implicated). GitHub's documented Dependabot behavior supports the chosen build prefix plus deps/deps-dev scopes, and git merge-tree --write-tree HEAD github/main completed without conflicts.
Local verification:
uv run pytest tests/test_commit_lint.py tests/test_readme_scope_canon.py -q: 13 passeduv run pre-commit run --files .github/dependabot.yml commitlint.config.cjs: passed or skipped, no failures- Real commitlint accepted
build(deps)andbuild(deps-dev)and rejected an unknown near-match scope - Repository commit-message checks, YAML/package-root structural checks, PR-body ASCII check, and
git diff --check: passed - GitHub checks were green except the coverage aggregation job was still pending when reviewed
The scope enum became enforced on 2026-09-05 with a literal "deps" entry, but main already carried chore(deps-dev) from 2026-06-30, so every dependabot dev-dependency PR fails scope-enum. Add the sibling scope that was already in use. Dependabot also guesses its own prefix from history and has drifted: the 12 dependency bumps merged so far are chore(deps*), the four open ones are build(deps*). Pin prefix "build" with include "scope" on every ecosystem so the type stops moving, and add the two npm trees that had no entry at all, /ui-web and /ui-tui/packages/hermes-ink; both have produced real dependabot pull requests while inheriting none of this configuration. Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
The scope fix in the previous commit has no gate behind it. The same drift that kept deps-dev out of the enum for two months would go unnoticed a second time, and a sixth npm tree could be added with no dependabot entry the way /ui-web and /ui-tui/packages/hermes-ink were. Four assertions, each of which was checked against a mutant that must fail it: every ecosystem pins its prefix, every scope the config emits is legal, scopes nobody declared are still rejected, and every lockfile in the tree has an entry. The enum is read by evaluating commitlint.config.cjs with node rather than by matching its text, anchored on a scope that cannot move so that a broken extraction fails loudly instead of satisfying every membership test. Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
360e3bd to
9268ecf
Compare
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: the previously reported source-branch policy violation remains unresolved.
No new code findings. The added contract test exercises the loaded commitlint configuration rather than its text, covers both Dependabot scopes plus negative cases, and keeps npm/uv configuration aligned with tracked lockfiles. It does not weaken existing tests.
I rechecked the complete github/main...HEAD diff, the new test and its subprocess/configuration callers, relevant history, backward compatibility, AGENTS.md/CLAUDE.md and CONTEXT routing, and architecture impact (none beyond CI policy). The rebase onto current main is conflict-free.
Verification:
uv run pytest tests/test_dependabot_scope_canon.py tests/test_commit_lint.py tests/test_readme_scope_canon.py -q: 17 passeduv run pre-commit run --files .github/dependabot.yml commitlint.config.cjs tests/test_dependabot_scope_canon.py: passed or skipped, no failures- Commitlint, repository commit-message checks, and
git diff --check: passed - GitHub's completed checks were green; unit/TUI jobs were still running when reviewed
The first gate checked each ecosystem in isolation, so it still passes with half of them pinned to build and half to chore, which is the split this branch exists to end. It also derived coverage from lockfiles alone, so dropping the github-actions entry, the one ecosystem with no manifest to derive from, went unnoticed. Two more assertions, each checked against its own mutant: the pinned prefixes agree with each other, and a repo that has workflows has an ecosystem for them. Whether an entry is pinned at all stays owned by the first assertion, so a missing block fails in one place rather than two. Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: the previously reported source-branch policy violation remains unresolved.
No new findings. The two added assertions close the remaining gaps in the contract test: configured ecosystems must agree on one accepted prefix, and the workflows tree must retain its root github-actions configuration. Existing coverage is not weakened.
I rechecked the complete github/main...HEAD diff, test/config callers and history, backward compatibility, AGENTS.md/CLAUDE.md and CONTEXT routing, and architecture impact. The merge simulation against refreshed github/main is clean.
Verification:
uv run pytest tests/test_dependabot_scope_canon.py tests/test_commit_lint.py tests/test_readme_scope_canon.py -q: 19 passed- Focused pre-commit hooks: passed or skipped, no failures
- Commitlint, repository commit-message checks, and
git diff --check: passed - Completed GitHub checks for this revision were green; unit shards were still running when reviewed
… test Four of the six assertions each spawned node to evaluate commitlint.config.cjs, and the answer cannot change inside a session. The unit job shards run under xdist on a two-core runner where wall-clock idle is what the idle ceiling measures, so spawns that buy nothing are not free to the tests sharing the box. Cached on the rule name, returning a tuple so no caller can mutate what the next one reads. All seven mutants still die, each in one place. Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; suggestions only, and they are marked inline.
The code can merge. The new cache is keyed by rule name and returns immutable tuples, so it removes redundant Node subprocesses without coupling the two enums or allowing one test to mutate another's result. Existing coverage is not weakened.
I am withdrawing my prior blocking stance under the required post-fourth-round convergence bar. The branch-name mismatch is introduced here, but it does not cause an ordinary operator failure with no working way out, so it remains only a named, nonblocking follow-up. It cannot be anchored to a changed line. There are no review threads opened by me to resolve.
I covered the complete github/main...HEAD diff, the cache callers and test history, backward compatibility, AGENTS.md/CLAUDE.md and CONTEXT routing, test strength, and architecture impact. The merge simulation against refreshed github/main is clean.
Verification:
uv run pytest tests/test_dependabot_scope_canon.py tests/test_commit_lint.py tests/test_readme_scope_canon.py -q: 19 passed- Focused pre-commit hooks: passed or skipped, no failures
- Commitlint, repository commit-message checks, and
git diff --check: passed - Completed GitHub checks were green; unit shards were still running when reviewed
…with 2 updates (#387) ## Summary Dependabot security update for the repository-root npm tree. Closes seven open Dependabot alerts, all high severity: | Package | From | To | Advisories | |---|---|---|---| | fast-uri | 3.1.3 | 3.1.7 | GHSA-v2hh-gcrm-f6hx, GHSA-7p8r-x3mc-p8w7, GHSA-5jgf-p345-68v8, GHSA-fph4-wmhf-6fwf, GHSA-f65p-4m7j-42xc, GHSA-jqff-g426-hqxp (host confusion and SSRF in URI parsing) | | js-yaml | 4.3.0 | 4.3.2 | GHSA-5p4m-2wfm-xmqj, GHSA-2883-xcg3-v3hh (quadratic and unbounded CPU use in !!omap and merge-key resolution) | Both are development dependencies, and this npm tree is not shipped: its only direct dependency is `@commitlint/cli`, which runs the commit-message gate in CI. `fast-uri` reaches us through `ajv`, which resolves `$ref` URIs inside commitlint's own schema; `js-yaml` reaches us through `cosmiconfig`, which loads config files, and this repo's config is `commitlint.config.cjs`, not YAML. Neither parser is fed anything from a pull request, so the exposure here is alert hygiene rather than a reachable path -- which is also why this is the cheapest of the four to take. This description replaces Dependabot's default. That one quotes upstream release notes verbatim, so it carries emoji and em-dashes, and the repository lints the PR body as ASCII because the squash merge uses it as the commit body. ## Type - [ ] Fix - [ ] Feature - [ ] Docs - [x] CI / tooling - [ ] Refactor - [ ] Other ## Verification The diff is six lines of `package-lock.json`. Reachability was read off the lockfile rather than assumed: ``` $ python3 -c "...load package-lock.json..." node_modules/fast-uri dev=True required by: node_modules/ajv node_modules/js-yaml dev=True required by: node_modules/cosmiconfig non-dev packages in this lock: 0 (77 packages total) ``` CI must be green on this branch before it merges; the checks that failed before the rebase were stale runs from 2026-09-11 plus the two commit-lint gates, which `ci(*): accept the deps-dev scope and pin dependabot's commit prefix` (#441) fixed on main. - [ ] Relevant tests pass locally - [ ] Relevant lint / type checks pass locally - [ ] User-facing docs or screenshots are updated when needed ## Risk No runtime behaviour changes: nothing in this lockfile is installed by users or packaged into the wheel. The risk of taking it is that commitlint itself breaks in CI, which the commit-lint jobs on this PR exercise directly. Rollback is to revert the commit. - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A. Closes seven Dependabot alerts on `package-lock.json`. Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary The idle ceiling has failed a unit shard on nearly every pull request opened today, `main` included, and it names a test in `tests/test_rpc_model.py` almost every time -- but hardly ever the same one twice. #446, #448, #441 and two pushes to `main` all died this way with every test passing. `_warm_the_heaviest_import` in `tests/conftest.py` exists for exactly this, and its docstring already states the mechanism: a cold read costs seconds that no CPU accounts for, it lands on whichever test reached it first, and "a gate cannot be held to a moving target". It warms litellm and stops there. Measured with that hook already active, listing every file over half a megabyte opened from inside a test in that module: | File | Size | Before this change | |---|---|---| | `litellm/.../tokenizers/anthropic_tokenizer.json` | 1.7 MB | not opened in a test (the hook covers it) | | `litellm/.../tokenizers/9b5ad71b...` | 1.6 MB | not opened in a test | | `litellm/model_prices_and_context_window_backup.json` | 1.4 MB | not opened in a test | | **`raven/providers/data/models.json`** | **0.6 MB** | **opened inside tests, repeatedly** | The repetition is the registry's own design: the three files under `raven/providers/data` sit behind `lru_cache`s that `registry_data.reset_cache()` drops, so each is read again after every reset, and the first of those reads in a run is the cold one. Warming them pays that read on a line that is the same every run instead of on whichever test got there first. After this change the same measurement finds nothing over half a megabyte opened inside any test in that module. **What this does not claim.** It does not claim to end the CI failures. It closes a gap the measurement shows is real and that the hook was already written to close; whether that gap accounts for the whole three-to-four seconds is a question only a CI run can answer, because the page cache on a developer's machine is warm and the effect cannot be reproduced locally at all. ## Type - [ ] Fix - [ ] Feature - [ ] Docs - [x] CI / tooling - [ ] Refactor - [ ] Other ## Verification The before and after are the same measurement, an instrumented `open` recording every file over 512 KB opened between a test's setup and teardown, run with `--idle-ceiling-strict` so the warm hook is active: ``` before: raven/providers/data/models.json opened inside tests, 3 times after: 0 files over 512 KB opened inside any test in the module ``` Suites: ``` $ uv run pytest tests/test_rpc_model.py -q 195 passed in 9.77s (same count as before the change) $ uv run pytest tests/test_rpc_model.py tests/test_provider_catalog.py tests/test_commit_lint.py -q 287 passed, 7 skipped in 10.21s $ uv run pytest -q --idle-ceiling-strict 1 failed, 22940 passed, 121 skipped in 223.50s ``` The single failure is `tests/test_read_file_image.py::test_image_block_is_the_only_place_the_shape_is_written`, which fails the same way on an unmodified `main` worktree: it shells out to `grep` and filters on `raven/utils/images.py`, and the `grep` on this developer's PATH emits a doubled separator, so the filter misses its own file. CI uses GNU grep and is green on it. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [ ] User-facing docs or screenshots are updated when needed ## Risk Test-harness only; nothing under `raven/` changes. The three accessors run inside the hook's existing `try`, which already swallows exceptions on the grounds that a tree without the provider extras is not this hook's business, so a checkout that cannot read the catalogue still starts its suite. The hook only runs under `--idle-ceiling-strict`, which is CI's invocation, so a plain local `pytest` is unaffected. Rollback is to revert the commit. - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A. Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
Summary
Two dependency-bump PRs (#385, #387) cannot pass commit lint, and the four
open dependabot PRs do not agree with the twelve already on main about what a
dependency bump is called. This fixes both, on the two sides that own them.
The scope enum lost a scope it was already using.
scope-enumbecameenforced on 2026-09-05 (8f6ef4c) with a literal
depsentry. Butchore(deps-dev)had merged to main on 2026-06-30 (b3959c9, #24), two monthsearlier. The enum swept in the sibling and missed this one, so every dependabot
dev-dependency PR fails a gate for using a scope the repo had already accepted.
deps-devis restored to the enum; it is not a new scope.Dependabot guesses its own prefix, and it has drifted. Nothing pinned it, so
it infers one from recent history. The 12 bumps merged so far are
chore(deps*);the 4 open ones are
build(deps*). That split meansgit log --grepneeds twoprefixes to find dependency work, and it will move again. Every ecosystem now
pins
prefix: "build"withinclude: "scope", which is the documented way toget
build(deps)/build(deps-dev).buildis what AGENTS.md section 3.1defines for external dependencies.
Two npm trees had no entry at all. The repo has five npm lockfiles;
dependabot.yml configured three.
/ui-weband/ui-tui/packages/hermes-inkweremissing, and both have produced real dependabot PRs (#385 is a
/ui-webPR rightnow, #57 was a hermes-ink one). Security updates reach them through the
dependency graph regardless of this file, so they were inheriting none of the
labels, grouping or commit-message settings the other trees get. Adding them is
what makes the prefix fix actually cover every tree rather than three of five.
Neither fix had a gate behind it, so there is one now. Both files are edited
by hand, neither imports the other, and the drift they just had stayed invisible
until a person read a CI log.
tests/test_dependabot_scope_canon.pypins sixthings: every ecosystem pins its prefix rather than letting dependabot infer one,
the pinned prefixes agree with each other, every scope the config will emit is in
the enum, scopes nobody declared are still rejected, every lockfile tracked in the
tree has an entry, and a repo that has workflows has an ecosystem for them. It reads the enum
by evaluating
commitlint.config.cjswith node instead of matching its text,anchored on a scope that cannot move so a broken extraction fails loudly rather
than satisfying every membership assertion.
What this does not fix: dependabot PR bodies quote upstream release notes
verbatim, which carry emoji and em-dashes, so they still fail the ASCII gate in
scripts/check_pr_body.py. That gate is correct - the PR body becomes the squashcommit body - and the body is rewritten by hand per PR. Only the scope half is
automated here.
Type
Verification
Ran in a clean worktree cut from
origin/main:Each of the four open dependabot subjects, and its prospective squash header,
piped through the real commitlint with this config. Before the change:
After:
The last three are the point: the enum still rejects scopes it should reject, so
this widens it by exactly one entry rather than loosening it.
Each assertion in the new test was then checked against a mutant that must fail
it, because a config test that only reads the config it was written against
proves nothing:
The dependabot.yml edit was checked structurally, not by eye: both versions
parsed, and every pre-existing entry is identical apart from the added
commit-messageblock, no entry was dropped, group names stay unique, and thenpm directories now equal the set of npm lockfiles tracked in the repo.
Risk
Behaviour changes, all in tooling:
feat(deps-dev): ...also becomes legal, becausecommitlint cannot tie a scope to a type.
depsalready had that property; thisdoes not introduce it.
build(deps)/build(deps-dev)instead of whatever it infers. Existing open PRs already usethose, so nothing needs rewriting.
/ui-weband/ui-tui/packages/hermes-inksecurity updates will now begrouped and labelled like the other trees rather than arriving unconfigured.
open-pull-requests-limit: 0matches the other entries, so no version-updatePRs are enabled by this.
behaviour here is documented but not observable from a local run. If it turns
out wrong, the enum change alone still unblocks the open PRs.
Rollback: revert the commits. Nothing depends on either file at runtime.
Related Issues
N/A. Unblocks #385 and #387, which fail
scope-enumtoday.