T-W8BH-kind-conversion-action
State: planning/proposed · Impact: high · Complexity: medium
ap-kind is a plain select in the fields form, so changing what a note is writes one frontmatter
key and nothing else: the note stays in the folder its old kind put it in, keeps fields its new kind
does not carry, and gets no container scaffolded for it. Conversion becomes a discrete action with a
preview and a consent step, on the same shape rename and move already use. That also removes the
phantom-container symptom, where a note claiming a plugin nothing declares mints an empty group in
the tree.
| Location | Role today |
|---|---|
solutions/determined/experiences/agent-pants/app/components/SkillFieldsForm.vue | The ap-kind DSelect over kindItems/KIND_UNSET writes the key straight into the model — no move, no preview, no consent |
solutions/determined/experiences/agent-pants/app/utils/fieldSpec.ts#fieldAppliesToKind | Says which fields a kind carries; the source for the kept/dropped list |
solutions/determined/crates/determined-agent-skills/src/lib.rs#move_destination | Computes where a move puts a note |
solutions/determined/crates/determined-agent-skills/src/lib.rs#rename_destination | The same computation for a rename |
solutions/determined/crates/determined-agent-skills/src/lib.rs#new_note_dir | Maps a placement onto its kind directory when a note is created |
solutions/determined/crates/determined-agent-skills/src/lib.rs#group_components | Groups the tree; or_insert_with mints a group with note: None for a plugin name no note declares — the phantom container |
solutions/determined/crates/determined-agent-skills/src/lib.rs#get_rename_component_preview | Preview half of the plan/apply pair, returning refusal, peers and expect etags |
solutions/determined/crates/determined-agent-skills/src/lib.rs#rename_component | Apply half: move the note, then rewrite frontmatter |
solutions/determined/experiences/agent-pants/app/components/ApRenameDialog.vue | The consent dialog shape: live preview, inline refusal, “don’t show this again”, classifyOpRefusal handoff |
solutions/determined/experiences/agent-pants/app/components/ApMoveDialog.vue | The same shape for a move, with a picker instead of a text field |
solutions/determined/experiences/agent-pants/app/pages/apps/agent-pants/notes/[...path].vue | The overflow menu — Rename…, Move…, Delete… — plus the undo toasts each apply raises |
solutions/determined/experiences/agent-pants/app/composables/useAgentSkillComponentOps.ts | The transport wrappers for each plan/apply pair |
solutions/determined/crates/determined-platform/src/api/v1/agent_skills.rs | The op seam; ontogen generates the IPC and HTTP transports from it |
solutions/determined/experiences/agent-pants/app/plugins/register.ts | contributes.commands — three commands today |
Proposed
Section titled “Proposed”“Convert kind…” is an entry in the note overflow menu and a registered command. It opens a dialog that previews, before anything is written:
- the destination path, from the kind directory the new kind belongs in;
- the fields kept and the fields dropped, derived from
fieldAppliesToKind; - the container scaffold that would be created, when converting to
pluginormarketplace; - collisions and any other refusal, rendered inline with the submit button disabled.
Applying calls one backend op, convert_kind, which moves the note, rewrites ap-kind and the
fields the new kind drops, and rewrites the wikilinks that pointed at the old path — as one unit,
with the same etag expect guard and preview-then-apply consent as rename. The ap-kind DSelect
is gone from SkillFieldsForm.vue; the form shows the resolved kind as read-only text with a link
to the action.
Approach
Section titled “Approach”determined-agent-skills: addconvert_kind_destination, the kind-directory sibling ofmove_destination, and cover it with unit tests over each kind pair.- Add
get_convert_kind_previewreturning a plan — destination, kept fields, dropped fields, scaffold,peers,expect,refusal— modelled onget_rename_component_preview. Writes nothing. - Add
convert_kind: move, frontmatter rewrite, link rewrite, in that order, through the sameVaultNotesPortcalls rename uses. Decision inside this step: converting to a container kind scaffolds its member folders; converting away from one refuses while the container still holds members, rather than orphaning them. - Wire types in
determined-core:AgentSkillConvertKindPlan,AgentSkillConvertKindRequest,AgentSkillConvertKindResult. - Expose both ops in
determined-platform/src/api/v1/agent_skills.rsand regenerate the transports. ApConvertKindDialog.vue, built onApRenameDialog.vue’s structure: kind picker, live preview, inline refusal, “don’t show this again” hiding only the review,classifyOpRefusalhandoff at apply time.- Add the transport wrappers to
useAgentSkillComponentOps.ts, the menu item and undo toast tonotes/[...path].vue, and anagent-pants.convert-kindcommand toregister.ts. - Remove the
ap-kindDSelect,kindItemsandKIND_UNSETfromSkillFieldsForm.vue; render the resolved kind read-only. - Fix
group_componentsso a plugin name that no plugin note declares does not mint a group. The members still show — under the marketplace’s shared group — and the tree stops advertising a container that does not exist.
| Area | Note |
|---|---|
solutions/determined/crates/determined-agent-skills/src/lib.rs | convert_kind_destination, get_convert_kind_preview, convert_kind; group_components stops minting phantom groups |
solutions/determined/crates/determined-core/src/wire/mod.rs | Plan, request and result types for the new op |
solutions/determined/crates/determined-platform/src/api/v1/agent_skills.rs | The two op functions; ontogen regenerates the IPC and HTTP transports |
solutions/determined/experiences/agent-pants/app/components/ApConvertKindDialog.vue | The preview-then-apply consent dialog |
solutions/determined/experiences/agent-pants/app/components/SkillFieldsForm.vue | Drop the ap-kind DSelect, kindItems and KIND_UNSET; show the resolved kind read-only |
solutions/determined/experiences/agent-pants/app/composables/useAgentSkillComponentOps.ts | Transport wrappers for preview and apply |
solutions/determined/experiences/agent-pants/app/pages/apps/agent-pants/notes/[...path].vue | ”Convert kind…” in the overflow menu, dialog mount, undo toast |
solutions/determined/experiences/agent-pants/app/plugins/register.ts | agent-pants.convert-kind command |
solutions/determined/experiences/agent-pants/docs/authoring.md | Document conversion as an action, not a field |
solutions/determined/products/agent-pants/tests/e2e/walkthrough-dialogs.e2e.ts | Walk the convert dialog beside rename, move and delete |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
SkillFieldsForm.vuecarries noap-kindDSelect, and neitherkindItemsnorKIND_UNSETremains in the file. - AC-2: The note overflow menu offers “Convert kind…”, and
agent-pants.convert-kindis a registered command. - AC-3: The dialog shows destination path, kept fields, dropped fields and — for
pluginandmarketplace— the scaffold, before anything is written. - AC-4: Converting a skill in
skills/tocommandleaves the note at thecommands/destination withap-kind: command, and the wikilinks that pointed at the old path resolve. - AC-5: A conversion whose destination already exists refuses inline, with the submit button disabled and nothing written.
- AC-6: Converting a plugin that still holds members refuses, naming the members.
- AC-7: A note declaring
ap-pluginfor a plugin no note declares no longer produces an empty group in the component tree, covered by agroup_componentsunit test. - AC-8: The dialogs e2e project passes with the convert flow added.
Out of scope
Section titled “Out of scope”- Converting several notes at once; the action takes one note.
- The palette roster as a whole (T-0BET-commands-for-every-operation-and-save rosters every operation, this task registers one).
- Kind-typed folder glyphs and the tree’s kind filter (T-0XMP-tree-typed-folders-filter-add).
- The
hookkind, which T-H56N-hooks-as-vault-entities adds to the vocabulary this action converts between.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Member of milestone M-BMFD, from the user’s planning request of 2026-09-07 on the data model.
The assessment solutions/agent-pants/docs/vault/design/workflow-and-ux-assessment.md records the
kind select as a field that silently disagrees with the folder it sits in, and the phantom container
is the tree symptom of the same gap. ApRenameDialog.vue and ApMoveDialog.vue set the consent
shape this action copies.