LearnNewsExamplesServices
Frontmatter
id11306
titleCollapse verbose `key: key` shorthand in object literals (~71 instances)
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-4-7
createdAtMay 13, 2026, 3:07 PM
updatedAtMay 24, 2026, 11:36 PM
githubUrlhttps://github.com/neomjs/neo/issues/11306
authorneo-opus-4-7
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 24, 2026, 11:36 PM

Collapse verbose key: key shorthand in object literals (~71 instances)

Closedenhancementaiarchitecture
neo-opus-4-7
neo-opus-4-7 commented on May 13, 2026, 3:07 PM

Friction Source

Surfaced 2026-05-13 by @tobiu during the #11187 archive substrate review:

"i see minor code smells like:

>     version              : version,

> filename : filename, > itemCount : itemCount, > itemIndex : itemIndex > ``` > => we can just use keys. could be a tech debt radar ticket, since this happens in many spots now."

ES2015 shorthand ({version, filename, itemCount, itemIndex}) is the idiomatic form when an object literal key matches an identifier already in scope. The aligned key : key, form was a pre-ES2015 pattern that accumulated through harness iterations and is now visually dominant in some hot paths.

<h2 class="neo-h2" data-record-id="3">Empirical Prevalence</h2>

Quick grep against the codebase (Perl back-reference, padded and unpadded forms):bash find ai src test/playwright -name ".mjs" | xargs perl -ne 'print if /^\s+(\w+)\s:\s*\1\s*[,}]/'


- **71 candidate instances** across `ai/`, `src/`, `test/playwright/`
- Concentrated in: `IssueService.mjs`, `DiscussionSyncer.mjs`, `DatabaseService.mjs`, `GraphService.mjs`, `MemorySessionIngestor.mjs`, `AuthService.mjs`, and a few `src/calendar/`, `src/main/`, `src/canvas/`, `src/tab/` files
- Note: 71 is a lower bound — the regex only catches identifier-equals-identifier; doesn't catch `key: this.key` or destructure-rename opportunities

<h2 class="neo-h2" data-record-id="4">The Problem</h2>

1. **Visual noise** — aligned `key : key,` blocks pad horizontally with redundant tokens that ES2015 lets us delete cleanly
2. **Drift risk** — when a variable rename misses a key (e.g. `filename → filepath`), the silent mismatch isn't caught by the literal form; shorthand syntax prevents this class of drift mechanically
3. **MX friction** — agents reading these hot paths waste tokens parsing redundant tokens

<h2 class="neo-h2" data-record-id="5">The Architectural Reality</h2>

Modern V8 handles shorthand identically to the verbose form (no runtime cost difference). The reason both forms coexist is just author-habit accumulation, not any deliberate substrate choice.

ESLint has `object-shorthand` rule (set to `'always'` or `'properties'`) that mechanically enforces the shorthand form on lint.

<h2 class="neo-h2" data-record-id="6">The Fix</h2>

Two-phase approach:

1. **Mechanical sweep** — run codemod or careful regex-replace over the 71 instances, producing a single grouped PR per directory (`ai/`, `src/calendar/`, `src/main/`, etc.) to keep diff review tractable.
2. **Permanent gate** — add `object-shorthand: ['error', 'always']` to the relevant `.eslintrc` so future authors get a lint-error instead of accumulating new instances.

Net byte savings ~500-1500 bytes across the codebase; primary benefit is the lint-gate against future drift.

<h2 class="neo-h2" data-record-id="7">Acceptance Criteria</h2>

- [ ] **(AC1)** Mechanical sweep replaces verbose `key: key` form with shorthand `{key}` across the ~71 identified instances
- [ ] **(AC2)** ESLint `object-shorthand: ['error', 'always']` rule added to project `.eslintrc` (or equivalent flat-config) so the gate is permanent
- [ ] **(AC3)** Lint passes on `dev` after sweep + rule activation
- [ ] **(AC4)** Spot-check audit: no remaining instances detected by `find ai src test/playwright -name "*.mjs" | xargs perl -ne 'print if /^\s+(\w+)\s*:\s*\1\s*[,}]/'` (zero matches expected)

<h2 class="neo-h2" data-record-id="8">Out of Scope</h2>

- Renaming variables to fit shorthand opportunities (e.g., `key: this.foo` → don't rename `this.foo` to `key`); only mechanical key-equals-identifier collapses
- Destructure-rename refactors (`{ foo: bar }` ≠ shorthand candidate)
- The `key : 'literal-string'` form (not a shorthand candidate by definition)

<h2 class="neo-h2" data-record-id="9">Avoided Traps</h2>

- **Hand-edit all 71 in one PR** — too large for review; group by directory
- **Skip the ESLint gate** — the sweep without the gate guarantees the pattern resurfaces within 1-2 release cycles
- **Pre-claim and rush the sweep** — this is a Quick Win lane that any peer can pull; tagged for self-selection

<h2 class="neo-h2" data-record-id="10">Related</h2>

- **Origin friction:** [@tobiu observation 2026-05-13](https://github.com/neomjs/neo/pull/11297#discussion) during <a href="#/news/tickets/11187">#11187</a> archive substrate batch-merge
- **Pattern reference:** ES2015 [Object Initializer / Property Definitions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#property_definitions)



</div>
                </div>
            </div>
                    <div id="timeline-11306-1" class="neo-timeline-item event" data-record-id="timeline-11306-1">
                        <div id="timeline-11306-1-target" class="neo-timeline-badge" ><i class="fa-solid fa-link"></i></div>
                        <div class="neo-timeline-body">
                            <a class="neo-timeline-user" href="https://github.com/tobiu" target="_blank">tobiu</a> referenced in commit <code><a href="https://github.com/neomjs/neo/commit/aca8011" target="_blank">aca8011</a></code> - &quot;refactor: collapse 64 <code>key: key</code> shorthand instances + ws cleanup (#11306) (#11926) <span class="neo-timeline-date">on May 24, 2026, 11:36 PM</span>
                        </div>
                    </div>
                    <div id="timeline-11306-2" class="neo-timeline-item event" data-record-id="timeline-11306-2">
                        <div id="timeline-11306-2-target" class="neo-timeline-badge" style="color: #8250df"><i class="fa-solid fa-circle-check"></i></div>
                        <div class="neo-timeline-body">
                            <a class="neo-timeline-user" href="https://github.com/tobiu" target="_blank">tobiu</a> closed this issue <span class="neo-timeline-date">on May 24, 2026, 11:36 PM</span>
                        </div>
                    </div></div>