* feat(plugins): add Todoist import plugin with Import/Export launcher
* fix(plugins): fix cross-chunk subtask loss in todoist-import + hardening
* fix(plugins): clamp non-finite Todoist durations + review polish
* feat(plugins): add Eisenhower priority mapping to Todoist import
Replace the single "map priorities to p1-p3 tags" checkbox with one
mutually-exclusive control (Nothing / p1-p3 tags / Eisenhower matrix).
The new Eisenhower option reuses SP's built-in urgent/important tags by
title (p1 -> urgent+important, p2 -> important, p3 -> urgent, p4 -> none),
so imported tasks populate the Eisenhower Matrix board with no new tag
and no new plugin API. Collapsing Todoist's single priority axis onto the
2-D matrix is opinionated, so it stays opt-in and off by default.
Requested in the review of #8882.
* fix(plugins): harden Todoist import data integrity
* fix(plugins): improve Todoist import feedback
SHA-pinning the actions in wiki-sync.yml (#8868) pushed the
setup-python `uses:` line to 84 chars, tripping yamllint's default
80-char line-length rule and failing the Lint job on every wiki push
and every PR touching wiki paths.
Relax the yamllint step to accept the 40-char SHA pins and their
single-space `# vX.Y.Z` version comments. Tightening the comments to
two spaces instead would push the already-80-char pin lines over the
limit, so raising the ceiling is the correct fix.
* ci(actions): SHA-pin remaining workflow actions
Replace the mutable derekprior/add-autoresponse@master ref in
issue-open-auto-reply.yml with an inline actions/github-script snippet
to remove the third-party code-injection risk in CI. SHA-pin the 4
remaining tag-pinned actions in wiki-sync.yml (checkout, setup-python)
to match the rest of the workflows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* wrong sha for the github-script v9
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The E2E Sync PR gate ran the full SuperSync + WebDAV suites on PRs with
no sync changes. The detect job diffed `${BASE_SHA}...HEAD` where HEAD is
the refs/pull/N/merge commit (PR merged into current base) and BASE_SHA is
the event-time pull_request.base.sha. As the base branch advances while a
PR is open, that sha goes stale; since it stays an ancestor of the merge
commit, the three-dot range expands to every commit merged into the base
since the PR opened, tripping the sync patterns on unrelated churn.
Diff the PR head against its merge-base with the live base (HEAD^1) so the
range is exactly the PR's own changes, regardless of base movement. No
extra fetch needed: HEAD^1 and the PR head are both parents of the
checked-out merge commit, and their merge-base is reachable under
fetch-depth: 0. Verified on PR #8722: 250 leaked files -> the real 7.
Supersede the SuperSync-only gate with e2e-sync-pr.yml, which shares
one change-detection job and one frontend build across both suites and
exposes two independent required checks (SuperSync E2E Gate, WebDAV E2E
Gate). Each suite runs only when its own path set changed; sharing the
build avoids a third frontend build per sync PR.
The SuperSync client (packages/sync-providers/src/super-sync) drives
the @supersync suite but was absent from both the new PR gate's detect
pattern and the nightly push paths, so a PR editing only the client
could merge green without the suite ever running. Add the package to
both. Also switch the detect grep to a here-string to remove a
theoretical SIGPIPE under pipefail.
Runs the SuperSync E2E suite on PRs only when sync-affecting code
changed, via a detect/build/shard/gate job shape. The always-running
gate job is the required status check, so path-filtered runs never
leave a required check pending. Nightly scheduled run remains the
backstop for cross-cutting changes the path heuristic misses.
Fold per-push Android dev distribution into build-android.yml (which
already builds a signed APK on every master push) instead of a second
workflow that duplicated the whole build. On non-tag master pushes it
stamps a dev versionCode into the working copy of build.gradle and, after
the existing build, ships the play APK to the Play 'internal' track so a
fresh build auto-updates on the phone. Internal testing skips review, so
builds land in minutes; dev builds (code base+1..999) and releases
(base+9000) share the track but stay distinct by versionCode. All new
steps are gated to refs/heads/master (non-tag); the tag/release path is
untouched, and every dev step is continue-on-error so it can never break
the release-critical build.
versionCode = last stable release's code + commits-since-that-tag
(first-parent), staying in the 999-slot band above the release and below
the next version's base. The base is derived from the last stable tag
(not the working tree), so a version bump landing on master before its
tag is pushed can't make dev codes jump ahead and then regress. Logic
lives in tools/android-dev-version-code.js (reusing getAndroidVersionInfo)
with unit tests; it degrades to skip on any anomaly.
Reuses existing keystore/Play/Unsplash secrets; no new secrets.
* feat(sync): offer E2EE at setup for file-based providers
File-based providers (WebDAV, Nextcloud, Dropbox, OneDrive, local file)
support optional client-side encryption but, unlike SuperSync, have no
mandatory-encryption upload guard — so their first setup sync would ship
plaintext before a user could enable E2EE.
Offer to set an encryption password during first-time setup and persist it
as part of the SAME config save: the key goes to the provider's privateCfg
and isEncryptionEnabled to the global config, atomically with isEnabled.
The normal first sync then encrypts from the first op via the standard
download-first flow — no separate snapshot-overwrite and no plaintext-upload
race. Skipping keeps the existing unencrypted behavior; works offline too.
Reuses DialogEnableEncryptionComponent in a new side-effect-free
collectPasswordOnly mode (returns the password, writes nothing), with a
provider-aware Skip action for the disableClose setup modal.
* test(sync): skip file-based setup encryption prompt in webdav e2e helper
Fresh file-based setup now opens the optional "Encrypt before first upload?"
dialog before the config is persisted, so setupWebdavSync would stall on the
disableClose modal. Dismiss it via a new Skip selector so every WebDAV spec
proceeds unencrypted, exactly as before; encryption specs still enable it
afterwards via enableEncryption().
* test(sync): add e2e for file-based setup-time encryption
Adds a @webdav @encryption spec that configures WebDAV with the encryption
password set in the setup dialog, then asserts the first upload's remote
sync-data.json does NOT contain the task title in plaintext (compression is
off by default, so a plaintext upload would) — proving the first sync is
encrypted — and that a second client with the same password decrypts and
receives the task without overwriting the remote.
setupWebdavSync gains an encryptAtSetup option (fills the new dialog instead
of skipping it); adds e2e selectors on the setup dialog password/confirm/
submit controls.
* ci(e2e): allow targeting the webdav job via workflow_dispatch grep
The webdav e2e job hardcoded --grep "@webdav"; add a webdav_grep dispatch
input (default @webdav, mirroring the supersync job) so a specific WebDAV
test can be run on demand.
* test(sync): fix remote sync-file path in setup-encryption e2e
Non-production builds nest the file under a /DEV segment
(sync-providers.factory.ts). The raw-fetch assertion omitted it and 404'd;
the CI trace confirmed the app uploaded to <folder>/DEV/sync-data.json and
that the first upload was encrypted.
* test(sync): cover joining an unencrypted remote with setup-time encryption
Adds the data-safety edge case for file-based setup-time E2EE: a client that
sets a password at setup while joining a remote that already holds UNENCRYPTED
data. Asserts the join reads and preserves the existing data (does not overwrite
the remote with the joining client's empty state) and that a subsequent write
upgrades the remote to encrypted (no plaintext titles remain).
* fix(sync): never upload plaintext ops for E2EE-mandatory providers
SuperSync's first-time setup ran an initial sync (dialog-sync-cfg save ->
sync(true)) BEFORE the user chose an encryption password, so all local ops
(incl. issue-provider credentials) were uploaded to the server in cleartext.
Completing setup deleted them; aborting left them stored indefinitely, breaking
the E2EE promise (GHSA-9v8x-68pf-p5x7).
Add an optional `isEncryptionMandatory` capability to OperationSyncCapable
(true for SuperSync) and refuse to upload in the op-log upload path while no
usable key is configured. Downloads still run (merge-first) and the encryption-
enable flow performs the first, encrypted upload, so the setup flow and prompt
are unchanged. File-based providers, where unencrypted sync is a legitimate
user choice, leave the flag unset.
* fix(sync): fail closed on plaintext snapshot for E2EE-mandatory providers
Multi-review hardening for GHSA-9v8x-68pf-p5x7. The op-upload guard closes the
reported leak, but SnapshotUploadService.deleteAndReuploadWithNewEncryption could
still push a plaintext snapshot for SuperSync on two adjacent paths: the (today
UI-unreachable) disable-encryption flow, and a keyless import declaring
isEncryptionEnabled:true. Reject an unencrypted snapshot for an encryption-
mandatory provider before any destructive deleteAllData, so the "never transmit
plaintext" invariant holds regardless of caller.
Also lower the mandatory-encryption upload-skip log from warn to normal: it is an
expected by-design skip during the pre-encryption setup window and would
otherwise fire on every auto-sync cycle.
* fix(sync): consolidate op-log into the encryption-enable snapshot
Follow-up to the GHSA-9v8x-68pf-p5x7 upload guard. With the guard, first-time
SuperSync setup leaves the whole local history unsynced until encryption is
enabled; the enable-snapshot then represents that full state, but the ops were
re-uploaded incrementally on top of it on the next sync (redundant server op-log
bloat, and previously untested at whole-history volume).
deleteAndReuploadWithNewEncryption now captures the pending ops the snapshot
subsumes (before the destructive delete, under runWithSyncBlocked + a modal
dialog so the set is stable) and marks them synced after a successful upload —
mirroring planRegularOpsAfterFullStateUpload in the op-log upload path, which
this direct snapshot upload bypasses. Also fixes the same latent redundancy in
the enable-from-settings-with-pending-ops flow.
Adds a multi-client e2e: local task history exists before first-time encrypted
setup, then a second client with the same password receives exactly those tasks
with no duplicates, conflicts, or errors.
* ci(e2e): add optional grep input to manual SuperSync e2e dispatch
* fix(sync): capture subsumed ops before state snapshot to prevent mark-synced data loss
deleteAndReuploadWithNewEncryption captured getUnsynced() AFTER the full-state
snapshot, so an op created in that window was marked synced yet absent from the
snapshot — silently lost. Capture the unsynced set first: every marked-synced op
is then guaranteed present in the snapshot, and a concurrent op arriving after
the capture is left unsynced and re-uploaded next sync (idempotent by op id).
* test(sync): mock WebCrypto so mandatory-encryption guard test reaches the guard
The 'enabling without a usable key' case passed isEncryptionEnabled: true but did
not mock WebCrypto, so the availability check threw WebCryptoNotAvailableError in
non-secure CI before the /unencrypted snapshot/ guard under test.
* chore(deps): bump @ngrx/* to 21.1.1 and @capacitor/* to latest 8.x
- @ngrx/{store,effects,entity,store-devtools,schematics} 21.1.0 -> 21.1.1
(moved as a set; the family pins each other's peers exactly)
- @capacitor/{core,cli,android,ios} -> 8.4.1, @capacitor/keyboard 8.0.1 -> 8.0.5
Angular left at 21.2.x: it is already the latest stable 21.x, and Angular 22
is blocked by @ngrx (no Angular-22-compatible release exists yet).
* ci(dependabot): group coupled npm families into single PRs
npm updates had no groups, so lockstep families (ngrx pins its own peers
exactly, Angular/Capacitor move by major together) arrived as separate
single-package PRs that can never resolve npm ci alone. Group @ngrx,
@angular, @angular-eslint, @capacitor, and typescript-eslint so each
family updates as one coherent PR.
* chore(deps): refresh safe in-range tooling + tier-2 bumps
Tier 2 (package.json):
- @material-symbols/font-400 ^0.44.10 -> ^0.45.5 (icon font, 0.x minor)
- eslint-plugin-jsdoc 62.9.0 -> 63.0.10 (its only breaking change is
'drop Node 20'; we run 22; plugin is not wired into eslint.config.js
so there is no lint-rule impact)
In-range leaf refresh (lockfile only; already permitted by caret ranges):
- @playwright/test + playwright 1.60.0 -> 1.61.1
- jasmine-core 6.2.0 -> 6.3.0
- nanoid 5.1.11 -> 5.1.16, fs-extra 11.3.5 -> 11.3.6
- baseline-browser-mapping 2.10.32 -> 2.10.40
- eslint-plugin-prettier 5.5.5 -> 5.5.6
Angular 21.2.x patches, electron-builder, @typescript-eslint and
prettier/stylelint are intentionally left for 'npm update' in a real
terminal: their build-tooling dep trees can't be regenerated cleanly
here (the sandbox prunes cross-platform binaries).
* style(habit-tracker): remove empty .header-spacer rule
Dead empty block left over from 394e554bd0 (compact-view refactor);
triggered stylelint block-no-empty. The .header-spacer div stays in the
template — it's positioned by the parent grid and needs no own styles.
Promoting the Android build from internal to production was only triggered
by 'release: published', which also fans out to AUR, Docker, snap, and web.
An Android-only release thus forced publishing a full GitHub release; undoing
it by deleting the release left the AUR PKGBUILD pointing at a removed .deb
(404). Add a workflow_dispatch trigger so the promotion can run on its own.
* feat(azure-devops): add optional WIQL override for auto backlog import
Adds an optional autoImportWiql config field to the Azure DevOps issue
provider plugin. When set, it fully replaces the generated auto-import
backlog query, so the user controls scope, state filtering and ordering
(e.g. to filter by iteration path, area path or work item type, or to
match custom done-state names). When empty, behavior is unchanged from
today's scope-based query, so it is fully backward compatible.
This mirrors the Jira provider's autoAddBacklogJqlQuery (a full query the
user owns) rather than appending a fragment, which can only narrow the
hard-coded English done-state exclusion and cannot accept a real exported
WIQL Select..From..Where statement.
Adds a vitest suite covering the default scopes, quote escaping, the
verbatim override and the blank-fallback, wires the plugin into the
plugin-tests CI matrix, and updates the issue integration comparison wiki.
Closes#7674
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DY1ESWymmU9x9ykLNaRHGH
* test(azure-devops): use node test env, drop unneeded jsdom dependency
The plugin tests exercise no DOM API, so vitest's default node
environment is sufficient (matching the clickup and google-calendar
provider plugins). Removing jsdom prunes ~530 lines of transitive
devDependencies from the lockfile.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
CI run 27780507031 failed when electron's npm postinstall timed out
(ETIMEDOUT) downloading its prebuilt binary from GitHub. Harden every
Electron build/package job against that transient failure.
- New composite action .github/actions/setup-electron-build: node 22, the
git-over-HTTPS workaround, and a combined npm-registry + electron-binary
cache (warm runs skip the flaky download). The electron cache dir is
OS-specific, so all three are listed; actions/cache ignores absent ones.
- Each job retries `npm i` via nick-fields/retry (the repo's existing
idiom) under shell:bash, dropping node_modules/electron between attempts
so npm actually re-runs the postinstall (verified: npm skips scripts for
an already-present package).
- mac/windows run `npm install @nx/<platform>` before `npm i`; since
`npm install <pkg>` reifies the whole tree, electron downloads in that
step, so the nx workaround is folded into the same retried command.
Routed through the composite: electron-smoke (linux), build.yml
(linux/mac/windows), manual-build (windows/mac), test-mac-dmg.
super-sync-server unit tests (vitest) previously ran only nightly and on
push to master via e2e-scheduled.yml, so server-side sync fixes were never
gated by a PR check. Add a dedicated workflow with a workflow-level paths
filter (super-sync-server, shared-schema, and its transitive sync-core dep)
so the suite runs on PRs that touch server code and stays off the rest -
matching the repo's plugin-tests.yml separate-workflow convention.
* feat(sync): show actionable error on persistent WebDAV 409
When a WebDAV PUT keeps returning 409 Conflict even after the parent
collection is created, the Base URL / Sync Folder Path is misconfigured
(the classic Synology / raw-WebDAV setup mistake). Previously the raw
"HTTP 409 Conflict" (or a bare "Unknown error") reached the user with no
hint at the cause.
Throw a dedicated WebDavSyncFolderUnusableSPError with a privacy-safe,
actionable message (no path, no response body) at the spot that already
detects this condition. Mirrors NetworkUnavailableSPError: a fixed
user-facing message matched by instanceof.
* ci(release): revive contributors section in release notes
* fix(plugins): stop leaked timers on plugin disable via onUnload hook
* fix(plugins): harden onUnload teardown hook after review
* refactor(plugins): group lifecycle registers into options object
* fix(plugins): close onReady stale-guard gap and timer interleave race
Comment the Cloudflare preview URL on any issue the PR closes (via a
Closes/Fixes #N keyword), in addition to the existing PR comment, so the
original reporter can validate the fix without checking out the PR.
- Resolve linked issues from the PR's closingIssuesReferences.
- Find-or-update one bot-authored marker comment per issue; in-place
edits are silent, so subscribers are notified at most once.
- Reconcile any duplicate comments from a concurrent run.
- Guard on a non-empty issue list and deploy URL; add issues:write.
Scheduled SuperSync shard 4/6 failed on a transient Docker Hub timeout
pulling postgres:15-alpine (auth.docker.io request canceled) before any
test ran. Wrap the WebDAV and SuperSync 'docker compose up' steps in
nick-fields/retry (3 attempts) so a single registry blip no longer fails
the job; per-attempt timeout_minutes also bounds the unbounded SuperSync
health-wait loop.
* ci: auto-submit iOS and macOS App Store builds for review
The iOS and Mac App Store workflows previously stopped after uploading the
build to App Store Connect via altool, leaving version creation, "What's New"
and submission as manual steps.
Add fastlane lanes (ios/mac release) that upload the prebuilt .ipa / MAS .pkg
using App Store Connect API key auth (reusing the existing notarization key
secrets), push release notes derived from build/release-notes.md, wait for
processing, submit for review and flag automatic release on approval.
Final version tags submit for review; pre-release tags (RC/beta/alpha) and
manual runs only upload the build. Listing metadata and screenshots remain
curated by hand in App Store Connect.
https://claude.ai/code/session_014c1W1mX7tfvFzpZ6wyWzsJ
* ci: wire iOS and Mac Store workflows to fastlane submit lane
The previous commit added the fastlane lanes but the workflow edits were not
applied. Replace the altool validate/upload steps in the iOS and Mac App Store
workflows with the fastlane submit lane: install fastlane, generate the App
Store "What's New" notes and run `fastlane <platform> release` with App Store
Connect API key auth.
Also extend the Mac workflow's harden-runner egress allowlist with the
rubygems and App Store Connect endpoints used by fastlane.
https://claude.ai/code/session_014c1W1mX7tfvFzpZ6wyWzsJ
* fix(ci): harden Apple App Store auto-submission after review
Address review findings on the iOS/macOS App Store automation:
- Tag gating: submit only when the tag has no "-" (final semver), instead of
denylisting RC/beta/alpha. GitHub Actions contains() is case-sensitive and
the repo's RC tags are mostly lowercase (-rc.N), so the old guard would have
auto-submitted release candidates to production review.
- Fastfile: set skip_metadata so deliver no longer reads back and re-uploads
curated listing fields; push only "What's New" via an inline release_notes
hash. Warn against verbose mode (can dump the API key).
- Gemfile.lock: add arm64-darwin/x86_64-darwin platforms so bundle install
works on the macOS runners.
- Workflows: install deps via pinned ruby/setup-ruby (bundler cache), and
resolve the artifact path with a strict nullglob check (exactly one match)
instead of ls | head.
- release-notes script: tighten emphasis regexes so stray * / _ (globs,
snake_case) survive, anchor footer patterns so legitimate "download" lines
are not dropped, and drop a duplicate mkdir.
- Docs: document the hyphen-based gate, single-use build numbers, automatic
release behavior and inline validation.
https://claude.ai/code/session_014c1W1mX7tfvFzpZ6wyWzsJ
* fix(ci): correct deliver metadata + setup-ruby version (second review pass)
Two bugs introduced by the previous review-fix commit, both confirmed against
upstream source:
- Fastfile: skip_metadata: true makes deliver's upload_metadata return early
(verified in fastlane 2.225.0 deliver/lib/deliver/upload_metadata.rb), so the
"What's New" notes were never uploaded. Revert to metadata_path pointing at a
dir that contains only <locale>/release_notes.txt; load_from_filesystem reads
only that file (next unless File.exist?) with no remote read-back, so other
listing fields stay untouched. Removed the now-unused inline release_notes
helper.
- Workflows: ruby/setup-ruby throws when ruby-version is unset and no
.ruby-version file exists (it does not fall back to system Ruby). Pin
ruby-version: '3.3' in both workflows.
Docs updated to match the corrected metadata approach.
https://claude.ai/code/session_014c1W1mX7tfvFzpZ6wyWzsJ
* fix(ci): remove invalid wait_for_uploaded_build from deliver lanes
wait_for_uploaded_build is a pilot/upload_to_testflight option, not a deliver one. Passing it to upload_to_app_store makes fastlane raise on the unknown key and fail both iOS and macOS release lanes on every run. deliver already waits for the build to finish processing during submit (select_build -> wait_for_build_processing_to_be_complete), so no replacement is needed.
Also pin the ruby/setup-ruby comment to its resolved version (v1.310.0), and slice the App Store release notes by code point so a multi-byte character is never split at the 4000-char cap.
---------
Co-authored-by: Claude <noreply@anthropic.com>
GitHub migrates the macos-latest label to macOS 26 between
2026-06-15 and 2026-07-15. Pin the release, Mac App Store, and
manual macOS build jobs to macos-15 to avoid the image swapping
mid-release; macOS 15 is maintained for ~1 more year. build-ios.yml
already runs on macos-26 and is left unchanged.
* chore(plugins): re-bundle document-mode and document Stage A path
Reverts the unbundling from b0cae69ffe. Stage 0 (gzip + throttle, shipped
in 84625be849) handles size; document-mode remains opt-in per context, so
cross-context conflict risk is bounded. Stage A (keyed plugin-persistence
API, issue #7749) is the documented future path for closing the LWW gap
on different-context concurrent edits — picked up when conflicts are
observed in practice.
Design sketch with multi-reviewed phasing lives in
docs/plans/2026-05-23-stage-a-keyed-plugin-persistence.md. Predecessor
plan's "Future work" section now links to it.
* test(plugins): cover document-mode bundled load and PLUGIN_USER_DATA LWW
Follow-ups from the multi-review of 199e816479's re-bundling decision:
- E2E smoke test asserts document-mode appears in plugin management so
a typo in BUNDLED_PLUGIN_PATHS fails loudly.
- Spec exercises PLUGIN_USER_DATA conflict resolution end-to-end, which
previously relied on analogy to REMINDER (same array+null branch) but
was never directly asserted after the migration off 'virtual'.
- Stage A plan risks: stale-editor-view gap surfaced by the review;
PluginHooks.PERSISTED_DATA_UPDATE already exists in the API but is
never dispatched host-side — wiring it is the path to a fix.
- background.ts: comment marks the known gap at the registerHook site.
* docs(plugins): plan for wiring PERSISTED_DATA_CHANGED hook
Designs the host-side wiring for the currently-dead
PluginHooks.PERSISTED_DATA_CHANGED so plugins can react to remote-driven
changes to their persisted data. Multi-reviewed twice; v4 trims scope to
host-only (no plugin adoption in this design) and preserves the
multi-review insights as seeds for the follow-up doc-mode adoption
tracked at issue #7752.
Implementation lands in a separate PR.
* fix(ci): pin wiki-sync second checkout to actions/checkout@v5
v6 stores credentials in a $RUNNER_TEMP file referenced via
includeIf.gitdir matchers; the wiki push intermittently runs without
credentials on github-hosted runners (run 26335467138 failed with
"could not read Username for 'https://github.com'"). v5 uses the
path-independent extraheader, which is stable for this second
checkout pattern.
Leaves the primary Checkout Code on v6.0.2 so Dependabot keeps it
current.
Refs: actions/checkout#2321, #2359
* test: harden e2e failure signals
Fail otherwise-passing E2E tests on browser runtime errors, keep Playwright retries disabled, preserve Docker E2E exit codes, and make plugin/WebDAV setup failures hard failures instead of logged or skipped conditions.
* test: harden provider e2e runners
Make WebDAV and SuperSync runner scripts require provider readiness, preserve cleanup and argument forwarding, and fail manual sync clients on uncaught page errors.
* ci: require providers for scheduled e2e
Set required-provider flags in scheduled WebDAV and SuperSync jobs, and remove the duplicate provider runner scripts while keeping local npm aliases inline.
* test: catch e2e teardown pageerrors and tighten fixture
- closeClient now asserts runtime errors AFTER context.close() so
pageerrors emitted during teardown (Angular destroy hooks, late RxJS
errors) are captured instead of dropped. Matches the pattern in
guardContextCloseWithRuntimeErrorCheck.
- test.fixture.ts isolatedContext now spreads Playwright's merged
contextOptions instead of destructuring 23 fields by hand. Future
option additions propagate automatically; the page fixture uses the
shared attachPageErrorCollector and only fails on pageerror (not
console.error, which is too noisy). Guards against a configured 0
timeout being treated as undefined.
- plugin-loading.spec.ts second test now hard-asserts that the plugin
menu entry reappears after re-enable, matching the first test instead
of silently logging when not visible.
* test(sync): stabilize ImmediateUploadService spec
Two complementary fixes for flaky failures observed under full-suite
random-order runs where the upload pipeline silently never fires:
- Pin navigator.onLine = true in beforeEach (restored in afterEach).
isOnline() inside _canUpload reads navigator.onLine directly. The
keyboard-layout spec replaces the whole navigator and the is-online
spec spies on it; if order or restoration ever drifts, every "should
fire upload" test fails trivially while the "should NOT" tests pass.
- Replace tick(2100) with tick(2000); flush(). The await chain inside
withSession() (provider.isReady, withSession entry, uploadPendingOps,
optional LWW re-upload) requires more microtask drain than tick's
fixed-time window reliably provides under load. flush() drains the
pipeline regardless.
* test(e2e): guard skipOnboarding init script against data: frames
The new page-error collector started failing plugin specs because
addInitScript runs in every frame — including the empty data:text/html
iframe that plugin-index swaps in on destroy — and localStorage access
in a data: URL throws SecurityError. Wrap the four setItem calls in
try/catch so the helper noops in storage-less frames.
GH Action will incorrectly mark discussions without the question-answer
format as answered when that is not possible. The action should be fixed
but restricting to the correct categories will also avoid this problem.
Re-ordered the steps to avoid a discussion that is marked as answered
just before the expiry to be false marked as OUTDATED.
Simplified messages.
Shortened waiting period for answered discussions.
The root lockfile pins app-builder-lib's transitive minimatch via the
`overrides` field. npm 10.9.7 (bundled with Node 22 in setup-node@v6)
flags this as drift and fails `npm ci`, while npm 11 accepts it.
ci.yml's main test job uses `npm i`, which tolerates the drift without
mutating the lockfile on disk.
Plugin-Tests has been red on every PR since 2026-05-08 for this reason.
The inner `npm ci` for plugin-specific deps stays strict.
Fetch the GitHub release body for the tag and publish it as an artifact
plus a job-summary block, so the maintainer can paste it into Partner
Center's "What's new" field during the manual appx upload. Falls back to
a pointer message when the release isn't yet published.