Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions .verification/p189-command-forms.plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"recipes": [
{
"recipe_id": "shared-command-form-preparation-and-confirmation",
"operations": [
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from python_introspect.choices import LabelledChoices"
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from textual.worker import Worker"
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from toad.worker_group import WorkerGroup"
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from agent_comms.cli_verbs.cli_command import TargetAction"
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from agent_comms.errors import RouteChangedError"
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from agent_comms.execution.native_failures import NativePiUnavailable"
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "PreparedChoices",
"replacements": [
{
"old_source": "class PreparedChoices(AnnotationChoices, DeclaredMetadata):",
"new_source": "class PreparedChoices(LabelledChoices, DeclaredMetadata):"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "PreparedChoices",
"replacements": [
{
"old_source": " def choices(self):\n return tuple(value for _, value in self.parameter.choices)\n\n def label(self, choice):\n return next((label for label, value in self.parameter.choices if value == choice), 'Choose\u2026')",
"new_source": " def labelled_choices(self):\n return self.parameter.choices"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "ApplyCommandAction.apply",
"replacements": [
{
"old_source": " await context.confirm()\n if context.query_one(ApplyCommandButton).disabled:\n return\n arguments = context.arguments()\n confirmed = context.query_one(CommandConfirmed).value\n context.query_one(ApplyCommandButton).disabled = True\n await context.app.preparation.run_thread(\n lambda: context.definition.edited(arguments).with_confirmation(confirmed))\n if context.is_attached and arguments == context.arguments():\n context.dismiss(arguments)",
"new_source": " await context.confirm()"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.confirm",
"replacements": [
{
"old_source": " self.form.finish()",
"new_source": " self.form.finish()\n if self.query_one(ApplyCommandButton).disabled:\n return\n arguments = self.arguments()\n confirmed = self.query_one(CommandConfirmed).value\n self.query_one(ApplyCommandButton).disabled = True\n await self.app.preparation.run_thread(\n lambda: self.definition.edited(arguments).with_confirmation(confirmed))\n if self.is_attached and arguments == self.arguments():\n self.dismiss(arguments)"
}
]
},
{
"operation": "insert_before_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog",
"source": "class CommandPreparation(WorkerGroup):\n \"\"\"One preparation follows the current draft without restarting on each edit.\"\"\"\n\n\n"
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.__init__",
"replacements": [
{
"old_source": "definition: PreparedTargetAction",
"new_source": "definition: TargetAction"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.on_mount",
"replacements": [
{
"old_source": " self.update_confirmation()",
"new_source": " self.prepare_current()\n self.update_confirmation()"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.draft_changed",
"replacements": [
{
"old_source": " self.update_confirmation()\n if paths & self.choices.keys():\n self.run_worker(self.refresh_catalogs(self.arguments()),\n group='command-catalogs', exclusive=True)",
"new_source": " self.prepare_current()\n self.update_confirmation()"
}
]
},
{
"operation": "insert_before_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.arguments",
"source": " def prepare_current(self) -> None:\n if not CommandPreparation.running_on(self):\n CommandPreparation.run_on(self, self.refresh_catalogs(self.arguments()))\n\n @on(Worker.StateChanged)\n def preparation_changed(self, event: Worker.StateChanged) -> None:\n if (event.worker.node is self and event.worker.group == CommandPreparation.group\n and event.worker.is_finished and self.is_attached):\n self.update_confirmation()\n\n"
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.refresh_catalogs",
"replacements": [
{
"old_source": " self.query_one(ApplyCommandButton).disabled = True\n edited = self.definition.edited(arguments)\n fields = (await self.app.preparation.run_thread(\n lambda: self.core.call(CommsPrepareTargetActionTool(action=edited)))).fields\n if not self.is_attached or arguments != self.arguments():\n return\n for parameter in fields:\n if parameter.name in self.choices:\n self.choices[parameter.name].parameter = parameter\n self.form.values[parameter.name].editor.paint()\n self.update_confirmation()\n",
"new_source": " self.query_one(ApplyCommandButton).disabled = True\n try:\n while self.is_attached:\n edited = self.definition.edited(arguments)\n prepared = await self.app.preparation.run_thread(\n lambda: self.core.call(CommsPrepareTargetActionTool(action=edited)))\n if not self.is_attached:\n return\n current = self.arguments()\n if arguments != current:\n arguments = current\n continue\n self.definition = prepared\n for parameter in prepared.fields:\n if parameter.name in self.choices:\n self.choices[parameter.name].parameter = parameter\n self.form.values[parameter.name].editor.paint()\n break\n except (NativePiUnavailable, RouteChangedError) as error:\n self.app.show_error(error, self.definition.label)\n if self.is_attached:\n self.update_confirmation()\n"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.update_confirmation",
"replacements": [
{
"old_source": " arguments = self.arguments()\n async def prepare():",
"new_source": " arguments = self.arguments()\n if CommandPreparation.running_on(self):\n self.query_one(CommandConfirmation).update(\"Loading choices\u2026\")\n confirmed.display = False\n return\n async def prepare():"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/thread_actions.py",
"target_qualname": "ThreadAction.collect",
"replacements": [
{
"old_source": " try:\n prepared = await ctx.app.preparation.run_thread(\n lambda: ctx.observed.core.call(CommsPrepareTargetActionTool(action=definition)))\n ctx.current()\n except (NativePiUnavailable, RouteChangedError) as error:\n ctx.app.show_error(error, definition.label)\n return\n if ctx.app.screen is screen:\n ctx.app.push_screen(\n CommandDialog(prepared, ctx.title, prepared.fields, ctx.observed.core), accepted)",
"new_source": " fields = await ctx.app.preparation.run_thread(lambda: definition.editable_fields)\n ctx.current()\n if ctx.app.screen is screen:\n ctx.app.push_screen(\n CommandDialog(definition, ctx.title, fields, ctx.observed.core), accepted)"
}
]
}
]
}
]
}
47 changes: 47 additions & 0 deletions .verification/p189-confirm-current-draft.plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"recipes": [
{
"recipe_id": "confirm-the-current-draft-through-its-worker-owner",
"operations": [
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/worker_group.py",
"import_source": "import asyncio"
},
{
"operation": "insert_before_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/worker_group.py",
"target_qualname": "WorkerGroup.running_on",
"source": " @classmethod\n def workers_on(cls, node: DOMNode) -> tuple[Worker, ...]:\n return tuple(worker for worker in node.workers\n if worker.node is node and worker.group == cls.group)\n\n @classmethod\n async def wait_on(cls, node: DOMNode) -> None:\n \"\"\"Wait for the node's actual work; its original error still refuses the caller.\"\"\"\n await asyncio.gather(*(worker.wait() for worker in cls.workers_on(node)))\n\n"
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/worker_group.py",
"target_qualname": "WorkerGroup.running_on",
"replacements": [
{
"old_source": "return any(worker.node is node and worker.group == cls.group and not worker.is_finished\n for worker in node.workers)",
"new_source": "return any(not worker.is_finished for worker in cls.workers_on(node))"
}
]
},
{
"operation": "ensure_import",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"import_source": "from agent_comms.cli_support.cli_failures import CLI_FAILURES"
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.confirm",
"replacements": [
{
"old_source": " self.form.finish()\n if self.query_one(ApplyCommandButton).disabled:\n return\n arguments = self.arguments()\n confirmed = self.query_one(CommandConfirmed).value\n self.query_one(ApplyCommandButton).disabled = True\n await self.app.preparation.run_thread(\n lambda: self.definition.edited(arguments).with_confirmation(confirmed))\n if self.is_attached and arguments == self.arguments():\n self.dismiss(arguments)\n",
"new_source": " button = self.query_one(ApplyCommandButton)\n if button.disabled:\n return\n self.form.finish()\n try:\n await CommandPreparation.wait_on(self)\n except WorkerFailed:\n # The preparation's own handler shows the original failure.\n return\n if not self.is_attached or any(view.editor.feedback for view in self.form.values.values()):\n return\n arguments = self.arguments()\n confirmed = self.query_one(CommandConfirmed).value\n button.disabled = True\n try:\n await self.app.preparation.run_thread(\n lambda: self.definition.edited(arguments).with_confirmation(confirmed))\n except CLI_FAILURES as error:\n self.app.show_error(error, self.definition.label)\n self.update_confirmation()\n return\n if self.is_attached and arguments == self.arguments():\n self.dismiss(arguments)\n"
}
]
}
]
}
]
}
35 changes: 35 additions & 0 deletions .verification/p189-confirm-loading-form.plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"recipes": [
{
"recipe_id": "the-confirm-operation-owns-its-preparation-wait",
"operations": [
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.update_confirmation",
"replacements": [
{
"old_source": " if CommandPreparation.running_on(self):\n self.query_one(CommandConfirmation).update(\"Loading choices\u2026\")\n confirmed.display = False\n return",
"new_source": " button = self.query_one(ApplyCommandButton)\n button.loading = CommandPreparation.running_on(self)\n if button.loading:\n self.query_one(CommandConfirmation).update(\"Loading choices\u2026\")\n confirmed.display = False\n # Confirm owns the wait, including an edit committed by this click.\n button.disabled = False\n return"
},
{
"old_source": "if not self.is_attached or arguments != self.arguments():",
"new_source": "if not self.is_attached or arguments != self.arguments() or CommandPreparation.running_on(self):"
}
]
},
{
"operation": "patch_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/src/toad/widgets/comms_command_dialog.py",
"target_qualname": "CommandDialog.confirm",
"replacements": [
{
"old_source": " self.form.finish()\n try:\n await CommandPreparation.wait_on(self)",
"new_source": " self.form.finish()\n button.disabled = True\n button.loading = True\n try:\n await CommandPreparation.wait_on(self)"
}
]
}
]
}
]
}
15 changes: 15 additions & 0 deletions .verification/p189-first-click-control.plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"recipes": [
{
"recipe_id": "exercise-held-preparation-and-first-mouse-confirm",
"operations": [
{
"operation": "insert_after_target",
"file_path": "/home/ts/wt/codex-parallel--P189--toad/tests/textual/test_application_actions_in_the_running_app.py",
"target_qualname": "test_new_session_button_opens_creation_and_tab_close_keeps_the_original_draft",
"source": "\n\nasync def test_command_form_opens_before_preparation_and_first_mouse_confirm_submits(workspace):\n from threading import Event\n\n from agent_comms.cli_verbs.cli_command import TargetAction\n from agent_comms.cli_verbs.new import NewThreadCliCommand\n from agent_comms.projects.checkouts import Checkout\n from agent_comms.transport.request import CoreAccess\n from toad.widgets.comms_command_dialog import ApplyCommandButton, CommandDialog, CommandPreparation\n\n entered, released = Event(), Event()\n\n class HeldPreparation(CoreAccess):\n def __init__(self, original):\n self.original = original\n\n def call(self, request):\n entered.set()\n if not released.wait(30):\n raise TimeoutError(\"The test did not release command preparation\")\n return self.original.call(request)\n\n async with running_session(workspace) as (ui, app, config, path):\n project = Checkout(workspace.project)\n command, = NewThreadCliCommand.project_bindings(workspace.comms, project)\n action = TargetAction((command,), (), NewThreadCliCommand)\n observed = await app.preparation.run_thread(app.coordination_access.observation.observed)\n accepted = []\n dialog = CommandDialog(action, str(project), action.editable_fields, HeldPreparation(observed.core))\n app.push_screen(dialog, accepted.append)\n try:\n await ui.until(entered.is_set, why=\"preparation to have entered its held request\")\n assert app.screen is dialog and not released.is_set()\n assert dialog.form.is_mounted\n released.set()\n await ui.until(lambda: not CommandPreparation.running_on(dialog), why=\"the real catalogs to arrive\")\n button = dialog.query_one(ApplyCommandButton)\n await ui.until(lambda: not button.disabled, why=\"the command values to be accepted\")\n editor = dialog.form.values[\"name\"].editor\n editor.scroll_visible(animate=False, immediate=True)\n await ui.until_clickable(editor, why=\"the name editor to be visible\")\n assert await ui.textual[1].click(editor)\n await ui.type(\"created-from-first-click\")\n # Do not blur first: this click must commit the focused editor and submit.\n button.scroll_visible(animate=False, immediate=True)\n await ui.until_clickable(button, why=\"the confirm button to be visible\")\n assert await ui.textual[1].click(button)\n await ui.until(lambda: bool(accepted), why=\"the first mouse confirmation to submit the form\")\n assert accepted[0][\"name\"] == \"created-from-first-click\"\n assert app.screen is app.workspace_screen\n finally:\n released.set()\n"
}
]
}
]
}
Loading
Loading