Skip to content

ci(*): accept the deps-dev scope and pin dependabot's commit prefix - #441

Merged
0xKT merged 4 commits into
mainfrom
ci/dependabot_commit_prefix_scope
Sep 16, 2026
Merged

0xKT merged 4 commits into
mainfrom
ci/dependabot_commit_prefix_scope

Conversation

@0xKT

@0xKT 0xKT commented Sep 16, 2026

Copy link
Copy Markdown
Member

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-enum became
enforced on 2026-09-05 (8f6ef4c) with a literal deps entry. But
chore(deps-dev) had merged to main on 2026-06-30 (b3959c9, #24), two months
earlier. 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-dev is 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 means git log --grep needs two
prefixes to find dependency work, and it will move again. Every ecosystem now
pins prefix: "build" with include: "scope", which is the documented way to
get build(deps) / build(deps-dev). build is what AGENTS.md section 3.1
defines for external dependencies.

Two npm trees had no entry at all. The repo has five npm lockfiles;
dependabot.yml configured three. /ui-web and /ui-tui/packages/hermes-ink were
missing, and both have produced real dependabot PRs (#385 is a /ui-web PR right
now, #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.py pins six
things: 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.cjs with 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 squash
commit body - and the body is rewritten by hand per PR. Only the scope half is
automated here.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

Ran in a clean worktree cut from origin/main:

$ uv run pytest tests/test_dependabot_scope_canon.py \
      tests/test_commit_lint.py tests/test_readme_scope_canon.py -q
19 passed in 2.65s

$ uv run pre-commit run --files .github/dependabot.yml commitlint.config.cjs
check yaml ... Passed   (all hooks Passed or Skipped, none Failed)

$ npx commitlint --from origin/main --to HEAD --config commitlint.config.cjs
(no output, exit 0)

$ PYTHONPATH=. uv run python scripts/check_commit_messages.py origin/main..HEAD
(no output, exit 0)

Each of the four open dependabot subjects, and its prospective squash header,
piped through the real commitlint with this config. Before the change:

FAIL  build(deps-dev): bump the npm-root-security group across 1 directory with 2 updates
PASS  build(deps): bump the tui-security group across 1 directory with 6 updates

After:

PASS  build(deps-dev): bump the npm-root-security group across 1 directory with 2 updates
PASS  build(deps-dev): bump js-yaml from 4.3.1 to 4.3.2 in /ui-web
PASS  build(deps): bump the bridge-security group across 1 directory with 2 updates
PASS  build(deps): bump the tui-security group across 1 directory with 6 updates
PASS  build(deps-dev): bump the npm-root-security group across 1 directory with 2 updates (#387)
PASS  build(deps-dev): bump js-yaml from 4.3.1 to 4.3.2 in /ui-web (#385)
FAIL  build(nonsense-scope): bump something
FAIL  build(deps-devv): bump something
FAIL  build(deps-dev): Bump something with a capital

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:

KILLED  drop deps-dev from the enum                -> 1 failed, 5 passed
KILLED  let the enum accept a typo scope           -> 1 failed, 5 passed
KILLED  empty the enum entirely                    -> 2 failed, 4 passed
KILLED  drop the /ui-web entry                     -> 1 failed, 5 passed
KILLED  split the ecosystems across two types      -> 1 failed, 5 passed
KILLED  drop the github-actions ecosystem          -> 1 failed, 5 passed
KILLED  let one ecosystem guess its prefix again   -> 1 failed, 5 passed

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-message block, no entry was dropped, group names stay unique, and the
npm directories now equal the set of npm lockfiles tracked in the repo.

  • Relevant tests pass locally
  • Relevant lint / type checks pass locally
  • User-facing docs or screenshots are updated when needed

Risk

Behaviour changes, all in tooling:

  • One more scope is legal. feat(deps-dev): ... also becomes legal, because
    commitlint cannot tie a scope to a type. deps already had that property; this
    does not introduce it.
  • Future dependabot commits and PR titles are pinned to build(deps) /
    build(deps-dev) instead of whatever it infers. Existing open PRs already use
    those, so nothing needs rewriting.
  • /ui-web and /ui-tui/packages/hermes-ink security updates will now be
    grouped and labelled like the other trees rather than arriving unconfigured.
    open-pull-requests-limit: 0 matches the other entries, so no version-update
    PRs are enabled by this.
  • The prefix pin cannot be verified until the next advisory; dependabot's
    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.

  • Security impact considered
  • Backward compatibility considered
  • Rollback path is clear for risky changes

Related Issues

N/A. Unblocks #385 and #387, which fail scope-enum today.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 passed
  • uv run pre-commit run --files .github/dependabot.yml commitlint.config.cjs: passed or skipped, no failures
  • Real commitlint accepted build(deps) and build(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

0xKT and others added 2 commits September 16, 2026 18:39
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>
@0xKT
0xKT force-pushed the ci/dependabot_commit_prefix_scope branch from 360e3bd to 9268ecf Compare September 16, 2026 10:40

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 passed
  • uv 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 gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@0xKT
0xKT merged commit 6093fc9 into main Sep 16, 2026
31 of 32 checks passed
@0xKT
0xKT deleted the ci/dependabot_commit_prefix_scope branch September 16, 2026 12:33
0xKT pushed a commit that referenced this pull request Sep 16, 2026
…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>
0xKT added a commit that referenced this pull request Sep 16, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants