Skip to content

[Bug]: integration-events.yml entry without an events key silently removes all event hooks #5092

Open

Description

@hnipps

Bug Description

In .specify/integration-events.yml, an integration entry that is a mapping but has no events key is treated as an explicit "disable everything". resolve_events reads it with key_data.get("events", {}) (src/specify_cli/events/__init__.py:1000), so a missing key becomes an empty map. That empty map replaces the built-in and extension handlers, and the next install or upgrade removes the native hooks. No warning is printed, so a typo such as evnts: turns off every hook without any sign.

Steps to Reproduce

From a fresh clone after uv sync, save this as repro.py and run uv run python -I repro.py:

import subprocess, sys, tempfile
from pathlib import Path

specify = str(Path(sys.executable).parent / "specify")
proj = Path(tempfile.mkdtemp()) / "p"

def run(*args):
    r = subprocess.run([specify, *args], cwd=proj if proj.exists() else None,
                       capture_output=True, text=True)
    return r.stdout + r.stderr

run("init", str(proj), "--integration", "claude", "--non-interactive", "--ignore-agent-tools")
ext = proj / ".specify/extensions/foo"
ext.mkdir(parents=True)
(ext / "extension.yml").write_text(
    'schema_version: "1.0"\nextension:\n  id: foo\n'
    "events:\n  pre_tool_use:\n    command: speckit.foo.x\n    matcher: Bash\n")
settings = proj / ".claude/settings.json"

run("integration", "upgrade", "claude", "--force")
print("Without override, settings.json exists:", settings.exists())

# Typo: "evnts" instead of "events".
(proj / ".specify/integration-events.yml").write_text(
    "integrations:\n  claude:\n    evnts:\n      stop:\n        command: speckit.x\n")
out = run("integration", "upgrade", "claude", "--force")
print("With typo override, settings.json exists:", settings.exists())
print("Warning lines:", [l for l in out.splitlines() if "warn" in l.lower() or "override" in l.lower()])

The script initialises a Claude project, adds an extension directory that declares a pre_tool_use event, upgrades the integration, then adds an override whose only key is misspelt (evnts) and upgrades again.

Expected Behavior

The override should be ignored with a warning, and the existing handlers kept. The code comment at src/specify_cli/events/__init__.py:993-995 says "Only an explicitly present, mapping-valued events field replaces the prior layers." docs/integrations/cli.md:447-449 documents events: {} as the way to disable all handlers, and says an override that fails validation produces a warning and the defaults and extension handlers are kept. A non-mapping entry (claude: bad) is already handled this way (test test_non_mapping_integration_entry_abandons_override in tests/specify_cli/events/test_events.py).

Actual Behavior

The hook config is deleted and nothing is printed:

Without override, settings.json exists: True
With typo override, settings.json exists: False
Warning lines: []

Specify CLI Version

1.2.4.dev0 (main at 27d14e2)

AI Agent

Claude Code

Operating System

Linux 6.18 (Ubuntu container)

Python Version

Python 3.12.3

Additional Context

The same happens for claude: {}. Only the case where events is present as a mapping should replace the prior layers.

Also reproduced on macOS (Darwin 25.6, arm64) with Python 3.14.4 and uv run python -I repro.py against 27d14e2; the output matches the above.

AI Disclosure

Found and reproduced with Claude Code (model: Claude Opus 4.8), run autonomously under human supervision. The human reporter re-ran every reproduction above before filing.

Activity

  1. added
    triage-must-haveVerdict: high-value, important work for Spec Kit — do first
    on Oct 23, 2026
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

    triage-must-haveVerdict: high-value, important work for Spec Kit — do first

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions