T-0AF7-hook-editor-and-harness-gap-table
State: planning/proposed · Impact: medium · Complexity: medium
Once hook is a vault kind it needs a form: SkillFieldsForm.vue is built around a skill’s fields
and a hook’s are different in shape — an event, a matcher over tool names, and then whichever fields
its type carries. The same work answers a second question for every kind: which of this note’s
fields does each of its targets actually honour. One table, rows the features the note uses and
columns its effective targets, turns the capability matrix into something an author reads while
editing rather than a document they go and find.
| Location | Role today |
|---|---|
solutions/determined/experiences/agent-pants/app/components/SkillFieldsForm.vue | The one fields form. Sections are hard-coded and fieldAppliesToKind gates each row |
solutions/determined/experiences/agent-pants/app/utils/fieldSpec.ts#FIELD_SPECS | The field roster: id, key, label, and the kinds each field applies to |
solutions/determined/experiences/agent-pants/app/utils/fieldSpec.ts#fieldAppliesToKind | The per-kind gate every row in the form calls |
solutions/determined/experiences/agent-pants/app/components/ApPropertiesPanel.vue | The aux panel that shows a note’s resolved placement and its declared keys — the closest thing to a gap view today, and it says nothing about targets |
solutions/determined/experiences/agent-pants/app/pages/apps/agent-pants/notes/[...path].vue | Mounts the form, the body editor and the aux region |
packages/rust/agent-adapt/src/tools.rs#ToolRegistry | The 11 canonical tool names, with each runtime’s spelling — the matcher builder’s vocabulary |
packages/rust/agent-adapt/src/render/hooks_json.rs#render_hooks_json | Translates a matcher’s pipe-delimited halves per runtime |
solutions/agent-pants/docs/vault/reference/harness-comparison.md | The per-harness survey in prose; nothing in the app reads it |
Proposed
Section titled “Proposed”ApHookFieldsForm.vue renders a hook note on the shared section renderer, with an event picker
whose options come from the capability matrix for the note’s effective targets, a matcher builder
over the canonical tool names from tools.rs, and type-specific fields — command, url, prompt
or the agent reference — shown for the selected type and hidden otherwise.
ApHarnessGapTable.vue is a view over any note, not just a hook. Rows are the features that note
actually uses — the fields it sets, its event, its matcher. Columns are its effective targets. Each
cell reads the matrix and says one of: honoured, translated (with what it becomes), or dropped. A
translated or dropped cell offers “override for this harness”, which opens that target’s block in
the edit form’s Overrides section with the key pre-filled.
The table is reachable from the preview pane for every kind, so an author checking what a harness receives can move from the rendered file to the reason a key is missing from it.
Approach
Section titled “Approach”- Add the hook fields to
FIELD_SPECSwithkinds: ['hook']and the section and order keys the shared renderer reads, so the hook form is data-driven like the rest. - Build
ApHookFieldsForm.vueon the section renderer from T-GHE3-edit-form-in-center-pane: event picker, matcher builder, type-specific field group. - Expose the canonical tool names to the client. Decision inside this step: read them from the
capability matrix rather than adding a second op over
tools.rs, so the matcher builder and the gap table share one source. - Build
ApHarnessGapTable.vue: derive the used-features row set from the note’s own fields, take the columns from the effective targets, and render each cell from the matrix with its verdict. - Wire the cell action: a dropped or translated cell opens the Overrides section for that target with the harness’s own key name pre-filled, from T-IIZO-per-target-overrides.
- Mount the table as a view reachable from the preview pane for every kind, and cover the hook
form and the table with component tests beside the existing
ApComponentTree.test.ts.
| Area | Note |
|---|---|
solutions/determined/experiences/agent-pants/app/components/ApHookFieldsForm.vue | The hook edit form on the shared section renderer |
solutions/determined/experiences/agent-pants/app/components/ApHarnessGapTable.vue | Used features by effective target, cells from the matrix |
solutions/determined/experiences/agent-pants/app/components/ApHarnessGapTable.test.ts | Cell verdicts and the override handoff |
solutions/determined/experiences/agent-pants/app/utils/fieldSpec.ts | Hook field entries with their section and order |
solutions/determined/experiences/agent-pants/app/pages/apps/agent-pants/notes/[...path].vue | Mount the hook form for a hook note; reach the gap table from the secondary pane |
solutions/determined/experiences/agent-pants/stories/screens/ | A story for the hook form and one for the gap table |
solutions/determined/experiences/agent-pants/docs/authoring.md | How to author a hook and how to read the gap table |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Opening a note whose kind is
hookrendersApHookFieldsForm.vue, notSkillFieldsForm.vue. - AC-2: The event picker offers only events the note’s effective targets carry, and says which target drops an event that is selected.
- AC-3: The matcher builder offers the canonical tool names from
tools.rsand writes them pipe-delimited. - AC-4: Changing
typeshows that type’s own fields and hides the others; the note never carries two ofcommand,urlandpromptat once. - AC-5: The gap table renders for a skill, a command, an agent and a hook, with one column per effective target.
- AC-6: A cell whose verdict is dropped or translated offers “override for this harness”, and taking it opens that target’s Overrides block with the harness’s key name pre-filled.
- AC-7: A field an override already supplies for a target renders as honoured, not dropped.
- AC-8: The gap table component test asserts one honoured, one translated and one dropped cell against a fixed matrix fixture.
Out of scope
Section titled “Out of scope”- The capability matrix itself (T-HLEW-harness-capability-matrix) and the per-field documentation entries (T-A27K-field-reference-docs-with-evidence).
- The inline support warnings on the edit form (T-8BUZ-marketplace-targets-and-support-warnings); this table is the full view, the warnings are the per-field nudge.
- Rendering the hook’s body, which is the ordinary body editor.
- The Overrides section’s own layout, owned by T-GHE3-edit-form-in-center-pane.
Dependencies
Section titled “Dependencies”- T-H56N-hooks-as-vault-entities defines the kind and its fields; there is nothing to edit before it lands.
- T-GHE3-edit-form-in-center-pane supplies the shared section renderer both surfaces build on.
- T-IIZO-per-target-overrides supplies the Overrides section a gap cell hands off to.
- T-K1A2-preview-pane-per-entity-and-target is where the table is reached from; it does not block the table’s own implementation.
Discovery context
Section titled “Discovery context”Member of milestone M-BMFD, from the user’s planning request of 2026-09-07: the editor should
warn where a harness does not honour a field, and hooks should be authored in the app. The
divergence the table renders is surveyed in
solutions/agent-pants/docs/vault/reference/harness-comparison.md, and the assessment
solutions/agent-pants/docs/vault/design/workflow-and-ux-assessment.md records that nothing in the
app tells an author what a target drops. Hook and permission vocabularies come from
Claude Code hooks,
Gemini CLI and OpenCode, fetched
2026-09-07.
Depends on
Section titled “Depends on”T-H56N-hooks-as-vault-entities, T-GHE3-edit-form-in-center-pane, T-IIZO-per-target-overrides