Address the third review round on github/spec-kit#3568:
- Path safety (intake, research, define, shape, decide): slug
normalization blocks lexical `..` but not symlinked path components.
Each command now, before any mkdir/read/write, resolves the real path
of .specify/assessments/<slug>/ and every artifact, refuses to follow a
symlinked .specify / assessments / slug dir / artifact, and verifies the
resolved path stays inside the project root. This blocks a cloned or
crafted project from redirecting reads/writes outside the repository.
Each stage enforces this independently since research/define/decide can
run without intake.
- research URL policy: replaced the open-ended "and comparable well-known
hosts" no-prompt branch with intake's exact enumerated allowlist, so an
agent cannot classify an attacker-controlled host as "comparable" and
fetch it without confirmation.
- README: guardrail now documents symlink/realpath containment.
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
Address the second review round on github/spec-kit#3568:
- Slug path traversal: intake and all four downstream commands
(research, define, shape, decide) now normalize an explicit or
user-supplied slug to the [a-z0-9-] alphabet (dropping '.', '/', '\\')
and reject an empty normalized result before constructing ASSESS_DIR.
This guarantees a slug like `../..` cannot escape .specify/assessments/.
- Metadata accuracy: the extension.yml and catalog.json descriptions no
longer imply a "build/kill" call is handed to /speckit.specify — only a
`go` hands off; a `kill` closes the assessment.
- Verdict determinism (decide): a `go` now explicitly requires evidence
strength `adequate`+ (never weak/unknown), resolving the conflict with
the thin-evidence guardrail.
- Risk polarity (decide): renamed the "Risk" criterion to "Risk posture"
with positive polarity (strong = risks understood and mitigated) so it
composes with the other scores that feed the verdict.
- README: aligned the go-threshold guardrail with the evidence rule and
documented the slug-normalization safety property.
The PR description was also updated to drop the stale before_specify
hook claim (the hook was removed in the previous commit).
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
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
Resolve review feedback on github/spec-kit#3568:
- catalog.json: bump top-level updated_at to this revision (2026-07-17)
- extension.yml + catalog.json: shorten the assess description to under
the documented 200-char manifest limit (kept aligned across both)
- extension.yml: make the before_specify hook prompt condition-neutral
(it fires on every /speckit.specify, so it must not claim "no
assessment found")
- intake.md: fix slug normalization to explicitly allow lowercase
letters a-z (the old rule permitted only digits and '-', contradicting
the offline-mode example)
- intake.md + research.md: require a sanitized source URL (strip
userinfo and credential/signature query params) instead of persisting
a verbatim URL that could leak secrets into project artifacts
- decide.md: remove the "trivially small" exception so a go always
requires a shaped concept, making verdict behavior deterministic and
consistent with the guardrails and README
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
Add a role-neutral, opt-in "Idea Assessment Pipeline" extension (id:
assess) covering the discovery work that happens BEFORE spec-driven
development. It provides a five-stage funnel: intake, research, define,
shape, decide, each writing one artifact under
.specify/assessments/<slug>/. A go verdict hands off to
/speckit.specify; killing an idea is a first-class success outcome.
Registration:
- extensions/catalog.json: bundled core opt-in entry (before bug)
- pyproject.toml: force-include maps into core_pack so it ships in the
installed wheel (verified via wheel build)
Also normalizes a Rich-wrapped substring assertion in test_workflows.py
so the suite passes at CI's 80-column non-TTY width.
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