LearnNewsExamplesServices
Frontmatter
id11930
titlecheck-shorthand.mjs — substrate gate against verbose `key: key` regression (#11306 AC2 follow-up)
stateClosed
labels
enhancementaiarchitecturemodel-experience
assigneesneo-opus-ada
createdAtMay 24, 2026, 11:14 PM
updatedAtJun 7, 2026, 7:15 PM
githubUrlhttps://github.com/neomjs/neo/issues/11930
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 27, 2026, 2:12 PM

check-shorthand.mjs — substrate gate against verbose key: key regression (#11306 AC2 follow-up)

Closed v13.0.0/archive-v13-0-0-chunk-13 enhancementaiarchitecturemodel-experience
neo-opus-ada
neo-opus-ada commented on May 24, 2026, 11:14 PM

Context

#11306 AC2 originally prescribed adding an ESLint object-shorthand: ['error', 'always'] rule. PR #11926's V-B-A surfaced that Neo has no ESLint configuration — the project's lint mechanism is custom buildScripts/util/check-*.mjs scripts (check-whitespace.mjs, check-chore-sync.mjs), wired into lint-staged via package.json. The ESLint prescription was mis-shaped.

This ticket is the corrected-shape substrate gate prescribed during #11306#11926 review cycle (2026-05-24): mirror Neo's existing check-*.mjs convention for the shorthand gate.

The Problem

Without a mechanical gate, the key: key verbose form will silently re-accumulate after the #11306 sweep. Discipline-alone enforcement empirically fails — operator @tobiu's original #11306 observation (2026-05-13) flagged 71 instances had accumulated despite ES2015 shorthand being available since the project's inception. Same friction pattern (#11133's branch-discipline gap, #11116's commit-shape gap) demonstrates discipline-without-mechanical-gate produces predictable drift.

The Fix

Implement buildScripts/util/check-shorthand.mjs matching Neo's existing check-*.mjs convention:

  1. Scan ai/, src/, test/playwright/ for *.mjs files (excluding worktrees, dist/, node_modules/)
  2. Regex ^(\s+)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:\s*\2\s*[,}] — identifier-only (not \w+ which matches numeric keys; per @neo-gpt's #11926 review, the original regex falsely matched 0: 0 in ai/services/ConceptService.mjs)
  3. Exit non-zero if any matches found; print file:line of each violation
  4. Wire into package.json lint-staged block alongside check-whitespace.mjs + check-chore-sync.mjs

Prescription

// buildScripts/util/check-shorthand.mjs (skeleton)
import {readFileSync} from 'fs';
import {execSync}     from 'child_process';

const re   = /^(\s+)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:\s*\2\s*[,}]/;
const dirs = ['ai', 'src', 'test/playwright'];

const files = execSync(`find ${dirs.join(' ')} -name '*.mjs' -not -path '*/.claude/*' -not -path '*/dist/*' -not -path '*/node_modules/*'`).toString().trim().split('\n');

const violations = [];
for (const f of files) {
    const lines = readFileSync(f, 'utf8').split('\n');
    lines.forEach((line, i) => {
        if (re.test(line)) violations.push(`${f}:${i + 1}: ${line.trim()}`);
    });
}

if (violations.length > 0) {
    console.error(`check-shorthand: ${violations.length} verbose key:key form(s) found:`);
    violations.forEach(v => console.error('  ' + v));
    console.error('\nUse ES2015 shorthand: {key} instead of {key: key}.');
    process.exit(1);
}
console.log(`check-shorthand: ${files.length} files scanned, 0 violations.`);

Acceptance Criteria

  • (AC1) buildScripts/util/check-shorthand.mjs created with the regex + scan logic above
  • (AC2) package.json lint-staged block extended to run check-shorthand.mjs on *.mjs files
  • (AC3) Local verification: node buildScripts/util/check-shorthand.mjs exits 0 on current dev (after #11306 / PR #11926 lands)
  • (AC4) Pre-commit hook fires the check; verified via test commit that re-introduces a verbose form (expected: pre-commit blocks)

Out of Scope

  • Migrating Neo to ESLint — that's a larger substrate decision, separate ticket
  • Other lint surfaces (max-line-length, trailing whitespace beyond what check-whitespace.mjs covers) — separate concerns

Empirical Anchors

Origin

Filed 2026-05-24 per @neo-gpt's #11926 review prescribing the corrected substrate shape for the deferred #11306 AC2 lint gate.

— @neo-opus-ada

tobiu referenced in commit 9f54984 - "feat(buildScripts): add check-shorthand.mjs substrate gate (#11930) (#12060) on May 27, 2026, 2:12 PM
tobiu closed this issue on May 27, 2026, 2:12 PM