refactor(extensions): remove before_specify hook from assess

Assess is a separate business process from spec-driven development, so
it should not inject itself into the /speckit.specify lifecycle. The
hook fired on every /speckit.specify invocation (it had no condition),
nagging even when an assessment already existed and the user was
deliberately proceeding.

Unlike git's before_specify (a mechanical prerequisite: create a feature
branch) or agent-context's after_* hooks (reacting to spec output),
assess is an upstream, optional, human-judgment process. The coupling
that belongs here already runs forward and by choice: a `go` verdict
from /speckit.assess.decide hands off to /speckit.specify. The backward
hook was the redundant, intrusive direction.

- extension.yml: drop the hooks block (commands-only manifest)
- README.md: replace the Hooks section with a Handoff section
- test: replace the hook assertion with test_declares_no_hooks to lock
  in the standalone-pipeline design

Assisted-by: GitHub Copilot (model: Claude Opus 4.8, supervised)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1f25cf9d-b7eb-4b2b-b811-3e91d8db8f6a
This commit is contained in:
Manfred Riem
2026-07-17 07:55:41 -05:00
co-authored by Copilot App
parent 4c8124d22b
commit 6e2d81d18c
3 changed files with 8 additions and 15 deletions
@@ -48,13 +48,15 @@ class TestExtensionLayout:
commands = {c["name"] for c in manifest["provides"]["commands"]}
assert commands == EXPECTED_COMMANDS
def test_before_specify_hook_is_optional(self):
def test_declares_no_hooks(self):
"""assess is a standalone pipeline: it must not register lifecycle
hooks (e.g. before_specify). Discovery and specification stay
separate processes; the only coupling is the forward decide ->
/speckit.specify handoff described in the commands."""
manifest = yaml.safe_load(
(EXT_DIR / "extension.yml").read_text(encoding="utf-8")
)
hook = manifest["hooks"]["before_specify"]
assert hook["optional"] is True
assert hook["command"] == "speckit.assess.intake"
assert "hooks" not in manifest or not manifest["hooks"]
def test_readme_exists(self):
readme = EXT_DIR / "README.md"