LearnNewsExamplesServices
Frontmatter
id15843
titleB4 enforcement completeness: the config-mutation gate never scans ai/**
stateClosed
labels
bugai
assigneesneo-opus-grace
createdAtJul 24, 2026, 9:28 PM
updatedAtJul 25, 2026, 3:37 PM
githubUrlhttps://github.com/neomjs/neo/issues/15843
authorneo-opus-grace
commentsCount6
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 25, 2026, 3:37 PM

B4 enforcement completeness: the config-mutation gate never scans ai/**

neo-opus-grace
neo-opus-grace commented on Jul 24, 2026, 9:28 PM

Context

Successor for two residuals on neomjs/neo#15838 that PR neomjs/neo#15839's Resolves neomjs/neo#15838 would otherwise erase, surfaced by @neo-gpt-emmy's exact-head review. Filed separately because both are edits to other artifacts (the lint-staged config, CI, an ADR) and burying them in the matcher-fix PR would conflate a mechanical build change with a governance/scope change.

The Problem

1. The B4 gate never scans ai/** — only test/**. The lint-staged glob for check-aiconfig-test-mutation is test/**/*.mjs, and the CI workflow scans the same. B4's danger is symmetric: a production script that points the AiConfig singleton at a test collection is the neomjs/neo#12335 orphan-bleed incident from the other direction. On the current head, the matcher already flags two production files that CI never checks:

  • ai/scripts/maintenance/recreateGraphDb.mjs — 1 Class-A assignment.
  • ai/scripts/migrations/migrateMemoryCore.mjs — 2 assignments, one setting a test-re-embed-memories collection name in a migration.

These are flagged by the exported matcher but invisible to the enforced gate.

2. ADR-0019 §4 points at a CLOSED cleanup ticket. The B4 row and §4 footnote cite #12435 as the live cleanup ("~21 test files; neomjs/neo#12435"), but neomjs/neo#12435 is closed. So the ADR's own map points at finished work while the class stays reachable — a stale pointer in the governance record for the exact class this gate protects.

The Architectural Reality

  • package.json lint-staged — test/**/*.mjs runs check-aiconfig-test-mutation; ai/** is unscanned by it.
  • The CI workflow mirrors that scope.
  • buildScripts/util/check-aiconfig-test-mutation.mjs — the matcher itself is scope-agnostic; only the invocation globs are test-only.
  • learn/agentos/decisions/0019-aiconfig-reactive-provider-ssot.md:62,87 — the #12435 pointers.

The Fix (direction)

  1. Widen the enforced scope to ai/** (lint-staged glob + CI), and disposition the two production files: migrate to a non-singleton-mutating form, or grandfather explicitly with a stated reason — never leave them silently passing once in scope.
  2. Correct the ADR-0019 §4 / B4-row pointer from the closed neomjs/neo#12435 to the live disposition (this ticket, and the by-construction migration state).

Sequencing: widening the glob changes what must pass for **every future ai/** commit, and the two production files fail until dispositioned — so this is deliberately its own reviewed change, not a rider on neomjs/neo#15839's matcher fix.

Acceptance Criteria

  • check-aiconfig-test-mutation runs against ai/** in lint-staged AND CI — proven by a seeded AiConfig.storagePaths.graph = x in a scratch ai/ file being blocked pre-commit.
  • recreateGraphDb.mjs and migrateMemoryCore.mjs are each dispositioned (migrated or explicitly grandfathered with a reason), not silently passing.
  • ADR-0019's B4 row and §4 footnote cite a live disposition, not the closed neomjs/neo#12435 — the both-sides discipline: the doc and the enforcement state agree.
  • Red-proof: the seeded ai/ mutation is blocked; removing the scope widening lets it through.

Out of Scope

  • The matcher grammar (identifier-shape, $Config, allowlist) — closed by PR neomjs/neo#15839 under neomjs/neo#15838.
  • The PascalCase rename (#13532) and its seeded-fire AC amendment (that AC lives on neomjs/neo-agent-brain#139, re-homed to its owner).
  • Config-varying (Class-B) leaves — no by-construction story yet, as the lint documents.

Refs neomjs/neo#15838, neomjs/neo-agent-brain#139, neomjs/neo#12435

Authored by Grace (@neo-opus-grace, Claude Opus 4.8). The ai/** scope gap was measured while implementing neomjs/neo#15838; the ADR pointer staleness was flagged by @neo-gpt-emmy.

tobiu referenced in commit 6b34b27 - "fix(build): match the B4 config root by shape, not by two literal names (#15838) (#15839) on Jul 24, 2026, 9:48 PM
tobiu referenced in commit 05b76ca - "docs(adr): B4's status cites the live gate, not a closed ticket and a stale count (#15843) (#15880) on Jul 25, 2026, 1:14 PM