On macOS 26, UNUserNotificationCenter aborts when executables are run
from Contents/Resources because bundleProxyForCurrentProcess returns
nil.
Moving the orca-notification-status helper to Contents/MacOS next to
the main Electron executable ensures proper bundle resolution and
avoids immediate crashes.
* feat(onboarding): state-aware macOS notification permission step
The Set up notifications step showed a one-size-fits-all 'Open Mac
Settings' button that simultaneously fired the macOS permission prompt
and opened System Settings — two competing system UIs, with System
Settings unnecessary for the common fresh-install case.
Electron exposes no API to read macOS notification authorization, but
scheduling outcomes do reveal it: a silent probe notification's 'show'
event means permission is granted, 'failed' means delivery is blocked.
A new notifications:probeDelivery IPC runs that probe (cached via
passive delivery evidence and a persisted confirmation flag), and the
onboarding card now renders the real state:
- fresh install: the probe itself pops the native Allow dialog the
moment the step opens; the card flips to 'Notifications are enabled'
automatically when the user clicks Allow (silent 2.5s re-probes)
- blocked: amber card with an Open System Settings deep-link, which
also self-heals once the user flips the toggle
- granted: green confirmation card
The test-notification button now feeds the same card instead of the
ambiguous 'if no banner appeared…' toast during onboarding.
Co-authored-by: Orca <help@stably.ai>
* fix: don't log expected probe rejections while polling for permission
Co-authored-by: Orca <help@stably.ai>
* fix: amber warning styling + single stable dev bundle id for notifications
- Blocked card now uses the app's shipped amber idiom (tinted surface with
amber title/body) instead of white-on-amber-wash, which read muddy in
dark mode; macOS permission card split into its own module to stay under
the max-lines budget.
- Dev instances previously minted a unique macOS bundle id per
branch x Electron version, registering a new Notification Settings entry
every time ('Orca: <branch>' rows piling up forever) and pointing the
settings deep-link at ids System Settings can't resolve. All dev
instances now share com.stablyai.orca.dev: one Notification Center
entry, one permission grant covering every dev build.
Co-authored-by: Orca <help@stably.ai>
* fix: tighten macOS permission card copy
Body copy was one long sentence; now a single short instruction with
'Updates automatically.' as a separate dimmer line. Also repairs locale
catalog parity for keys introduced by commits rebased into this branch.
Co-authored-by: Orca <help@stably.ai>
* fix: drop 'Updates automatically.' line; ad-hoc sign dev app copies
The extra line read as confusing filler — the cards now carry one short
instruction each.
Dev Electron copies had broken code signatures (the Info.plist identity
edits invalidate the ad-hoc seal), which macOS punishes by refusing
Notification Center registration outright: every dev notification failed
with UNErrorDomain error 1, the app never appeared in System Settings >
Notifications, and the settings deep-link had nothing to land on. The dev
runner now ad-hoc re-signs the copied bundle after the plist edits
(bundleLayoutVersion bumped so stale unsigned copies are recreated).
Verified end-to-end: runner-built copy passes codesign --verify --deep,
probe delivery returns delivered, the onboarding card flips green in dev,
and the deep link opens the dev app's own notifications pane.
Co-authored-by: Orca <help@stably.ai>
* fix: drop confusing copy line; session-only permission evidence
Removes the 'Updates automatically.' line from both permission cards.
Also drops the persisted notificationDeliveryConfirmed flag: OS-level
permission changes between sessions, and a stale positive rendered a
false green card. Delivery evidence is now session-scoped only.
Documented detection ceiling (verified empirically on macOS 26): while
the permission dialog is unanswered — and when notifications are toggled
off in System Settings after being authorized — macOS accepts requests
and silently swallows them, with no public API (Notification Center
delivered-history and legacy ncprefs both included) able to distinguish
that from real delivery. 'failed' remains definitive for unsigned builds
and dialog-level denials.
Co-authored-by: Orca <help@stably.ai>
* feat: real macOS notification permission readout via native helper
Electron has no API for UNUserNotificationCenter authorization, and every
observable fallback lies: scheduling succeeds (and getHistory lists the
notification) even while macOS silently swallows display because the
permission dialog is unanswered or notifications were toggled off in
System Settings. The onboarding card therefore showed 'enabled' after the
user disabled notifications.
Adds native/notification-status-macos: a tiny Swift binary that prints
the app's real authorization status. It runs from inside the app bundle
(NSBundle resolves the bundle by walking up from the executable) and
embeds the app's CFBundleIdentifier in a __TEXT,__info_plist section so
every codesign --force pass — electron-builder's signing or the dev
runner's ad-hoc deep sign — derives the identifier macOS keys
notification records to. Spawning it from the app returns authorized /
denied / not-determined exactly matching System Settings.
notifications:probeDelivery now prefers this readout (authoritative,
silent), firing at most one dialog-trigger probe per session while the
decision is pending, and falls back to the previous delivery-probe
heuristics when the helper is unavailable. The card polls the readout
silently in every state, so toggling Allow notifications in System
Settings flips the card within a poll — both directions, verified live.
Test notifications also consult the readout so 'delivered' is no longer
claimed for swallowed notifications.
Packaged builds ship the helper via extraResources and sign it in
afterPack like the computer-use helper; dev copies compile it on demand
(swiftc, non-fatal when missing) with the shared dev bundle id.
Co-authored-by: Orca <help@stably.ai>
* feat: in-app fallback for swallowed notifications + permission card in Settings
- Dispatch now consults the authorization readout before creating a
native notification: when macOS would silently swallow it (denied or
prompt unanswered) it returns reason 'blocked-by-system' instead of
piling invisible notifications into Notification Center. The terminal
notification path surfaces that as a once-per-session in-app toast
with an Open System Settings action. Mobile fan-out is unaffected.
- Settings > Notifications now shows the same live permission card as
onboarding (moved to components/notifications/), polling the readout
so System Settings changes reflect within seconds, and the test
button updates it inline.
- Test sends that are blocked at the OS level now show the
settings-pointing failure toast instead of a generic error.
Co-authored-by: Orca <help@stably.ai>
* fix: hide macOS permission card while Orca notifications are disabled
A green 'Notifications are enabled' card next to a disabled Enable
Notifications toggle read as a contradiction — the card now renders (and
the readout polls) only while Orca's own notifications setting is on.
Also single-flights the authorization helper so simultaneous agent
completions share one readout process.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
46 commits from main. Conflict resolutions (branch structure wins, main
semantics grafted):
- pending-backlog cap (#7630): main's capPendingPtyData/droppedBacklog
2MB tail-trim EXCISED — this branch's droppedOutput sentinel mechanism
(O(1) drop + query salvage + settings-scaled cap + restore marker)
already bounds the unsent backlog and strictly supersedes it; carrying
both would race two cap policies over one buffer. Kept main's
makePtyDataPayload helper and exit-time pending flush (adapted to the
sentinel). Main's duplicate never-ACK cap test removed (covered by the
existing sentinel cap tests); its reload-gate tests kept with the
runtime mock extended.
- replay guard (#7661): main's blind REPLAY_GUARD_RELEASE_FALLBACK_MS
release dropped — this branch's probe-certified stall release covers
the same cold-restore reattach lockout without the auto-reply leak a
timed release can cause in agent TUIs. Main's three regression tests
KEPT, adapted to probe timing (explicit stallCheckMs).
- hidden-pressure scenario: kept this branch's STRICT 1500ms restore
budget (the background keep-tail global budget earns it; main relaxed
to 4s for CI drain overhead) + grafted main's timer-drift gate const.
- ssh-docker-relay-perf: kept this branch's ACK-stalled background PTY
test AND grafted main's new file-streams+git-churn test (4 tests).
- preload/renderer: droppedBacklog threading excised with the mechanism;
main's unrelated changes (ssh importConfig reAdopt, etc.) kept.
Gates: typecheck clean; ipc+daemon+terminal-pane+pane-manager suites
4,396 passed / 14 skipped.
Co-authored-by: Orca <help@stably.ai>
Hidden panes are exempt from pendingData flow control (main gate-drops
their bytes after ingestion), so N background agents ran unbounded ahead
on the one shared daemon->main stream socket (measured 192MB user-space
backlog) and visible-pane echo waited FIFO behind it — typing appeared
seconds late whenever several agents burst on a loaded machine
(8x512KB/s + 12 CPU spinners: p50 293ms fix-off; 12x1MB/s: 6.1s).
Mechanism (replaces producer pacing — no reveal catch-up, ever):
- Shallow socket write gate (128KB) + per-session fairness bypass bounds
echo latency by construction; kernel-flush refill sentinel keeps held
bulk draining at full speed (drain-only refill capped at ~8MB/s).
- Backgrounded sessions' queued output is keep-tail dropped (newest
512KB kept, in-order dataGap replaces the middle); a ~2MB GLOBAL
budget shrinks per-session keep-tails (floor 64KB) so a worktree
switch never waits behind the aggregate. Reply-eliciting query bytes
(DSR/DA/OSC probes) are salvaged from dropped spans.
- Notifications are structurally lossless: the daemon runs the same
shared scanners main uses (bell/OSC 133/pr-link/2031) over every byte
BEFORE drop decisions and relays facts in byte order; ordered
background markers hand scan authority back and forth, seeded with the
emulator's partial escape tail so a sequence split across the handoff
neither phantom-fires nor goes missing. Titles/agent-status stay
main-side (kept-tail convergent).
- Main: background = hidden AND no remote view subscriber (a live
mobile/web view is never thinned); on dataGap main resets cross-chunk
parse carries, drops the headless mobile mirror, and reuses the
hidden-drop model-restore marker.
Wire: three new stream events, tolerated within protocol v19 (old mains
ignore unknown events; old daemons never see the trigger). Kill
switches: ORCA_DAEMON_BACKGROUND_STREAM_DROP=0,
ORCA_DAEMON_SHALLOW_SOCKET_GATE=0.
A/B (pnpm bench:multi-workspace-typing): 8x512KB/s + 12 CPU workers
p50 293ms/p90 647ms -> 15/21ms (= baseline); 12x1MB/s 6,146ms -> 20ms;
light loads unchanged; zero missing echoes. Latin hidden-restore e2e
green (probe-verified aggregate-drain root cause). New deterministic
repro harness: tests/e2e/terminal-multi-workspace-typing-latency.spec.ts
+ CPU pressure workers.
Co-authored-by: Orca <help@stably.ai>
oxlint already fails any file over max-lines that is not suppressed, so the
only way to grow past the budget is to add an eslint/oxlint-disable max-lines
comment or a per-file max-lines bump in mobile/.oxlintrc.json. This adds a CI
gate that freezes the current set of suppressions (config/max-lines-baseline.txt,
355 grandfathered entries) and fails the build when a NEW one appears — with a
loud, actionable message pointing at 'split the file'. Existing oversized files
are untouched; the baseline may only shrink (pnpm check:max-lines-ratchet --prune).
Wired into the root lint script and as a dedicated pr.yml step. Unit-tested
(15 cases) and verified against all three failure paths + clean-tree pass.
Co-authored-by: Orca <help@stably.ai>
74 commits from main, including the STA-1278 ai-vault transcript perf work,
terminal flicker fix (#7540), renderer V8 heap raise (#7531), scrollbar
position preservation (#7472), and the per-delivery debug-snapshot
allocation removal (#7541).
Conflict resolutions (our structure, main's semantics):
- agent-detection: kept the split-module barrel; grafted main's Gemini/Pi
title-detection refinements (path-fragment false positives, legacy Pi
synthetic titles) into agent-title-core, dropped the superseded
PI_IDLE_PREFIX.
- orca-runtime wait-blocked detection: kept the throttled
scheduleWaitBlockedCheck (branch §1 optimization) and ported it to main's
new cached TerminalTailWaitState API; main's per-chunk cached scan stays
on the leaf mismatch path where it auto-merged. pty.tailWaitState is
deliberately never populated on this branch (throttle owns the pty
record), so the leaf mirror copy invalidates rather than shares — the
fromTail exactness contract is never violated by stale state.
- recordPtyRendererDeliveryPressure: took main's allocation-free peak
update (#7541) and derived per-pty in-flight from this branch's
cumulative sent/acked accounting instead of the removed
rendererInFlightCharsByPty map.
- rpc/methods/terminal detach: kept both sides — our
flushAllAckPendingOutput and main's exitWaiterAbort release.
- pty-connection: kept our single-policy handleCommandFinished (invariant)
alongside main's new sampleVisiblePaneForegroundAgent (bodies were
byte-identical where they overlapped).
- e2e pressure scenarios: kept the Phase-4 hidden-gate contract assertions
and the extracted pressure-script module; adopted main's
MAX_WORST_KEY_LATENCY_UNDER_LOAD_MS budget.
Also documents the renderer-pull delivery watchdog in the branch guide.
Gates: typecheck clean; ipc/daemon/terminal-pane/pane-manager suites 4256
passed; both fuzz suites pass.
Co-authored-by: Orca <help@stably.ai>
* Clarify Orca orchestration tool boundary and sidebar lineage
Add a "Tool Boundary" section to the orchestration skill, requiring
explicit Orca runtime state instead of generic subagent tools or
chat-only parallel workers. Also add tests to verify the tool boundary
and clarify sidebar lineage for same-worktree workers.
* Clarify worktree lineage guidance and parent-child boundaries
Update orchestration guidance and tests to clarify when to use child versus
top-level worktree lineages, and when to prefer same-worktree workers.
* Require stating the desired Orca lineage before creating a worktree from
an active feature branch.
* Limit child worktrees to conceptually stacked or dependent tasks.
* Prefer same-worktree workers unless isolated checkouts are explicitly
needed and do not require uncommitted changes.
Enable three unicorn rules — one correctness, two performance — and fix every
existing violation repo-wide so the rules pass as errors.
prefer-number-properties (76 sites)
- parseInt/parseFloat/NaN -> Number.* : safe aliases (autofixed).
- isNaN -> Number.isNaN (12 sites, hand-converted): global isNaN coerces its
argument, Number.isNaN does not. Verified every call site already passes a
number (Number.parseInt results, number-typed fields, Date.getTime()), so the
conversion is behavior-preserving today and guards against a future non-numeric
argument silently coercing.
prefer-array-find (26 sites)
- .filter(pred)[0] -> .find(pred); .filter(pred).at(-1) / .pop() -> .findLast(pred).
Drops the intermediate array and short-circuits.
prefer-array-index-of (5 sites)
- .findIndex(x => x === v) -> .indexOf(v).
Verified: typecheck (node/cli/web) clean, 53 affected suites pass (1679 tests),
oxlint clean repo-wide. mobile/ uses findLast safely (already ships ES2023
.toReversed()); config scripts and e2e helpers run on Node 24.
- Instruct workers to stop and idle or exit immediately after sending
`worker_done`, rather than running a 10-minute polling loop.
- Distinguish instructions based on worker kind: prompt-returning
agents should remain idle for re-engagement, while bare-shell
workers should exit.
- Prevent infinite polling overhead since the coordinator re-engages
workers via fresh terminal input instead of inbox polling.
* Revert "Preload the daemon windowsHide shim via --require; wrap promisify custom (#7499)"
This reverts commit 8f396badaf.
* Revert "Hide console windows for children of the node.exe-hosted daemon (#7486)"
This reverts commit f0fdd3a716.
* Revert "fix(daemon): relocate daemon host image out of the install-dir kill zone (#7473)"
This reverts commit f4faafa987.
* Revert "fix(pty): keep runtime dirs a surviving daemon still uses (#7463)"
This reverts commit 3cd23a13a1.
* Revert "Relocate node-pty ConPTY runtime outside the Windows install dir (fixes update-time terminal loss) (#7421)"
This reverts commit 509c41e2bf.
Conflict: src/renderer/src/lib/github-links.ts (parsing core lives in
src/shared/github-links.ts on this branch). Kept our shared-module
structure; grafted main #7371 semantics into it: exported the named
GitHubIssueOrPRLink type from shared, and the renderer wrapper keeps
normalizeGitHubLinkQuery with the stricter HTTP_URL_PREFIX_RE check and
the new directLink result field.
Also extracted seeded-test-repo.ts from tests/e2e/helpers/orca-app.ts:
the merged fixture crossed the max-lines cap, and the seeded-repo
helpers were a self-contained concept.
Auto-merged hot files verified: main-side changes are spawn-time CWD
canonicalization (#7334/#7403) and a mobile fit-override rebind hook
(#6596) - not on the delivery/restore path, so no bench required per
the branch guide. Throttled wait-blocked machinery intact.
Validated: typecheck clean; daemon + ipc/pty + terminal-pane + both
fuzz suites + github-links run - all failures are pre-existing
Windows-environment ones (identical on a main-based tree), zero
merge-caused.
Keep PetOverlay runtime keyframes out of localization so translated locales cannot invalidate sprite or bob animations.\n\nAlso adds regression coverage for translated-locale keyframe CSS and removes the stale generated locale entries.
Quality pass on main-process/build PRs merged 2026-07-03:
- ios-emulator-backend: resolve the serve-sim executable via a lazily-cached getter
instead of eagerly in the constructor. The bridge is built before the main window
is shown, so the one-time recursive copy + xattr subprocess (first launch after each
version bump) no longer blocks macOS startup for a feature that may go unused (#7174).
- index: collapse the two near-identical `{webContentsId, until}` reload flags
(expectedRendererReload / recoveryReloadInFlight) into one `createWebContentsTimedFlag`
primitive; behavior preserved, including consume-on-read for the recovery reload (#7290).
- check-reliability-gates: coerce gate.commands/testFiles/platforms/providers with an
`asArray` helper before `.includes`, so a hand-edited manifest with a missing/mistyped
field reports a validation failure instead of throwing an uncaught TypeError; extract
`hasCompleteRedGreenEvidence` for the duplicated status check (#7295).
- claude-pty: derive FABLE_WEEKLY_LABEL_RE from WEEKLY_RE.source so a future weekly-
wording change stays in one place and can't reopen the parsing gap it just closed.
- macos-tcc-login-shell: trim the 30-line flag-by-flag JSDoc to the two non-obvious whys
(TCC identity, env(1) SHELL re-assertion) per the repo comment guidance (#7003).
Typecheck, oxlint, oxfmt, `check:reliability-gates`, and touched unit suites all pass.
No max-lines disable per repo rules; extracted to
artificial-opencode-pane-interactions.ts. toReversed() and
import.meta.filename replace reverse()/fileURLToPath.
Co-authored-by: Orca <help@stably.ai>
Main inlined an OSC 133 parser with new 133;C onCommandStarted support;
our branch parses via the shared scanner so main-side side-effect facts
stay byte-identical. Resolution keeps the shared-scanner architecture and
adds 133;C to it; the foreground-agent tracker hooks into the branch's
single handleCommandFinished policy (bytes and sideEffect facts alike).
Co-authored-by: Orca <help@stably.ai>
rc kind can now append an extra prerelease identifier (rc.N.perf):
sorts above its base rc.N but below rc.N+1, so perf-branch RCs are
identifiable by clients without outranking the main RC series.
Co-authored-by: Orca <help@stably.ai>
Fixes#5579. Pass previous_tag_name to GitHub generate-notes so draft-release notes stop accumulating across releases; omit it safely on first release. Adds tag parse/compare + paginated tag fetch and regression tests.
Conflict resolutions:
- terminal-stream-protocol: Metadata keeps opcode 12 (shipped to mobile
in v1.4.120); branch-only Ack renumbers to 13.
- scheduler: keep HIGH_PRIORITY_MAX_WRITES_PER_DRAIN=8 (perf fix) over
main's 2 (#7139 landed there with the old constant).
- headless-emulator: keep chain structure (scanner classes, query-reply
forwarding); graft main's pathFlavor/remotePosixFileUriAuthority OSC7
options through TerminalOscCwdTitleScanner. Main's
TerminalPrivateModeTracker is logic-identical to the chain's
TerminalMouseModeMirror (kept, with pre-filter).
- orca-runtime: keep chain factory; graft main's pathFlavor wiring and
snapshot cwd fallback; keep both teardown lines.
- pty-connection: keep both chain reconcile and main's ordered-seq
tracking; clear the chain's restored-snapshot baseline on pty exit
(same seq-domain boundary main's reset enforces) — fixes revive with
restarted seq counter dropping chunks as duplicates.
- subscribe-buffer tests: updated to merged semantics — main's inline
overflow recovery ships current data as the FIRST snapshot; the
chain's resized/pending-output-overflow follow-up remains as backstop.
Validation: typecheck clean; 7,548 tests green across daemon/ipc/
runtime/pane-manager/terminal-pane/shared/mobile-transport (1 documented
pre-existing WSL failure).
Co-authored-by: Orca <help@stably.ai>
Revives the terminal model/view architecture chain (hidden view parking,
hidden delivery gate, side-effect authority in main, model query authority
— all kill-switched, defaults on) on top of orca-performance's terminal
performance fixes. 34 conflicted files resolved with these decisions:
- Both sides' features kept and kill-switchable. orca-performance's fixes
preserved exactly: parse-clocked scheduler drains (9e8bb2243), windowed
retained-tail redraw (4e08a28cd), throttled wait-blocked check
(66f20258e), #7139/#7150 cooperative drain, backlog caps, wedge guards,
and probe-certified replay release.
- orca-runtime.ts onPtyData: chain's per-PTY title tracker + side-effect
facts replace the inline OSC title blocks; our scheduleWaitBlockedCheck
and redraw-cursor plumbing retained; refreshPtyForegroundAgent re-grafted
into applyTrackedPtyTitle on status transitions.
- pty.ts: chain's hidden-gate drop sites, restore markers, initiallyHidden
spawn marking, ConPTY DA1 record, and view-attribute/delivery-interest IPC
wired through our newer spawn/delivery structure. The chain's flat 2 MB
pending cap was NOT taken: our scrollback-scaled cap (#7150, empty-drop +
droppedOutput sentinel) wins and now also emits the chain's one-per-episode
pty:modelRestoreNeeded 'pending-cap' marker when the gate is enabled. Our
8 MB renderer in-flight high water kept over the chain's 2 MB.
- pty-connection.ts: chain's gate sync, model-restore channel, side-effect
fact consumer, and post-restore seq reconciliation composed with our
hidden-startup query grammar, synchronized-frame latency machinery, and
probe-certified restore. Phase 6 (skip-grammar deletion) intentionally NOT
taken: our side evolved that grammar past the chain's base and #7150
machinery builds on it; with the gate on it is dormant (main drops hidden
bytes), with switches off it remains the byte-identical fallback.
- Shared module moves adopted (agent-title-*, github-links, bell detector,
command-code, PR-link detector) with our newer bounded regex-free OSC
parsers wired through the chain's scanner classes; our pi-synthetic/Devin/
MiMo agent additions ported into the split modules.
- daemon-pty-adapter: chain's sleep-restore cache + our checkpoint cooldown,
teardown flag, and oscLinks-carrying cold-restore payload combined.
- rpc/methods/terminal.ts: chain's ACK-gated multiplex output + recovery
snapshots composed with our budgeted mobile snapshots and resize re-stream.
- Tests adapted only where they encoded superseded cadence: watermark counts
(2 MB->8 MB), pending-cap trim -> empty-drop sentinel, initial snapshot
frame seq (layout seq -> snapshot output seq), mock module paths.
Contract tests: 881 passed. Renderer terminal suites: 1895 passed.
Full main runtime+ipc: 2974 passed. Typecheck (3 configs) clean.
Pre-existing baseline failures (pty-subprocess WSL, PR sidebar, Linear
prompt, SidebarToolbar) are unrelated and fail identically on 7839fb9db.
Co-authored-by: Orca <help@stably.ai>
* Verify Windows app executable signing
* Isolate Windows signing verifier tests
* Handle direct Windows installer extraction
---------
Co-authored-by: Neil <neil@stably.ai>
Migrate fileURLToPath(import.meta.url) / dirname(...) boilerplate to the
native import.meta.dirname / import.meta.filename, then enable the rule
at error so new code stays on the native form.
The oxlint autofix rewrites the expression but leaves the now-unused
node:url / node:path imports behind (which the already-enabled
no-unused-vars=error would then flag), so this commit also removes those
34 orphaned imports — trimming the named import where other names are
still used, deleting the line where it was the sole import.
Scope is build scripts + Node-env tests only (config/scripts, tools/
benchmarks, *.test.{ts,mjs}, vitest configs); zero shipped runtime code.
The native properties are exact equivalents (Node >= 20.11; repo is on
24), so behavior is unchanged.
Verified: oxlint 0 errors tree-wide (root + mobile), oxfmt clean,
typecheck (node+cli+web) + mobile tsc pass, root vitest 22825 passed /
0 failed, mobile vitest 1018 passed. Exercised the rewritten scripts
directly: build:relay (6 targets), ensure-native-runtime,
verify-macos-entitlements all run correctly with import.meta.dirname.
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules
Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day
minimum-release-age supply-chain guard; nothing here needs it). The
bump is a no-op on the existing config.
Enable 3 error rules (backlog autofixed to zero in this commit) and
4 warn rules (surface signal without gating CI):
error (autofixed, behavior-preserving):
- unicorn/prefer-node-protocol (~1531 sites: bare builtin -> node:)
- typescript/no-import-type-side-effects (~36: all-inline-type -> import type)
- unicorn/no-array-reverse (19: copy-then-reverse -> toReversed)
warn (real signal, current fires are test-only/correct):
- unicorn/no-array-fill-with-reference-type (aliasing footgun guard)
- typescript/no-unsafe-function-type (bans bare Function type)
- unicorn/prefer-array-flat-map (map().flat() -> flatMap())
- unicorn/prefer-regexp-test (.match() in bool ctx -> .test())
mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix
ran from root and covered mobile/ too.
Verification (all green): oxlint 0 errors (root+mobile+aux configs),
oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed,
builds (electron-vite + web + cli) succeed. node: rewrites confirmed to
skip embedded SSH/CLI string payloads (AST-only); all toReversed sites
verified to operate on fresh copies or write-once locals.
* chore(lint): bump mobile oxlint to 1.71 so inherited rules parse
mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which
lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since
mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile &&
oxlint' failed to parse the new rule. Bump mobile to match root (1.71).
Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit
pass, vitest 978 passed / 0 failed.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
Machine translation rendered the abbreviation "PR" (pull request) as "relaciones públicas" (public relations) across 32 Spanish UI strings — e.g. "Reabrir relaciones públicas" (Reopen PR), "Crear relaciones públicas" (Create PR), "Abrir cheques de relaciones públicas" (Open PR checks).
Adds the missing `es` rule to LOCALE_PHRASE_FIXES. Because "relaciones públicas" is a real Spanish phrase (unlike the CJK-only ko/zh patterns 홍보/公关), the rule guards on the actual `PR`/`PRs` token via a new optional `whenEnMatches` RegExp guard in applyPhraseFixes, so it never fires on unrelated English that merely contains a "pr" substring (approve, preview, press). `whenEnMatches` is backward compatible — existing `whenEnIncludes` rules are unchanged.
Regenerates the 32 affected es.json leaves. Adds a vitest spec covering the rewrites and a negative case proving the token guard ignores genuine "public relations" strings.