Skip to content

feat: improve dynamic workspace compatibility - #425

Open
veryCrunchy wants to merge 44 commits into
mainfrom
feat/dynamic-workspace-compatibility
Open

feat: improve dynamic workspace compatibility#425
veryCrunchy wants to merge 44 commits into
mainfrom
feat/dynamic-workspace-compatibility

Conversation

@veryCrunchy

@veryCrunchy veryCrunchy commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

  • expand native groupware read/write support for Contacts, Calendar, Tasks, and Notes, including safe DAV multiget fallback, task completion, timed-due preservation, and quoted ETag handling
  • expose trusted dynamic writes at the workspace root even when an app has no matching read route, while keeping route-bound writes contextual and provenance-gated
  • preserve same-label create actions for different resources, classify imports from their declared request schema, bind terminal named mutations to a proven sibling collection, and retain compiler-proven overview roots such as Tables
  • add exact signed-package write adapters for Chores teams and invitations, Budget accounts, and Music playlists
  • make create/update/delete recovery work for APIs that return bodies without a usable location header or require the selected record identity in the request body
  • add first-house Pantry recovery, scoped Android write-test safety, emulator-safe demo session import, and self-signed/private-CA transport support
  • keep Office documents in the embedded Nextcloud web editor while native-capable apps remain native

Document-only Office follow-up at 3aee820f

  • Office entries now open a native DAV document browser, not a Nextcloud app dashboard. Remove the dashboard URL fallback and WebView account-authentication mode.
  • Embed only the selected document's core Direct Editing session. Fix top-level navigation to that session, block popups and clicked frame navigation, and return Back to native screens. Request/page-load guards cover paths missed by navigation interception.
  • Keep Preview and Edit separate. Secure server-advertised MIME support now controls edit eligibility, including PDFs and formats outside the built-in Office family list; fresh identity, version, and write permission still gate editing.
  • Keep editor actions accessible when the thumbnail is loading or fails. Cached browser listings require online confirmation before document selection.
  • Clear cookies, cache, and web storage between editing sessions. A general Web version entry and native deep linking remain outside this Office-focused change.

Validation for this revision:

  • 60 focused tests passed with no failures or skips, covering native Office browsing, stale/cancelled responses, document navigation boundaries, provider/MIME selection, and file actions.
  • Android compilation, :androidApp:lintDev, :androidApp:assembleDev, and bash tools/check-repository.sh passed on the build host.
  • Rendered and visually inspected the production native browser with synthetic folders, documents, spreadsheets, slides, and PDF entries at 390x844 and 1280x800.
  • Changed source files matched the remote build inputs by SHA-256.
  • Real-phone editor/save testing is still pending. Providers that require a different top-level URL, including federated redirects, remain blocked; this is not a claim of universal suite compatibility.
  • The local demo remains stopped.

Earlier Office follow-up at b341bf84

  • Route verified Office app navigation to an explicit Android web integration. Other dynamic apps remain native.
  • Select document editors from the server's secure Direct Editing registry and exact MIME types, including multiple advertised providers. Do not assume every server uses richdocuments.
  • Add a visible system-app opener for PDFs and other document previews, with preparation errors and retry.
  • Request a fresh Direct Editing session when retrying a failed editor load, because the initial token is single-use.
  • Preserve same-origin navigation, approved-certificate checks, account isolation, and subdirectory installation paths.
  • Document the Office-only web integration and its unverified live compatibility boundary.

Validation for this revision:

  • Build host: 58 focused tests across Office workflow/request selection, app routing, file actions/integration, Android editor request validation, and embedded origin handling.
  • Build host: :androidApp:assembleDev, :androidApp:lintDev, and bash tools/check-repository.sh passed.
  • Verified the remote source files against the local committed files using SHA-256.
  • The broader local Android UI unit suite encountered missing sqliteJni in unrelated media-ledger tests. This follow-up does not claim a full-suite pass or verified end-to-end Office save behavior.

Earlier compatibility evidence

  • ran signed-contract reachability audits for Deck, Tables, Cospend, Budget, Cookbook, Music, Pantry, Mail, and Chores
  • verified all Chores and Music mutations are surfaced; Pantry house creation and its core workspace mutations are surfaced
  • verified native Cookbook recipe create/read/update/delete against the demo and recovered native forms for URL import, reindex, configuration, and category rename
  • verified disposable server-side CRUD and cleanup for Contacts, Calendar, Tasks, Notes, Tables, Cospend, Budget accounts, and Music playlists
  • verified Deck board create/read/update and expected soft-delete behavior
  • installed the signer-matched dev.obiente.nextcloudnative.dev APK in place on the Android emulator; the credential-encrypted data inode remained unchanged and no write scope remained installed
  • retained the earlier real-device evidence for native Chores mutation and cleanup, Contacts loading, and private-CA recovery; Office editor open/save behavior is not verified for the current revision

Earlier validation

  • integration/nextcloud-demo/tests/test-nextcloud-demo.sh
  • bash -n tools/nextcloud-demo.sh
  • tools/nextcloud-demo.sh validate
  • bash tools/check-repository.sh
  • bash tools/test-prerelease-update-contract.sh
  • git diff --check
  • signed live compatibility tests for Pantry, Chores, Cookbook, Budget, Music, and the cross-app reachability matrix
  • focused regression suites for dynamic navigation and root-read safety
  • clean build-host run: ANDROID_HOME=/opt/android-sdk ./gradlew --no-build-cache --max-workers=4 :ui:desktopTest :contractAcquisition:test :androidApp:testDevUnitTest :ui:lintDebug :androidApp:lintDebug :androidApp:assembleDev
  • 2,740 desktop tests passed with 1 skipped; Android unit tests, lint, and the exact dev APK assembly passed
  • verified APK package, version, signer certificate, and SHA-256 before adb install -r
  • final hosted Linux/Android build, Windows MSI validation, capture refresh, security scan, and deployment preview passed on head 78af985d

Scope

This improves the generic dynamic compatibility framework and validates a broad representative set of real apps. It does not claim that every version of every third-party Nextcloud app is already exhaustively covered. Mail write E2E still needs a configured mail account, and the current Office revision still needs maintainer phone/cloud open-save testing across providers. The local demo is stopped at the maintainer's request. No personal-cloud write testing was performed for this revision.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 15:59 Destroyed
@gitguardian

gitguardian Bot commented Aug 27, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@obiente-cloud

obiente-cloud Bot commented Aug 27, 2026

Copy link
Copy Markdown

Obiente preview

NC Native · 718fe42e6221 · Ready

Open preview

View in Obiente

Obiente updates this comment as the preview changes.

@veryCrunchy
veryCrunchy marked this pull request as ready for review August 27, 2026 16:00
@veryCrunchy
veryCrunchy force-pushed the feat/dynamic-workspace-compatibility branch from 99198e2 to edfbc14 Compare August 27, 2026 16:03
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 16:04 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 16:11 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99198e2c5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/NextcloudNativeApp.kt Outdated
Comment thread tools/nextcloud-demo.sh
Comment thread tools/nextcloud-demo.sh Outdated
@veryCrunchy
veryCrunchy force-pushed the feat/dynamic-workspace-compatibility branch from 4189371 to 9aa79e0 Compare August 27, 2026 16:14
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 16:14 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 16:16 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc129fd90b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/nextcloud-demo.sh Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 19:38 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 19:43 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 96d955970f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/nextcloud-demo.sh
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 19:49 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 27, 2026 19:54 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74cc12cd4a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 14:08 Destroyed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-30T01:05:17.885388Z 3aee820 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af9d42fda1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/GroupwareTasksScreen.kt Outdated
Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/GroupwareTasksScreen.kt Outdated
Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/GroupwareTasksScreen.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 14:36 Destroyed
@veryCrunchy
veryCrunchy force-pushed the feat/dynamic-workspace-compatibility branch from 46b2ee1 to c2ab45a Compare August 29, 2026 14:38
@veryCrunchy

Copy link
Copy Markdown
Member Author

@codex review

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 18:59 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 19:04 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d633f18822

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/GroupwareTaskEditor.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 19:24 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 19:29 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

private fun NextcloudSession.cacheKey(): String =
serverUrl.trim().trimEnd('/').lowercase() + '\u0000' + loginName

P2 Badge Preserve base-path case in Office capability cache keys

When two Nextcloud instances share a host and login name but use case-sensitive base paths such as /Cloud and /cloud, lowercasing the complete server URL maps both accounts to the same cache entry. A preview on the second instance can therefore immediately expose Office editing using the first instance's editor inventory and ETag before its asynchronous capability refresh finishes; normalize only the case-insensitive origin components or key this cache by the canonical local account identity.

AGENTS.md reference: AGENTS.md:L410-L413

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/app/GroupwareTasksScreen.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 19:44 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 19:49 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca2a64fa41

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 20:07 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 29, 2026 20:11 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a03b13350c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

val structuredDraftSaver = remember(structuredSpecs) {
nativeRepeatableObjectDraftSaver(structuredSpecs)
}
var repeatableObjectValues by rememberSaveable(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep large repeatable drafts out of saved state

When a repeatable form contains many long values, this rememberSaveable serializes the entire draft into Android's activity state; the custom saver permits up to 256 Ki characters, which can occupy roughly half of the process-wide Binder transaction allowance before the app's other saved state is included, causing TransactionTooLargeException during recreation. Store these drafts in a scoped draft store or impose a substantially smaller UI-enforced saved-state limit.

AGENTS.md reference: AGENTS.md:L245-L248

Useful? React with 👍 / 👎.

Comment on lines +14 to +16
that the embedded browser sees. The representative app manifest covers
native DAV and API workspaces, plus Nextcloud Office as an embedded-web
boundary. The optional catalog command can stage every App Store package

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe Office as an external browser handoff

Fresh evidence in the final tree is that NextcloudDocumentPreview.kt now invokes services.openExternalUrl and explicitly tells the user the editor is opening in a browser, while these newly added lines still describe an embedded browser and embedded-web boundary. This leaves the maintained demo guide and manifest contradicting both the implementation and ROADMAP.md, so compatibility testing may validate the wrong integration model; update the guide and surface labels to describe the external Office handoff.

AGENTS.md reference: AGENTS.md:L61-L70

Useful? React with 👍 / 👎.

Comment on lines +115 to +117
LaunchedEffect(activeEditor.sameOriginUrl) {
runCatchingPreservingCancellation {
services.openExternalUrl(activeEditor.sameOriginUrl)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate browser editing for app-only certificates

When an Android account works only because the user approved its self-signed or private-CA certificate inside the app, this handoff still enables Office and creates a one-time editing session before dispatching the URL to the system browser. The approval is stored only in the app's custom OkHttp trust manager, while openExternalUrl uses an external ACTION_VIEW, so a browser that does not separately trust that certificate cannot load the editor; detect this unsupported trust state and explain how to resolve it before issuing the token-producing POST.

AGENTS.md reference: AGENTS.md:L447-L449

Useful? React with 👍 / 👎.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 30, 2026 00:26 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 30, 2026 00:31 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b341bf8462

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +49 to +50
return values.takeIf { encoded ->
encoded.sumOf { value -> value.length } <= MAX_TASK_EDITOR_SAVED_STATE_CHARACTERS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve drafts that exceed the saved-state cap

When a user edits or creates a task whose combined fields exceed 32,768 characters, the text fields continue accepting input but this saver returns null; after rotation or activity/process recreation, the editor is restored without the in-progress values and the draft is silently reset. Either enforce the same bound in the editor or move larger drafts to a scoped draft store so accepted input is not lost.

AGENTS.md reference: AGENTS.md:L249-L251

Useful? React with 👍 / 👎.

Comment on lines +69 to +70
val uid = property("UID")?.value?.trim()?.takeIf(String::isNotBlank)
?: return@mapNotNull null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject control characters in task identities

Although blank UIDs are now dropped, the final validation still accepts a nonblank UID containing NUL or another control character and exposes that task for editing. The raw UID is retained by updateGroupwareTaskContent, after which GroupwareDavMutationSpec.toGroupwareDavRequest rejects the calendar because it contains NUL, so every save for that displayed task fails; reject unsafe or oversized UIDs at this parser boundary.

AGENTS.md reference: AGENTS.md:L325-L326

Useful? React with 👍 / 👎.

fun normalized(): TaskDraft = copy(
title = title.trim().normalizeGroupwareTextLineEndings(),
dueDate = dueDate.trim(),
description = description.trim().normalizeGroupwareTextLineEndings(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve task-description whitespace

When an existing description begins or ends with intentional whitespace, such as blank lines or Markdown-indented content, saving any task edit strips that whitespace here and uploads the altered description. Normalize line endings without trimming the free-form description so unrelated edits do not permanently rewrite its content.

AGENTS.md reference: AGENTS.md:L25-L26

Useful? React with 👍 / 👎.

Comment on lines +195 to +196
if (response.status in 500..599 || response.status in setOf(405, 501)) {
return loadGroupwareTaskObjectsIndividually(calendarHref, objectReferences, execute)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate transient multiget server failures

When a multiget REPORT receives a transient 5xx response such as 503 during maintenance or throttling, this branch treats it like unsupported REPORT and performs an individual GET for every listed object; across successive batches that can turn one failed refresh into thousands of requests instead of backing off. The Contacts loader has the same 5xx fallback, so restrict fan-out to definitive method-unsupported responses or explicitly proven compatibility cases and preserve transient server failures.

AGENTS.md reference: AGENTS.md:L281-L283

Useful? React with 👍 / 👎.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #425 / NC Native August 30, 2026 00:55 Destroyed
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