Skip to content

Adoption tracking: hook-config-delivery convention (channel matrix codified in docs/conventions/, #1249) #1182

Description

@kyle-sexton

Proposal: codify a "delivering a userConfig value to a hook" channel decision matrix (convention)

Multiple plugins deliver a userConfig scalar into a hook's decision logic, and they do it three
different ways with no documented rule
— the exact reuse-or-replace fragmentation:

This proposes a single decision matrix, grounded in verified Claude Code behavior (2.1.218), so every
plugin/fix picks a channel by rule instead of ad hoc. Driving cases: the two inbox findings below.

Verified channel facts (CC 2.1.218; citations)

  1. Plugin hooks.json hooks get ${user_config.KEY} (exec form only) and CLAUDE_PLUGIN_OPTION_<KEY> env — plugins-reference (userConfig section); shell-form ${user_config.*} is rejected since CC 2.1.207 (use exec args or the env var).
  2. Skill/agent-frontmatter hooks get neither — CC docs are silent on it; asserted by disk-hygiene: kill switch (disk_hygiene_enabled) cannot reach a skill-frontmatter guard hook — audit-only degrades to prompt-gated #1019 and the disk-hygiene guard. (Evidence-strong, not CC-doc-stated.)
  3. A declared default is in the schema but NOT implemented for substitution — an unset-but-defaulted ${user_config.*} resolves as unset. Upstream: #46477 (closed not planned) + comments (independent repro on an MCP url; "its in the schema… just not implemented"); #39455 / #39827. No CHANGELOG entry ever implemented it (latest 2.1.218).
  4. Skill/agent body ${user_config.KEY} substitutes into model-visible content, non-sensitive onlyplugins-reference.
  5. Open (undocumented): whether a project/local settings.json env block overrides the harness-injected CLAUDE_PLUGIN_OPTION_* (decides whether the env channel is repo-tamper-resistant). Inference leans "injection wins → safe," unverified.

The matrix

Channel Reaches skill-hook? Unset-default Repo-tamper-resistant Failure mode Sensitive-safe Machinery
A. Exec argv ${user_config.KEY} no broken (Fact 3) yes (user/managed) unsafe — hook drops on argv none
B. Env CLAUDE_PLUGIN_OPTION_<KEY> no safe (in-script default) unknown (Fact 5) safe yes none
C. SessionStart → ${CLAUDE_PLUGIN_DATA} file yes (only channel) inherits Fact 3 at resolve yes (plugin dir) safe yes +hook +file-trust
D. required:true + argv (no default) no n/a (no unset case) yes safe on argv none
E. Skill/agent body ${user_config.KEY} (model, not a hook) n/a yes advisory only no none

The decision rule

  1. Skill-frontmatter hook → only C delivers a value; else E (advisory only). [= disk-hygiene: kill switch (disk_hygiene_enabled) cannot reach a skill-frontmatter guard hook — audit-only degrades to prompt-gated #1019]
  2. Plugin hook, MANDATORY value (no sensible default)D: required:true + argv. Trusted, simplest, sidesteps Fact 3 (no unset case).
  3. Plugin hook, optional-with-default, safety/security-criticalnever bare argv (Fact 3 drops it). Baseline B (env + in-script default). Add C only if a configured value must resist a hostile repo AND Fact 5 resolves against B.
  4. Sensitive valueB or C, never E or logged argv.

Driving cases (inbox findings, filed separately)

  • Item 1 — disk-hygiene unconfigured kill switch: plugin hook on a safety+optional-default value uses bare argv → violates rule 3; Fact 3 drops the hook → the engine-gate/kill-switch is absent on unconfigured installs (observed; cause inferred). Fix = apply rule 3 + raise Fact 3 upstream (root cause).
  • Item 3 — PowerShell-tool guards inert: the PowerShell tool is a documented preview tool (tools-reference), matcher name PowerShell is doc-sanctioned, and hook-firing is not a listed preview limitation — yet a Bash|PowerShell PreToolUse matcher does not intercept PowerShell-tool commands on 2.1.218 (reproduced). So the matcher is doc-correct (not a plugin fix) → this is a docs-vs-behavior divergence to raise upstream + document the limitation. Ecosystem-wide (guardrails + disk-hygiene). Windows hook-payload lineage: #57137, #36156, #16564 (all closed).

Ask

  1. Adopt the matrix + rule as a convention doc (where — docs/? a shared plugin note?).
  2. Confirm the rule-3 default (B baseline; C only when tamper-resistance is required) — or resolve Fact 5 first.
  3. Track the two upstream raises (Fact 3; the PowerShell-tool hook divergence).

Full research + evidence table + confidence/gaps: session artifact RESEARCH.md (not committed).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: mediumReal value, no hard deadline; normal backlog flow.status: readyTriaged, unblocked, and fully specified; eligible to pick up.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions