LearnNewsExamplesServices
Frontmatter
id18302
titleDependabot only moves a dependency when an advisory fires: security updates run, version updates were never configured
stateClosed
labels
enhancementai
assigneesneo-opus-grace
createdAtSep 4, 2026, 8:02 PM
updatedAtSep 4, 2026, 9:16 PM
githubUrlhttps://github.com/neomjs/neo/issues/18302
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 4, 2026, 9:16 PM

Dependabot only moves a dependency when an advisory fires: security updates run, version updates were never configured

neo-opus-grace
neo-opus-grace commented on Sep 4, 2026, 8:02 PM

Context

Operator direction, 2026-09-04: "please add majors too. we want to move fast. we forget it otherwise, then get security alerts. the NEXT real PR will get pipeline fails, and if this happens (and only then) we create new tickets." Plus the cost anchor he named: a routine js-yaml ^4 → ^5 bump consumed roughly an hour and a large share of a scarce Fable budget on hand-verification and a review cycle — work an automated bump does for free.

The trade is deliberate and it is his to make: spend cheap CI on dev instead of scarce reasoning tokens, and ticket a break only when a real PR trips over it.

The Problem

Dependabot already runs here — but only for security. Five merged Dependabot PRs sit in this repo's history (#18132 2026-09-02, #16669, #16608, #16478, #16476), all titled bump the npm_and_yarn group, which is the security-updates group. Security updates need no config file; they are a repository setting.

Version updates need .github/dependabot.yml, and no repository in the org has one. Checked at 2026-09-04: neo, neo-agent-skills, neo-agent-brain, neo-agent-institution, create-app, workspace, devindex, jsdoc-x, benchmarks, pages — all ten carry a package.json, none carries a Dependabot config.

So dependencies move only when an advisory fires. That is exactly the reported symptom — "we forget it otherwise, then get security alerts" is the mechanism, not a figure of speech: an advisory is currently the only event that moves a dependency.

The Architectural Reality

Three measured facts decide the shape, and two of them are prerequisites rather than nice-to-haves.

  1. dev has zero required status checks. GET /repos/neomjs/neo/rules/branches/dev returns ["code_scanning","copilot_code_review","deletion","non_fast_forward"], and the merge-readiness projection reports requiredSet.contexts: []. GitHub auto-merge waits only for required checks. With none required, enabling auto-merge merges each PR as soon as it is mergeable — before CI reports. "Green CI is enough" would silently become "CI is irrelevant."

  2. No PR gate blocks bots at all. The premise was false — twice corrected. (First revision over-scoped it to four gates; this revision retracts it entirely, on @neo-gpt's review of PR #18322, source-verified.)

    pr-baseline.yml does fire on a Dependabot PR — that part held. But it is a caller, and the work happens in the pinned reusable workflow at neo-agent-skills@e409dea5d, which I had never opened. Both of its PR-body steps carry:

       if: startsWith(github.event.pull_request.user.login, 'neo-') || contains(github.event.pull_request.labels.*.name, 'ai')

    A Dependabot PR is authored by dependabot[bot] and labelled dependencies / javascript (per #18132). It matches neither clause, so the body validator never runs on it. There is nothing to exempt.

    The callee's own comment states the design, and it also states why a caller-level skip would be actively harmful: "The steps are gated rather than the job, so the check still REPORTS on a human PR: a skipped job cannot serve as a required status context." A caller-level if: marks all five jobs skipped — pr-base, skills-materialized, source-comment-archaeology, substrate-size, pr-body — and a skipped job cannot be bound as a required context. It would therefore have broken prerequisite (1) of this very ticket.

    Consequence: prerequisite (2) does not exist. Only the required-checks prerequisite (1) remains, and this ticket is a one-file delivery.

    How the error survived a deliberate verification pass: I swept four workflow on: blocks and concluded correctly that pr-baseline fires. I never asked the next question — whether the job inside it applies to a bot. The instrument was pointed at the caller's trigger; the gate lives one level down, in a pinned SHA. A caller firing is not a check running.

  3. A bad bump on dev cannot reach consumers. npm-publish.yml triggers on release: and workflow_dispatch only — never on a push to dev. Publishing sits behind the release cut (buildScripts/release/publish.mjs, the atomic dev → main commit), which is human-driven per AGENTS.md §critical_gates 8. This is the structural reason the operator's risk call is contained: agent work targets dev, main is release-only, and the blast radius of an auto-merged bump ends at dev.

Major updates need no configuration. They are Dependabot's default; you opt out with ignore: update-types: ["version-update:semver-major"]. The requirement is satisfied by not adding that key.

The Fix

Commit a ~12-line .github/dependabot.yml. Build no mechanism around it.

An installer/materializer in neo-agent-skills was the intuitive shape and is declined by precedent. #17798 settled it: the operator rejected copied-bytes-plus-policing-machinery twice (SSOT violation on neomjs/devindex#6; merged-then-reverted neomjs/neo-agent-brain#5), with the ruling "a store whose contents are duplicated N times is not canonical, and the machinery needed to police those duplicates was evidence of the mistake rather than mitigation of it." Its Contract Ledger already names this exact surface: freshness | npm | npm outdated / dependabot | ecosystem-native; no bespoke gate.

A materializer plus a drift check is byte-copying plus a policing guard — the rejected shape, applied to a file of a dozen static lines that changes perhaps once a year. There is no freshness question here worth machinery. Five hand-committed files is the whole job.

version: 2
updates:
  - package-ecosystem: npm
    directory: "/"
    schedule: {interval: daily}
    groups:
      all-deps:
        patterns: ["*"]
  - package-ecosystem: github-actions
    directory: "/"
    schedule: {interval: daily}
    groups:
      actions:
        patterns: ["*"]

github-actions is included because the pinned action versions (actions/checkout@v4, actions/setup-node@v4) are a live supply-chain surface that nothing currently updates.

Operator-owned steps

Two settings are outside agent scope and must land for the policy to mean what it says:

  1. Make unit and components required status checks on dev — without this, auto-merge ignores CI entirely (Reality 1).
  2. Enable auto-merge on the repository.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
.github/dependabot.yml operator direction 2026-09-04 daily grouped version updates, npm + github-actions, majors included by default absent ⇒ today's behaviour (security-only) file comment no config in any of 10 org repos at 2026-09-04
pr-baseline.yml and its pinned callee reusable-pr-baseline.yml@e409dea5d :203, :222 unchanged — the callee already scopes body validation to neo-* authors or the ai label, so a bot PR reports green through its existing skip a caller-level exemption would mark all five jobs skipped, which cannot serve as a required context callee's own docblock both steps' if: read at the pinned SHA
dev required checks GitHub auto-merge semantics unit + components required empty required set ⇒ auto-merge merges before CI ruleset requiredSet.contexts: []
freshness mechanism #17798 Contract Ledger none — ecosystem-native, no installer, no drift check a materializer would re-run the twice-rejected copied-bytes shape this ticket #17798, devindex#6, neo-agent-brain#5

Acceptance Criteria

  • .github/dependabot.yml exists on dev with npm + github-actions ecosystems, daily, grouped, and no ignore entry for version-update:semver-major.
  • No workflow is modified. The delivery is one file. Verified at the pinned callee that a Dependabot PR already reports green through the existing author/label scoping.
  • Post-merge receipt on the first real bot PR: grouped version-update PRs appear from the dev config, all five baseline contexts are still emitted (not skipped), and baseline / PR body is green because its agent-only steps skipped — not because anything suppressed the workflow. A missing context is the failure signal here, since it would mean something removed a check.
  • No materializer, installer, sync workflow, or drift check is added in this ticket or a sibling — #17798 governs.
  • The two operator-owned settings are recorded here as done or explicitly deferred, with the consequence of deferral stated (auto-merge without required checks ignores CI).

Out of Scope

  • The installer / materializer — declined by #17798; see The Fix.
  • Sibling repos. neo-agent-skills, neo-agent-brain, neo-agent-institution, create-app each want the same file, but each has its own gate set to check. One leaf per repo after this one proves the shape; not a fan-out from here.
  • Renovate or any alternative — Dependabot is already partially live; switching tools is a different question.
  • Reverting a bad bump. Per operator direction, a break is discovered by the next real PR's CI and ticketed then.

Avoided Traps

  • Enabling auto-merge before required checks exist. The most expensive possible outcome: it looks like "merge on green" and is actually "merge, ignore CI". Reality 1 is why the ordering in The Fix is not arbitrary.
  • Adding a mechanism because five repos need the same file. The pull toward an installer is real and #17798 already ruled on it. Five files, once, by hand.
  • Citing careful manual verification as evidence that manual verification is needed. A reviewer (me) cited the js-yaml bump's hand-verification as an argument for review; the operator corrected it as the cost being avoided. Recorded so the next reader does not re-derive the inverted economics.
  • Treating dev breakage as a release risk. It is not: npm-publish.yml never fires from dev.

Related

#17798 (skills reach consumers as an npm dependency, never copied bytes — governs the transport question) · #18135, #18205, #17461 (the security-advisory path that currently is the only mover) · #18132 (most recent Dependabot security PR)


Live latest-open sweep: latest 20 open issues at 2026-09-04T17:57Z; no equivalent. Closed sweep run (mandatory for a "we should start doing X" ticket): surfaced #17798, which changed this ticket's prescription from an installer to no-mechanism, and the five merged Dependabot security PRs that corrected its premise. A2A in-flight claim sweep: 15 most recent messages across all read-states at 2026-09-04T18:02Z; no claim on this scope. Memory Core rationale sweep: returned only 2025-era unrelated hits — a known live degradation (@neo-opus-ada reported semantic recall silently serving 2025 results at 17:22Z today). Recorded as a null result, not a clean sweep. Own-assignment sweep: 12 open assignments, none on this surface.

Decision Record impact: none — no ADR governs dependency-update cadence. #17798's Contract Ledger row on freshness is the binding precedent and this ticket complies with it rather than amending it.