build(eslint): add the missing config, and stop capping eslint's major - #64
Merged
Conversation
`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>
This was referenced Sep 3, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
npm run linthas 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 ciwithERESOLVE, not at any check step — worth stating plainly, because it's easy to assume a "bump eslint" PR fails on lint:eslint-plugin-reacthas no release that accepts eslint 10 — thenexttag is a stale7.8.0-rc.0peering^3 || ^4. It was also dead weight: with no config, nothing had ever referenced it.What changed
eslint-plugin-react, bumped@typescript-eslint/*to^8.69.0— the first line peeringeslint ^8.57 || ^9 || ^10. Nothing in the tree caps eslint's major any more.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.lintscript — it still passed--ext, removed in eslint 9.currentPresentationPathin both Windows backends — written twice, read never.macos.tsgenuinely reads it as a fallback, so it stays there.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-reactto a version requiring vite 8, and each fails precisely because the other is a separate PR.Still blocked upstream — not fixed here
@typescript-eslintpeerstypescript >=4.8.4 <6.1.0, and latest8.69.0still does.electron-vite@5.0.0(latest stable) peersvite ^5 || ^6 || ^7. Only6.0.0-beta.1accepts vite 8.Grouped, both now sit still instead of failing every week.
Known warnings
0 errors, 18 warnings.
require-atomic-updatesis set towarnrather than disabled — it flags seven pre-existing interleaving hazards in the automation backends, where module-level slide state is read before anawaitand 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 areno-explicit-anyat the COM and PowerShell boundaries.Verification
lint,typecheck,test(7 passed),buildand a cleannpm ciall pass locally. Separately confirmed that settingeslintto^10.9.0on 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.jsonmoved here.Closes #63
🤖 Generated with Claude Code