Skip to content

build(eslint): add the missing config, and stop capping eslint's major - #64

Merged
BernardJen merged 1 commit into
mainfrom
claude/pipeline-59e3dd
Sep 3, 2026
Merged

build(eslint): add the missing config, and stop capping eslint's major#64
BernardJen merged 1 commit into
mainfrom
claude/pipeline-59e3dd

Conversation

@BernardJen

Copy link
Copy Markdown
Contributor

Why

npm run lint has never worked (#63). The script shipped from the start, but no config ever did — so eslint exited non-zero on every invocation, and nothing caught it because no workflow ran it.

Fixing that also unblocks #51. That PR was failing at npm ci with ERESOLVE, not at any check step — worth stating plainly, because it's easy to assume a "bump eslint" PR fails on lint:

While resolving: eslint-plugin-react@7.37.5
Found: eslint@10.9.0
Could not resolve dependency:
peer eslint@"^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" from eslint-plugin-react@7.37.5

eslint-plugin-react has no release that accepts eslint 10 — the next tag is a stale 7.8.0-rc.0 peering ^3 || ^4. It was also dead weight: with no config, nothing had ever referenced it.

What changed

  • Dropped eslint-plugin-react, bumped @typescript-eslint/* to ^8.69.0 — the first line peering eslint ^8.57 || ^9 || ^10. Nothing in the tree caps eslint's major any more.
  • Added eslint.config.mjs, deliberately not built on @eslint/js, which peers a single eslint major and would recreate this exact failure on the next bump.
  • Fixed the lint script — it still passed --ext, removed in eslint 9.
  • CI runs lint now, between install and typecheck. A lint script no job runs is how this went unnoticed for the life of the repo.
  • Dead code removal to reach a clean run: unused imports, two never-read catch bindings, and currentPresentationPath in both Windows backends — written twice, read never. macos.ts genuinely reads it as a fallback, so it stays there.
  • Dependabot grouping (lint-and-types, vite, react).

On dependabot grouping

#51 was one of four PRs failing the same way: a solo bump of a package whose peers are pinned to a sibling's major can never resolve alone. #49 and #50 are the clearest case — one bumps vite to 8, the other bumps @vitejs/plugin-react to a version requiring vite 8, and each fails precisely because the other is a separate PR.

Still blocked upstream — not fixed here

Grouped, both now sit still instead of failing every week.

Known warnings

0 errors, 18 warnings. require-atomic-updates is set to warn rather than disabled — it flags seven pre-existing interleaving hazards in the automation backends, where module-level slide state is read before an await and written after. They look real, but fixing them means reworking concurrency in code only exercisable against a live PowerPoint install. Left visible so the debt stays counted. The other 11 are no-explicit-any at the COM and PowerShell boundaries.

Verification

lint, typecheck, test (7 passed), build and a clean npm ci all pass locally. Separately confirmed that setting eslint to ^10.9.0 on this branch resolves cleanly and lints clean — i.e. #51 will go green once rebased.

Note: #60 will need a dependabot rebase, since package-lock.json moved here.

Closes #63

🤖 Generated with Claude Code

`npm run lint` has never worked (#63). The script existed from the start but
no config ever did, so eslint exited non-zero on every invocation, and nothing
caught it because no workflow ran it.

Fixing that also unblocks #51. Dependabot's eslint 9 -> 10 bump was failing at
`npm ci` with ERESOLVE, not at any check step: eslint-plugin-react@7.37.5 peers
`eslint ^3 || ... || ^9.7` and has no release that accepts 10 (the `next` tag is
a stale 7.8.0-rc.0 peering ^3 || ^4). The plugin was also dead weight - with no
config, nothing had ever referenced it. Dropping it and moving
@typescript-eslint to ^8.69.0, the first line peering `^8.57 || ^9 || ^10`,
leaves nothing in the tree capping eslint's major.

The new config is deliberately not built on @eslint/js, which peers a single
eslint major and would recreate this exact failure on the next bump.

Getting to a clean run needed some dead code removed: unused imports, two catch
bindings that were never read, and `currentPresentationPath` in both Windows
backends, which was written twice and read never (macos.ts genuinely reads it
as a fallback, so it stays there). `require-atomic-updates` is set to 'warn'
rather than disabled - it flags seven pre-existing interleaving hazards in the
automation backends that need a real PowerPoint install to fix safely.

CI runs lint now, between install and typecheck. A lint script no job runs is
how this went unnoticed for the life of the repo.

Finally, dependabot grouping, because #51 was one of four PRs failing the same
way: a solo bump of a package whose peers are pinned to a sibling's major can
never resolve alone. #49 and #50 are the clearest case - one bumps vite to 8,
the other bumps @vitejs/plugin-react to a version requiring vite 8, and each
fails precisely because the other is a separate PR.

Two of the four remain blocked upstream and are not addressed here: #47
(typescript 7) waits on @typescript-eslint, which still peers `<6.1.0`, and
#49/#50 wait on electron-vite 6 leaving beta, since 5.0.0 peers `vite ^5 ||
^6 || ^7`. Grouped, those now sit still instead of failing every week.

Refs #63, #51

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BernardJen
BernardJen merged commit 18a51d8 into main Sep 3, 2026
1 check passed
@BernardJen
BernardJen deleted the claude/pipeline-59e3dd branch September 3, 2026 19:51
BernardJen added a commit that referenced this pull request Sep 3, 2026
…updates (#70)

Every PowerPoint backend caches the slideshow position in module-level state
and refreshes it from PowerPoint across an `await` - an AppleScript round trip
on macOS, a PowerShell bridge command or a COM call on Windows. Nothing stopped
two of those from overlapping: the web remote can fire `next` from several
devices at once, a double-tap queues two `nextSlide()` calls, and the 500 ms
poll in server/socket.ts calls `getSlideInfo()` on top of both.

`require-atomic-updates` flagged seven sites where that state is read before an
await and written after it, and #64 had to land the rule as 'warn' to keep CI
green. This fixes the sites and turns it up to 'error'.

serializeAutomation() chains every call onto one promise, so a backend has at
most one command in flight - which is also what the PowerShell bridge's FIFO
response matching already assumed. Each backend exports its object already
wrapped, so main/index.ts and server/socket.ts share one queue rather than
getting a mutex each. Methods are listed explicitly instead of proxied, so
adding an unserialized method to the interface is a type error.

The mutex alone does not satisfy the lint rule, which is purely syntactic, so
each flagged site also re-reads its state after the await. That is deliberate
belt and braces: it keeps the invariant checkable instead of resting on the
wrapper still being there. nextSlide now commits slide and animation step in one
synchronous block (same result as before - the old code incremented the step
then zeroed it if the slide had moved), and localPresentationCopy is cleared
before the unlink is awaited rather than after.

The 500 ms poll needed a guard as a direct consequence: overlapping polls used
to just run concurrently, but under serialization a poll that outlives its tick
queues behind the next poll and every remote command, so a slow backend would
grow an unbounded backlog. It now skips a tick while one is outstanding.

Verified against a real PowerPoint 16.112.3 on macOS with a generated six-slide
deck (slide 3 hidden): openPresentation, gotoSlide, getSlideInfo,
closePresentation, concurrent and burst navigation, and the temp-copy cleanup.
With the serializer stubbed out, two concurrent gotoSlide calls interleave and
one is silently dropped - PowerPoint stays on slide 5 after a request for slide
2. With it in place the commands apply in order.

Not verified against a real install: startSlideshow, nextSlide, prevSlide and
stopSlideshow on macOS go through System Events keystrokes, and Accessibility
permission was not granted to the test process. Neither Windows backend was run
at all; their changes are the same three patterns applied identically.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BernardJen added a commit that referenced this pull request Sep 3, 2026
… them (#71)

Grouping typescript with the eslint tooling in #64 was right - as
@typescript-eslint peers both, a solo bump of either can never resolve - but
it converted a red PR into a red dependabot job.

The updater cannot build a resolvable lockfile for typescript 7 while
@typescript-eslint peers `>=4.8.4 <6.1.0`, so it aborts the dependency:

    Handled error whilst updating typescript: dependency_file_not_resolvable
    {message: "Error while updating peer dependency."}

and fails the whole run. That happened four times this evening (jobs
1556166625, 1556170248, 1556184655, 1556194840). Before grouping, the same
bump succeeded at the job level and produced #47, which then failed in CI
instead - the 08-31 typescript job ran green.

The group PR is still created regardless (#65 came out of one of those
"failed" runs), so the practical cost is a permanently red dependabot job
every cycle, which would hide a real failure later.

Ignoring the major stops dependabot attempting what it cannot complete. This
does not freeze typescript - 5.x minor and patch updates still flow, still
grouped with the eslint tooling. It also retires #47, open since 08-28 and
unpassable on any current release.

Lift this as soon as typescript-eslint widens its peer range; the grouping
then does what it was added for and bumps both together.

Co-authored-by: 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.

npm run lint has never worked: no ESLint config exists

1 participant