Skip to content

feat: Add 'reversibility' and 'item count' to action classification - #298

Merged
marcelklehr merged 15 commits into
mainfrom
feat/reversibility-and-item-count
Nov 5, 2026
Merged

marcelklehr merged 15 commits into
mainfrom
feat/reversibility-and-item-count

Conversation

@marcelklehr

@marcelklehr marcelklehr commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Reversibility and item coutn are now two more dimensions of the tool action classification, each with its own admin threshold.

How it decides

  • A batch of tool calls is confirmed with the user as soon as any of teh five gates is reached:
    • Impulse radius (unchanged)
    • Deletion: a deletion or overwrite reaching the deletion threshold
    • Irreversible: An action that can't be undone, reaching the new irreversible threshold
    • Item count: more items than the new count threshold, added up over the whole batch
    • Always confirm: unchanged
  • Reversibility has three levels: REVERSIBLE, RECOVERABLE (only from a trash bin or version history), and IRREVERSIBLE (gone for good, or already sent)
  • Item count includes the contents of folders subpages and so on. Read only tools count 0.
  • No answer means worst case: MCP tools, tools without the new markers and hooks that fail are treated as irreversible with an unknown item count, so they are always confirmed.

New admin settings (defaults in brackets):

  • Confirm actions that can't be undone from this radius on [individual people]
  • What counts as "can't be undone"? Only actions that are gone for good or also ones that need a trashbin restore [gone for good only]
  • Don't ask about deletion that can be undone directly [off]
  • Confirm when more than N items are changed [10]

One bhevior change: The deletion gate now uses the reach of the deletion itself. Before, it used the wides radius in the whole batch. For example: Deleting a priovate note while also sharing sopmething with a group is now judged on the note's reach. I think that makes more sense.

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI (N/A)

@marcelklehr

marcelklehr commented Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

Decisions I made, that are worth a look:

  • Edits that keep no history count as irreversible
  • Folder moves and copies count every file inside
  • Talk messages to large audiences are still count=1
  • DEleting from external storage, some group folders or a received federated share is permanent, but classified RECOVERABLE.

@lukasdotcom lukasdotcom left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For file share editing/creating/deleting the item count should also use the count of files in the folder if it is a folder like in copy_file IMO.

@julien-nc julien-nc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code looks good. A few comments/questions.

Comment thread ex_app/lib/all_tools/talk.py Resolved
Comment thread ex_app/lib/all_tools/cookbook.py Resolved
Comment thread ex_app/lib/all_tools/deck.py Resolved
Comment thread ex_app/lib/all_tools/files.py Resolved
@marcelklehr
marcelklehr requested a review from julien-nc November 4, 2026 12:04
Comment on lines +26 to +32
async def form_size(form_id):
"""Deleting a form takes every submission it collected along with it."""
payload = await nc.ocs('GET', f'/ocs/v2.php/apps/forms/api/v3/forms/{form_id}/submissions')
submissions = payload.get('submissions') if isinstance(payload, dict) else None
if submissions is None:
raise ValueError(f'Could not read the submissions of form {form_id!r}')
return 1 + len(submissions)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: seems like we can use a hack here to make this less compute intensive
passing limit = 1 here still gives us the count of all the submissions without actually hydrating the submission objects. The key filteredSubmissionsCount has the count.
https://github.com/nextcloud/forms/blob/fa6bbd7dea20a2c7ed7766e8c86282de848c3388/lib/Controller/ApiController.php#L1277

Comment thread ex_app/lib/all_tools/tables.py Resolved
Comment thread ex_app/lib/all_tools/collectives.py Resolved
Comment thread ex_app/lib/all_tools/forms.py Resolved
@kyteinsky

Copy link
Copy Markdown
Contributor

just an idea, not sure if it's worth pursuing, for changes like table title updates, we could make them reversible in the true sense by fetching the old title before making the change.
after the change, we return both the old and the new titles to the agent making it reversible and having in the agent's context.
it does not work for large fields though, and adds an extra GET request.

@marcelklehr

Copy link
Copy Markdown
Member Author

after the change, we return both the old and the new titles to the agent making it reversible

That's not the sense of reversible that is meant here, I think. it's not about whether the agent can revert the action on its own, but whether it's possible to revert (the consequences of) the action in general.

I think the consequences of any simple title change are negligible, because it's usually quite easy to restore the title by hand, even if there is no explicit undo functionality.

@kyteinsky

Copy link
Copy Markdown
Contributor

fair enough.

@marcelklehr

marcelklehr commented Nov 5, 2026 •

Copy link
Copy Markdown
Member Author

Blockers:

  • deletion on External storage should be IRREVERSIBLE It goes to trashbin as well, so RECOVERABLE is fine
  • Make changing title IRREVERSIBLE
  • Add a CI check that all tools have all markers

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Epistemologically, the information is exposed at the time he share is created, even if the share can be deleted, the information was leaked already. Hence, irreversible.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
API doesn't offer that

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
API doesn't offer that

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
…ERSIBLE

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
…ved threshold getters

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
@marcelklehr
marcelklehr force-pushed the feat/reversibility-and-item-count branch from 568d92d to 04fb1d2 Compare November 5, 2026 13:37
@marcelklehr
marcelklehr merged commit ef3162e into main Nov 5, 2026
8 checks passed
@marcelklehr
marcelklehr deleted the feat/reversibility-and-item-count branch November 5, 2026 14:14
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.

4 participants