RFC: Plugin-Registrable Actions #1380
Replies: 3 comments 33 replies
-
|
Gap 1, i was thinking free form so plugins can pick the same categories as the core if they want to. Gap 2, dynamic. Gap 3, we could surface it, if we want to support it in the first place. gap 4, I'd lean on UUIDs gap 5, obviously the id would change if the class name changes. that was intentional in the proposal i made in chat, but if you want stable ids across refactors and renames then we can introduce a new key or id field, in which the id is either used as is, or derived from. but now we need to manage key/id collision across all actions across all active plugins, whereas if we leave it on auto inferred from the class name (+ namespace) then there will be less likely that there would be collisions from different plugins. gap 6, for api shape, then whatever you (@hidden4003) or @harshithmohan wants will do. gap 7, depends on what we want gap 8. all actions would require admin. privileges, as normal users shouldn't need to nor have the right to run them. if someone disagrees then we can take it from there. gap 9, ephemeral instances, created when iterated through the service. if a plugin wants a singleton, they can declare it as such. we only ask the DI to initialize or grab an instance for us to use, so not our concern. gap 10, depending on if we run it in the queue or not. if it's ran in the queue, then we log it the same way as when running in the queue. if we run it directly, then we can surface it to the client or caller. gap 11, cancellation would be set by the caller. so for direct calls by a client, then would be bound to the request timeout/abort, whereas for other plugins it would be whatever they decide. timeout would just be a cancellation token which cancels after a set time. gap 12, we won't remove the older actions, but we'll port all of them to use the newer system at once, so there will be a grace period where both the older actions and new actions will function for the client to implement it's needed changes to use the newer system. gap 13, no need to document here. just know there are a few. gap 14, everything would be parameterless (except the cancellation token, which is optional), but there could still be actions to run which depends on other factors than caller parameters. gap 15, I'd lean on opt-in, so the service has a direct call and schedule in queue, and the client can decide to run it then and there, or run it in the queue. if ran then and there, then the cancellation token will be tied to the request, but if ran in the queue, then it would be tied to the system uptime or just not set. gap 16, that's for you (@hidden4003) and @harshithmohan to decide if you want to change it or keep it. gap 17, |
Beta Was this translation helpful? Give feedback.
-
|
But we could
…On Wed, Jul 8, 2026, 20:01 hidden4003 ***@***.***> wrote:
we currently have descriptions for actions, not for categories
—
Reply to this email directly, view it on GitHub
<#1380?email_source=notifications&email_token=AGGOKQTIKRWMT5A4IXYVXV35DZLLVA5CNFSNUABIM5UWIORPF5TWS5BNNB2WEL2ENFZWG5LTONUW63SDN5WW2ZLOOQXTCNZVG4ZTQOJXUZZGKYLTN5XKO3LFNZ2GS33OUVSXMZLOOSWGM33PORSXEX3DNRUWG2Y#discussioncomment-17573897>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AGGOKQUIA3HWTISLBZSKGA35DZLLVAVCNFSNUABHKJSXA33TNF2G64TZHMZDEOJXHE4DQOB3IRUXGY3VONZWS33OHMYTAMZYGE3TQMFBOYBA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***
com>
|
Beta Was this translation helpful? Give feedback.
-
|
will this be User Based/Profile-like settings for plugins ? or it all-for-one ? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Plugin-Registerable Actions — Spec (v3, adds series-scoped actions)
Problem
Right now the Actions menu in the web UI is a fixed, server-hardcoded list. Plugins have no way to add entries to it. As we add more provider-style plugins (AniZip, and potentially a Relay integration), each needs a way to expose "run this thing on demand" behavior to the user without core needing to know about it in advance.
Goal
Let plugins (and core, by migrating existing actions to the same shape) register discrete, invokable units of work — "actions" — that:
Proposed interface
Display metadata
Categoryis a fixed enum (ActionCategory), not a free string.Genericis a member of that same enum and is the default/fallback for any action that doesn't specify a category.name/descriptionis owned centrally by core — plugins do not supply a category description, so the earlier "description precedence when names collide" concern is now moot (there's no plugin-supplied description to conflict with).ActionCategoryenum — this is now a hard technical constraint, not just a documented convention.Enabled(Gap 2 — ✅ resolved) andAllowConcurrentExecution(Gap 3 — ✅ resolved)Both dropped from v1. Consensus from the proposal author: not worth the implementation effort right now.
Executeitself.Identity / API shape
Namespace.ClassName).Categoryis a hard enum (Gap 1), the category-id/slug question is resolved implicitly — the enum value itself is the canonical, stable identifier, and itsname/descriptionare looked up centrally rather than carried per-action.name/descriptionon each category object come from the enum's central definition (Gap 1), not from any individual action or plugin.POST /actions/{id}/execute(or similar). ✅ Resolved (Gap 7)Registration & DI
IExecutableActionimplementations via the existing plugin DI hookup; constructor dependencies resolve normally.Executecalls), it must declare that itself in its own DI registration — maintaining state between invocations is explicitly the plugin's responsibility, not the framework's.Execution service
A small dedicated service (e.g.
ActionService) enumerates registered actions, exposes them to the API/UI layer, and runs one by ID — always by scheduling it through the existing job/queue system (queueing is mandatory, see Queue integration below; there is no separate concurrency-tracking responsibility since the queue owns that). It does not live on an existing service (e.g. not bolted onto the job scheduler).IExecutableActionorActionService.Migrating existing actions
IExecutableActionat once (not incremental). The old endpoint(s) are kept and marked deprecated, run in parallel with the new endpoint for a grace period so the frontend can migrate, and are removed in a later release.Concurrency
AllowConcurrentExecutiondropped (Gap 3) and queue execution mandatory (Gap 15),ActionServiceno longer needs its own per-action-type concurrency guard — the queue system handles serialization/concurrency for jobs itself. Actions remain confirmed parameterless (aside from the optionalCancellationToken), so if the queue's own concurrency handling ever needs an identity key, it's the action UUID alone.Queue integration — ✅ Resolved (Gap 15)
Reversed from the earlier opt-in lean. Per the main server maintainer: every action execution is enforced to run as a queued job. There is no direct/immediate execution path exposed by
ActionService— the API always enqueues.Rationale: if actions go through the queue unconditionally, the queue system handles concurrency itself, removing the need for a separate
AllowConcurrentExecutionmechanism (ties back into Gaps 2/3/14 being dropped/simplified).Note: this governs execution via
ActionService/the API. It doesn't prevent a plugin from calling its ownExecutemethod directly in-process if it wants to bypass the service entirely — that's just a plugin calling its own code, not something the framework mediates either way.UI
Keep the current Actions menu UI, data-driven off the new listing endpoint.
Naming
IExecutableAction, no objections raised.Series-scoped actions (new scope — Gaps 18–20)
New requirement: actions also need to exist scoped to a specific series, alongside the existing global actions. Proposed endpoints, mirroring the existing
/Series/{seriesID}resource pattern:GET /Series/{seriesID}/ActionsPOST /Series/{seriesID}/Actions/{id}/ExecuteThis is new surface area, not just a gap fill, and it opens several design questions the current interface doesn't answer:
Executestays fully parameterless, as originally confirmed (Gap 14) — there is noseriesIdargument added to the signature. For aScope: Seriesaction, the series ID comes from the URL route (/Series/{seriesID}/Actions/{id}/Execute), not as a method parameter. The exact mechanism by which the action itself accesses that route-supplied series ID duringExecute(e.g. an injected scoped "current execution context" service resolved by DI per invocation, consistent with the existing transient-per-invoke lifetime from Gap 9) is an implementation detail, not a further open design gap — worth documenting precisely when implemented, but not blocking.Scope(GlobalorSeries) on the interface itself (see Proposed interface above). The framework uses this to route an action to the correct listing endpoint (/Actionsvs./Series/{seriesID}/Actions) and to reject invocation via the wrong route. Whether the series listing endpoint reuses whichever shape wins Gap 6 (B1/B2) is still assumed but not explicitly reconfirmed for the series case.Gap tracker (v3)
All 20 gaps resolved. 🎉
Beta Was this translation helpful? Give feedback.
All reactions