fix(mobile): keep saved Solo retries available outside discovery

Allow locked retries to return their saved agent to Flutter for authoritative ownership revalidation. Keep inventory membership required for new chats and cover both native button and submit gates across inventory refreshes.

Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
kenny lopez
2026-10-23 11:44:14 -07:00
parent e66bfab750
commit b2282c11ea
2 changed files with 25 additions and 4 deletions

View File

@@ -6,6 +6,7 @@ struct MeAgentAccessibilityTests {
@MainActor static func main() {
for locked in [false, true] {
for empty in [false, true] {
var submitted: [String: String]?
let controller = NativeProfileTextEditorViewController(
title: "New chat", initialValue: "Chat", placeholder: "Chat name",
multiline: false, pageBackgroundColor: .systemGroupedBackground,
@@ -14,8 +15,8 @@ struct MeAgentAccessibilityTests {
channelDescription: nil, originalName: nil, originalDescription: nil,
canEditDetails: true, canEditCanvas: false, canvasContent: "",
canvasLoaded: false,
chatOptions: ["presentationId": "test", "assistantsLoading": empty, "fieldsLocked": locked, "assistants": empty ? [] : [["id": "agent", "name": "Agent"]]],
onCancel: {}, onSet: { _ in }
chatOptions: ["selectedAssistant": "agent", "presentationId": "test", "assistantsLoading": empty, "fieldsLocked": locked, "assistants": empty ? [] : [["id": "agent", "name": "Agent"]]],
onCancel: {}, onSet: { submitted = $0 as? [String: String] }
)
controller.loadViewIfNeeded()
precondition(controller.numberOfSections(in: controller.tableView) == 3)
@@ -31,6 +32,11 @@ struct MeAgentAccessibilityTests {
precondition(cell.accessibilityTraits.contains(.notEnabled) == disabled)
precondition(cell.accessibilityTraits.contains(.button) == !disabled)
precondition((cell.selectionStyle == .none) == disabled)
let canSubmit = locked || !empty
precondition(controller.navigationItem.rightBarButtonItem?.isEnabled == canSubmit)
// Invoke the action even when disabled to check its independent gate.
_ = controller.perform(NSSelectorFromString("setTapped"))
precondition(submitted?["assistant"] == (canSubmit ? "agent" : nil))
if empty && !locked {
controller.tableView.frame = CGRect(x: 0, y: 0, width: 390, height: 844)
controller.tableView.reloadData()
@@ -47,6 +53,14 @@ struct MeAgentAccessibilityTests {
precondition(!updated.accessibilityTraits.contains(.notEnabled))
precondition(controller.tableView(controller.tableView, titleForFooterInSection: 2) == nil)
}
submitted = nil
controller.updateChatAssistants([
"presentationId": "test",
"assistants": empty ? [] : [["id": "agent", "name": "Agent"]],
])
precondition(controller.navigationItem.rightBarButtonItem?.isEnabled == canSubmit)
_ = controller.perform(NSSelectorFromString("setTapped"))
precondition(submitted?["assistant"] == (canSubmit ? "agent" : nil))
}
}
avatarTests()

View File

@@ -449,13 +449,20 @@ private final class NativeProfileTextEditorViewController:
updateNavigation()
}
// A durable retry keeps its saved recipient even if discovery hides it.
// Flutter revalidates ownership before any creation writes.
private var canSubmitAssistant: Bool {
guard let selectedAssistant else { return false }
return fieldsLocked || assistants.contains { $0["id"] == selectedAssistant }
}
private func updateNavigation() {
navigationItem.rightBarButtonItem?.isEnabled = canEditDetails && hasUnsavedChanges
&& (!isChannelEditor || !canonicalChannelName.isEmpty)
&& (!isChatEditor || (
!currentValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
&& currentValue.count <= 120
&& (isProjectEditor || assistants.contains { $0["id"] == selectedAssistant })
&& (isProjectEditor || canSubmitAssistant)
))
navigationController?.isModalInPresentation = hasUnsavedChanges
placeholderLabel.isHidden = !textView.text.isEmpty
@@ -497,7 +504,7 @@ private final class NativeProfileTextEditorViewController:
onSet(name)
} else if isChatEditor {
guard let selectedAssistant,
assistants.contains(where: { $0["id"] == selectedAssistant }),
canSubmitAssistant,
!currentValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
currentValue.count <= 120 else { return }
onSet(["name": currentValue, "assistant": selectedAssistant, "project": selectedProject ?? ""])