Skip to content

Run LocalThought integrations entirely in the browser - #1401

Merged
michielbdejong merged 4 commits into
codex/localthought-api-pluginsfrom
codex/browser-integrations
Sep 9, 2026
Merged

Run LocalThought integrations entirely in the browser#1401
michielbdejong merged 4 commits into
codex/localthought-api-pluginsfrom
codex/browser-integrations

Conversation

@michielbdejong

@michielbdejong michielbdejong commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Run the LocalThought catalog, tenant-secret handoff, OAuth return, paginated imports, review and apply entirely in the browser. A user can paste TENANT_SECRET and import into the local Store/OPFS database with no AtomicServer HTTP instance available. LocalThought remains the remote OAuth/API proxy.

The browser owns credential rotation and serializes requests with Web Locks. Syncables runs in the Atomic WASM bundle using in-memory catalog documents. Installation and schema recovery use local identity lookup, and the shipped mapper produces ordinary reviewed proposals. The native /integration-proxy/* handlers and server Syncables dependency are removed. Existing server-held connections need reconnecting in the browser; legacy direct integrations, actions and schedules are outside this migration.

Validation

  • Passed the complete mock browser journey with AtomicServer requests blocked: local drive creation, consent, WASM pagination, review/apply, and all five records present after reload.
  • Generated WASM smoke verifies typed ontology, timestamps, pagination, provider failures and duplicate-page refusal.
  • 10 LocalThought mapping/transport tests and 3 installation lookup tests pass.
  • Frontend TypeScript checking and Atomic WASM build pass.
  • Companion Syncables: 142 native tests, Clippy and wasm32 compilation pass. Companion proxy: 39 tests pass.

Live provider OAuth on this browser path has not been verified. Deploy the proxy CORS change before live use. Connection credentials remain in browser storage outside the synced graph; the tenant secret is used in tab memory only.

Related Issues

Checklist

  • Add changelog entry linking to issue, describe API changes
  • Add or update tests if needed
  • Update docs if needed

@michielbdejong
michielbdejong force-pushed the codex/browser-integrations branch from be0fde3 to cb81923 Compare September 9, 2026 12:50
@michielbdejong
michielbdejong changed the base branch from develop to codex/localthought-api-plugins September 9, 2026 12:51
Michiel de Jong and others added 2 commits September 9, 2026 15:46
* Add browser Devonian issue tracker sync demo

* Use browser tenant connections and test two-way issue sync

* Record live proxy OAuth verification and repository access blocker

* Run one-way Google Calendar sync entirely in the browser (#1398)

* Show one-way Google Calendar imports in a calendar view

* Verify browser-only Calendar sync through shared mock proxy

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
@michielbdejong
michielbdejong merged commit d24722b into codex/localthought-api-plugins Sep 9, 2026
0 of 2 checks passed
@michielbdejong
michielbdejong deleted the codex/browser-integrations branch September 9, 2026 13:47
michielbdejong added a commit that referenced this pull request Sep 9, 2026
)

* Add LocalThought catalog plugins with typed paginated imports

* Record successful live GitHub import verification

* Add bounded Calendar event imports through Syncables

* Record successful scoped Calendar live import

* Fix LocalThought lint and retain upstream preview timing checks

* Fix shared Cargo cache locking across CI containers

* Allow the full-app compiler test to finish on hosted CI

* Queue main CI runs instead of replacing pending validation

* Run LocalThought integrations entirely in the browser (#1401)

* Run LocalThought integrations entirely in the browser

* Run focused Pets E2E on the browser integration branch

* Add browser Devonian issue tracker sync demo (#1399)

* Add browser Devonian issue tracker sync demo

* Use browser tenant connections and test two-way issue sync

* Record live proxy OAuth verification and repository access blocker

* Run one-way Google Calendar sync entirely in the browser (#1398)

* Show one-way Google Calendar imports in a calendar view

* Verify browser-only Calendar sync through shared mock proxy

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
michielbdejong added a commit that referenced this pull request Sep 11, 2026
* feat(integrations): add trivial demo Pets plugin

First commit toward rebuilding the API-plugins direction from PR #1383 on
feat/plugin-model. Pets is a static, provider-free demo integration (five
pets, no OAuth, no secrets) that walks every touch point a real API plugin
needs: a code-first ontology, an import mapping into the shared sandbox, a
reproducible plugin.js bundle, an installable connection with a table and
view, integration discovery in the UI, and a server-side sandbox test.

See planning/api-plugins.md for the direction, including the next step:
discovering providers through a localthought proxy instead of hand-writing
integrations/<name>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix: discover local Ollama and restore feedback hover (#1389)

* fix: subscribe to live collaborator profile updates (#1390)

* Allow recovery-code secret reveal and additional passkey enrollment

* Security audit: fix sync, authentication and rights gaps, bump advisories (#1384)

* docs(planning): security and code-quality audit, September 2026

Consolidated findings for server, lib, browser, desktop, CLI and CI:
unauthenticated /iroh-sync, SYNC_PUSH without per-entry authorization,
agent impersonation via /agents/{key} URLs on arbitrary hosts, committed
API keys, forwarded-host trust, plus dependency advisories and a list of
sloppy code with file:line references.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(security): close the sync, authentication and rights gaps from the September audit

Findings from planning/security-audit-2026-09.md fixed on this branch:

- Remove the committed editor config carrying live API keys, plus the
  tracked pnpm-store and vitest cache files; ignore them.
- /iroh-sync requires a signed agent with write on the drive (or the
  policy's leave to bring a new drive here) and only answers POST. Dial-
  side owner trust is confined to the drive that was dialed for.
- SYNC_PUSH checks every entry against the admitted drive: an existing
  resource must carry it as its stored drive stamp, a new one must
  resolve to it via its parent or its own stamp.
- Legacy https://host/agents/{key} subjects bind the key in the path to
  the signing key in both header auth and commit validation; agent
  subjects on other hosts are refused instead of fetched.
- check_append no longer falls back to the new resource's own write
  array, and a parentless did:ad:agent genesis is accepted only from
  that key or the node's own agent. Regression tests for both.
- Request origin is taken from Host/X-Forwarded-* only for the
  configured domain, its tenants or loopback; the WebSocket handler uses
  the same origin. Unit tests.
- /forget-peer requires write on a drive the peer was paired for (known
  peers now record those) or the node's own agent.
- Desktop and Android bind the embedded server to 127.0.0.1 by default.
- The data-browser accepts a node-reported portalUrl only as https (or
  http on localhost), pins the portal a device token was issued by, and
  routes every portal navigation through safePortalUrl. URI values and
  downloadUrl reject javascript:/data:/vbscript: schemes.
- BLOB_RESPONSE bytes must hash to the requested key; downloadUrl is
  escaped in the SPA meta tags; the plugin-ui query string is attribute-
  escaped; multipart uploads and search limits are bounded; LanceDB
  filter literals are quoted; origin() no longer panics; image rendition
  parameters are quantized before caching; config.toml is written 0600;
  plugin RPC ignores foreign message sources; the session cookie is
  Secure on https.
- Sloppy code: ValueComp comma-case, unawaited resource.save() calls, the
  CLI agent race, a commented-out tool block, the duplicate EventManager,
  SIGNER set twice, bitwise & on bools, the empty authorization test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(server): treat *.localhost names as loopback in the request-origin check

CI serves the app at atomic.localhost for a server whose domain is
`atomic`, so the browser signs its WebSocket AUTH and requests for that
origin. The host allowlist added for the forwarded-host fix rejected it,
fell back to the configured origin, and every signed request failed
("requestedSubject ... does not name this server"). Names under
.localhost resolve to loopback by definition (RFC 6761), so accept them
like localhost itself.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(browser): mock setManagedDeviceToken in the managed session test

logoutManagedSession now also drops the device token, so the api mock
from develop's new session test needs that export too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(security): bind Iroh AUTH to the responder, close the remaining rights gaps, bump advisories

Second round of fixes from planning/security-audit-2026-09.md:

- Iroh AUTH: the dialer signs `drive#<responder node id>` and the accept
  side verifies its own node id as the connection's challenge, so a proof
  captured by one responder cannot open another. A proof without a node
  id is honoured only from a peer this node paired with.
- check_append: a parentless non-agent DID is allowed only when it has no
  parent at all, or when its drive stamp names a drive the agent may
  append to; a non-DID top-level Drive needs the node's own agent.
- Path-only auth signatures are no longer accepted; the signer's Agent
  resource is created only after the commit is accepted; genesis certs
  with a parent or drive must match the document; did: resources are
  indexed only into their own drive's watched queries; Agent and
  SharedConfig redact secrets in Debug.
- Plugin zips and bookmark bodies are fetched through the SSRF guard
  with size caps; EPHEMERAL Loro payloads are relayed only from a
  subscriber; the ACME flow returns errors instead of panicking and a
  daily task renews the certificate on disk; /plugin-list and plugin UI
  files are read as the calling agent (the data-browser signs the list
  request) and the plugin parameter is validated; default_service logs
  at debug.
- The Flutter bridge deletes the database only on a corruption error;
  the atomic-saas checkout no longer persists its PAT; release.yml has
  per-job permissions; notarization secrets go through env; the desktop
  devtools feature is opt-in.
- Dependencies: wasmtime 47.0.4, h2 0.4.19, quinn-proto, rustls-webpki
  0.103.13; @tiptap/* 3.30, @modelcontextprotocol/sdk 1.30 and the
  transitive build tools, after which pnpm audit reports nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* docs: changelog entry for the security audit fixes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(lib): upload integration tests create the agent's private drive

The fixture fell back to the server's root URL as the upload parent when
the config carried no initialDrive. A fresh server has no resource at
that URL since the key-derived drive became the default, and the
tightened check_append now refuses a child whose parent is not on the
server, so the roundtrip test failed with "Parent ... not found".
Materialize the private drive through Store.ensurePrivateDrive, the
same path the data-browser takes at sign-in, and upload into that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* Fix desktop workspace discovery and restore feedback

* style(browser): fix the lint errors develop gained in #1389 and #1390

`pnpm lint` failed on develop's own head after those merges: missing
blank lines in websockets.ts, LocalOllamaDiscovery.tsx and the two new
e2e specs, and three shadowed names in username-live.spec.ts. The CI
pipeline stops at the first failing package, which hid the rest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(data-browser): give the whole-app compiler test a 60 second budget

It transforms every TSX file in the app: about two seconds on a
workstation and on the self-hosted runner, nine on a two-core hosted
runner, where vitest's default five-second budget failed it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* revert(lib): drop the drive-scoping of DID resources in watched queries

For a `did:` query, `QueryFilter.drive` is `drive_prefix_from_subject`
of the queried subject, which is that subject itself rather than the
drive root. Comparing it with a resource's `drive` stamp excluded every
legitimate row, and the query_aggregates tests failed on the first CI
run that reached them. Back to develop's index; C17 moves to the
report's not-fixed list with what a correct fix needs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* test(lib): drop the drive-scoping test that went with the reverted C17 change

It asserted the watched-query index behaviour the previous commit
withdrew, so it failed on the first CI run after the revert.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(lib): do not steal our own OPFS lock during a slow cold start

Ported from #1386. When the leader election window (2 s) elapses while
this tab already holds the lock and its worker is still importing WASM
and opening OPFS, the election treated the silence as a ghost leader,
logged the "stealing OPFS lock" warning and re-requested the lock with
steal. On a loaded CI runner every fresh page hit that path, and the
browser-diagnostics gate from #1382 fails a spec on any warning, so the
light e2e suite was red on develop and on this branch. Skip the steal
when our own worker already exists; the settle wait below still covers
the genuine ghost case. Adds #1386's unit test for the cold start.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* fix(data-browser): skip the SaaS logout call on a FOSS server

Ported from #1386. Signing out posted /api/logout to whatever
getManagedApiBase resolved to, and on a self-hosted node that is the
node's own origin, which answers 405. The browser logs that as an
error, and the diagnostics gate from #1382 fails the sign-out smoke
spec on it (the only failure left on this branch's hosted run).
logoutManagedSession now returns early when no control plane is known:
no linked portal, no build-time API base, no remembered portal and no
compiled-in portal URL. The local device token is still dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* chore(browser): dedupe prosemirror-model and prosemirror-view

The @tiptap bumps in this PR pulled prosemirror-model 1.25.11 in next
to the 1.25.7 that @tiptap/pm still resolved, and the two copies made
`pnpm typecheck` in data-browser fail on incompatible Node types in the
RTE chunks. `pnpm dedupe prosemirror-model prosemirror-view` collapses
them (and, as dedupe does, other duplicates to versions the lockfile
already carried: vite 8.2.2, codemirror state and lint, picomatch).
Typecheck, lint, the data-browser unit tests and the vite build pass;
a frozen offline install is consistent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tcSpZhjA3SgzcEfRdUr67

* Fix formatting and lint checks for desktop restore PR

* Validate sync entries before persistence and bind discovery authentication

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Fix per-drive hosting status, metadata and collaboration UX (#1386)

* Cancel automatic Vault work when the account session ends

* Improve collaboration profiles and managed subscription UX

* Fix nodeless profile loading and cold database startup warnings

* Settle lint and formatting after develop integration

* Allow cold CI time for whole-app compiler regression check

* Scope Cloud Server hosting and sync status to the selected drive

* Preserve drive display metadata and scope managed billing requests

* Prepare 0.41.0-beta.6 release (#1393)

* Prepare 0.41.0-beta.6 versions and release gates

* Record full beta.6 E2E results and release blockers

* Fix release E2E regressions in sync and plugin loading

* Finalize beta.6 release notes after passing E2E

* test(integrations): cover Pets setup flow

* fix(server): rustfmt pets_tests.rs

CI's fmt check failed on the multi-line assert! in pets_tests.rs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* ci: run Pets browser journey on its branch

* fix(ci): mount integration sources for browser build

* fix(ci): alias /browser for integrations' tsconfig extends in jsBuild()

Mounting /integrations (previous commit) fixed the UNRESOLVED_IMPORT
errors, but every integrations/*/tsconfig.json extends the repo-root-
relative ../../browser/tsconfig.build.json, which doesn't resolve
inside the container: browser mounts at /app, not /browser. jsTest()
and integrationCertificationReport() already hit this and fix it with
`ln -s /app /browser`; apply the same fix to jsBuild(), which backs the
production vite build, e2e and netlify-preview pipelines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(i18n): settle translation catalogs for the Pets integration card

The e2e run showed the Pets card rendering with a blank heading and
description text: wuchale renders an untranslated string as empty
rather than falling back to source text, and these new literal strings
(IntegrationDiscovery.tsx's bundledIntegrations() entry) were never
extracted into the catalogs. Settled by running the app, per AGENTS.md's
translation-catalog guidance (matches the dev server's fixed point,
unlike the standalone `wuchale --clean` extractor).

ConnectPets.tsx's own strings (only reachable once the setup dialog is
opened against a real drive) still need the same treatment in a
follow-up commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(i18n): settle translation catalogs for ConnectPets.tsx

Same issue as the previous commit, for the strings only reachable once
the Pets setup dialog is actually opened (Install/Reinstall demo pets,
the description paragraph, Open Pets). Settled by running the app
against a real dev-drive session and opening the dialog, so this
matches the dev server's fixed point exactly (verified: purely
additive, 20 lines per locale, no reordering).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(pets): restore production labels and display pet names

* fix(e2e): give the Pets install-and-apply assertion room to actually finish

Root-caused the CI failure at "Apply 5 changes": not a hang, not a
missing translation, and not an artifact of a locally-underbuilt
server (ruled that out by rebuilding atomic-server with the real
wasm32-wasip2 plugin runtime and rerunning). Installing genuinely
takes ~24s — pluginClassesFor's six sequential lookups, two
ensureSchema calls, three ensureInstallationResource calls, then the
sandboxed plugin run itself — measured locally on a fresh, otherwise
idle server via the real `playwright test`, not a synthetic script.
The dialog's default 10s expect timeout is tuned for interaction
latency, not this one-time setup cost. Same targeted-timeout pattern
already used elsewhere in this file (vault-backup-restore.spec.ts,
onboarding.spec.ts, etc.).

Verified: `playwright test -g "Pets installs its demo table"` passes
end to end (~47s) against a real dev-drive session with the actual
embedded plugin runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(ci): install wasm32-wasip2 before building the e2e server binary

The Pets E2E job consistently failed at "Apply 5 changes" never
enabling, even with a generous timeout. The server log in the job
output shows why: `/plugin-run` 500s with "this server was built
without the plugin runtime... Rebuild with the wasm32-wasip2 target
installed."

rustBuild()'s e2e path enables the `wasm-plugins` cargo feature, whose
build.rs compiles atomic-plugin-runtime for wasm32-wasip2 as a nested
cargo build. Without that target's std lib installed, the nested build
fails and build.rs treats it as an optional degradation — it embeds an
empty runtime instead of failing the outer build, so the gap is silent
until something actually calls /plugin-run. rustTest() already adds
this target for the identical reason; rustBuild() (which builds the
binary the e2e Playwright suite actually talks to) never did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(e2e): type plugin test fixtures to unblock CI

* fix(ci): mount testdata/ and lib/defaults/tasks.json for lib tests

jsBuild()/jsTest() only mounted testdata/pairing-request.json as a
single file and never mounted lib/defaults/tasks.json outside the
Vite-only /app/lib-defaults alias. Newly added
browser/lib/src/plugin-manifest.test.ts,
plugin-plan.fixtures.test.ts, and task-schema.test.ts read these
repo-root fixtures directly via readFileSync/readdirSync (not
Vite-resolved), so `vitest run` failed with ENOENT for all three in
CI while passing locally against a full checkout.

plugin-plan.fixtures.test.ts also readdirSync()s the whole
testdata/plugin-plans/ directory, which a single-file mount can't
provide, so testdata/ is now mounted whole (it isn't an OS path, so
unlike /lib there's no collision risk). lib/defaults/tasks.json stays
a single-file mount, matching the existing genesis_test_vectors.json
precedent, to avoid touching /lib itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(ci): bump rust-target cache volume to unstick the plugin runtime

The wasm32-wasip2 fix (20368ff) didn't take: the Pets E2E run on
that exact commit still failed with the identical "this server was
built without the plugin runtime" 500. build.rs's build_plugin_runtime()
only declares `rerun-if-changed` on plugin-runtime/{src,wit} and the
ATOMICSERVER_SKIP_PLUGIN_RUNTIME env var — nothing tells cargo that
"the wasm32-wasip2 target just became installed" should invalidate its
build-script fingerprint. Every prior CI run had already cached an
empty embedded runtime (from before that target existed) in the
rust-target-v3 volume, so cargo kept trusting that stale fingerprint
and never re-ran the nested wasm32-wasip2 build to notice the target
was now there.

This is the same class of staleness bug TOUCH_WORKSPACE_SOURCES
already documents fixing twice before (the -v2 and -v3 renames on this
same volume, for the same "cached artifact looks newer than fresh
sources" reason) — same fix, one more rename to force a clean
re-evaluation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(ci): isolate the WASI plugin build from musl compiler settings

* fix(data-browser): give the full-app oxc-transform sweep real headroom

src/oxc-react-compiler.test.ts's "transforms every app TSX/JSX file
without a fatal error" test compiles every TSX/JSX file under src/ one
at a time in a single it() block, with vitest's default 5000ms budget.
That budget was already tight and now consistently times out in CI
(reproduced twice in a row on the same commit, ~38s total suite
duration but this one test alone exceeding 5s) — this is deterministic
under CI load, not a flake, and the file count only grows over time.
Give it its own 20s timeout; the assertions are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* fix(data-browser): fix oxfmt formatting from the previous timeout fix

My previous commit's manual reformatting for the it() timeout argument
didn't match oxfmt's expected style, which failed the format-check
lint step. Move the explanatory comment onto a named constant instead
of a trailing comment after the test body, which oxfmt formats cleanly
and reads better besides. Verified with oxfmt --check, oxlint, and a
local vitest run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkL48idFqkuYw8n7KdH6Dj

* Revert "fix(data-browser): fix oxfmt formatting from the previous timeout fix"

This reverts commit 740d603.

* Revert "fix(data-browser): give the full-app oxc-transform sweep real headroom"

This reverts commit d59d4d2.

* Add LocalThought catalog plugins with typed paginated imports

* Record successful live GitHub import verification

* Add bounded Calendar event imports through Syncables

* Record successful scoped Calendar live import

* Fix LocalThought lint and retain upstream preview timing checks

* Fix shared Cargo cache locking across CI containers

* Remove temporary Pets E2E workflow

* Allow the full-app compiler test to finish on hosted CI

* Run LocalThought integrations entirely in the browser

* Run focused Pets E2E on the browser integration branch

* Add browser Devonian issue tracker sync demo

* Use browser tenant connections and test two-way issue sync

* Show one-way Google Calendar imports in a calendar view

* Verify browser-only Calendar sync through shared mock proxy

* API plugins: LocalThought connections and typed Syncables imports (#1394)

* Add LocalThought catalog plugins with typed paginated imports

* Record successful live GitHub import verification

* Add bounded Calendar event imports through Syncables

* Record successful scoped Calendar live import

* Fix LocalThought lint and retain upstream preview timing checks

* Fix shared Cargo cache locking across CI containers

* Allow the full-app compiler test to finish on hosted CI

* Queue main CI runs instead of replacing pending validation

* Run LocalThought integrations entirely in the browser (#1401)

* Run LocalThought integrations entirely in the browser

* Run focused Pets E2E on the browser integration branch

* Add browser Devonian issue tracker sync demo (#1399)

* Add browser Devonian issue tracker sync demo

* Use browser tenant connections and test two-way issue sync

* Record live proxy OAuth verification and repository access blocker

* Run one-way Google Calendar sync entirely in the browser (#1398)

* Show one-way Google Calendar imports in a calendar view

* Verify browser-only Calendar sync through shared mock proxy

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Allow reconnecting Devonian trackers and record live write blocker

* Preserve and render exclusive date-only all-day event ranges

* Recognize namespaced all-day properties in installed Google calendars

* Document all-day validation and remaining Google Calendar import gaps

* Document successful live browser issue sync verification

* Add reviewed two-way Google Calendar event edits (#1408)

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Support recurring Google Calendar meetings and exceptions (#1409)

* Preserve and render exclusive date-only all-day event ranges

* Import and render recurring calendar series with exceptions and timezone support

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Keep local integration resource reads off the server (#1407)

* Fix local integration reads reaching the server

* Fix integration formatting errors blocking CI

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Connect LocalThought platforms without pasting tenant secrets (#1417)

* Connect LocalThought integrations without pasting a tenant secret

* Satisfy demo callback statement-spacing lint

* Apply Rust formatting required by CI

* Format calendar recurrence library for workspace CI

* Clarify automated coverage and live verification records

* Lint the plugin feature set used by CI tests and deployment

* Fix Node MCP loading and complete plugin CI feature coverage

* Declare certification compiler and report failed checks

* Align compiler dependency with workspace override

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Externalize platform integrations and extend metadata-driven imports (#1419)

* Load Clockify and Notion integrations from Devonian

* Load OAuth token endpoint from trusted metadata

* Remove server tests for extracted integration bundles

* Restore integration regression tests during migration

* Follow OpenAPI links across sync collections

* Describe only root sync parameters

* Validate full Moneybird traversal fixture

* Consume platform lenses from Devonian

* Make integration OAuth provider-driven

* Keep import budgets in consumer and preserve mixed record fields

* Pin generated OAuth provider metadata

* Apply catalog import selections explicitly

* Handle declared absent objects and share link response context

* Document generic platform traversal regression coverage

* Extract GitHub and Calendar platform code

* Verify explicit selection removes inherited query defaults

* Validate per-item link selection and query removals

* Update Calendar import coverage ownership

* Load platform UI from Devonian registry

* Restore neutral external action test fixture

* Run external platform tests against pinned package

* Neutralize embedded platform host fixtures

* Document pinned external platform regression coverage

* Run external platform regressions in JS CI

* Render platform UI from external metadata

---------

Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>

* Preserve bundled plugins and add configurable integration proxy settings

* Fix integration test root, local reads, and reconnect isolation

* Fix regression test statement spacing

* Make integration certification independent of CI mount paths

* Split general CI hardening out of the integration feature

* Restore required shared test fixture mounts in CI

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joep Meindertsma <joep@ontola.io>
Co-authored-by: Michiel de Jong <michielbdejong@ontola.io>
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.

1 participant