Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(team-init): emit PreToolUse deny in hookSpecificOutput shape#1971

Open
heslanx wants to merge 1 commit into
garrytan:main from
heslanx:fix/team-init-pretooluse-deny-schema
Open

fix(team-init): emit PreToolUse deny in hookSpecificOutput shape #1971
heslanx wants to merge 1 commit into
garrytan:main from
heslanx:fix/team-init-pretooluse-deny-schema

Conversation

@heslanx

@heslanx heslanx commented Jun 11, 2026

Copy link
Copy Markdown

Problem

The check-gstack.sh enforcement hook generated by bin/gstack-team-init required prints its deny decision as a top-level permissionDecision:

{"permissionDecision":"deny","message":"gstack is required but not installed. ..."}

Claude Code's PreToolUse decision control only reads hookSpecificOutput.permissionDecision (with hookEventName: "PreToolUse") — the deprecated top-level fields are decision/reason, not permissionDecision/message. The output is therefore treated as a no-op, and the required-mode gate never actually blocks skill usage when gstack is missing. Teams adopting gstack-team-init required get an enforcement hook that silently enforces nothing.

Found via codex review while rolling team mode out across our org's repos; verified against Claude Code's hook output validation (it rejects hookSpecificOutput without hookEventName and ignores unknown top-level fields).

Fix

Emit the deny in the supported shape — the same one #1509 adopts for careful/freeze:

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"gstack is required but not installed. See stderr for install instructions."}}

The stderr install instructions are unchanged.

Tests

Added a test that runs gstack-team-init required and then executes the generated hook in both scenarios:

  • HOME without gstack → structured hookSpecificOutput deny
  • HOME with gstack installed → {} no-op
bun test test/team-mode.test.ts
 25 pass, 0 fail (57 expect() calls)

Note (out of scope)

The settings matcher generated by team-init only gates the Skill tool, so plain Read/Edit/Bash work proceeds without gstack. If the intent of required mode is to gate all AI-assisted work, a catch-all matcher might be worth considering — left out of this PR since it's a design decision (and team-mode.test.ts pins matcher === 'Skill'). Happy to follow up if there's interest.

🤖 Generated with Claude Code

The check-gstack.sh hook generated by gstack-team-init printed
permissionDecision at the top level. Claude Code's PreToolUse decision
control only reads hookSpecificOutput.permissionDecision (with
hookEventName), so the deny was silently ignored and the required-mode
gate never actually blocked skill usage when gstack was missing.
Use the same hookSpecificOutput shape adopted for careful/freeze in
garrytan#1509, and add a test that executes the generated hook in both
scenarios (gstack missing -> structured deny; installed -> {}).

trunk-io Bot commented Jun 11, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

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

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /