mirror of
https://github.com/github/spec-kit.git
synced 2026-08-03 06:26:30 +08:00
1. workflow run ownership check followed symlinks via Path.resolve()
before mapping a direct YAML path back to its installed workflow ID.
A symlinked .specify/workflows/<id>/workflow.yml resolved outside the
tree, missed the ownership match entirely, and let the disabled-workflow
guard be silently skipped while engine.load_workflow still followed the
symlink. Now maps ownership from a lexically-normalized path (os.path.
normpath, no symlink following) and explicitly refuses to run if the
installed <id> directory or workflow.yml leaf is itself a symlink.
Direct external workflow paths that don't match .specify/workflows/...
are unaffected.
2. WorkflowRegistry._load() caught a read OSError and silently fell back
to an empty in-memory registry, only blocking a later save(). Callers
that only query is_installed()/get()/list() before writing a file
(e.g. commands/init.py's bundled speckit install, which overwrites
workflow.yml once is_installed() reports false) could act on that
false-empty state and destroy real data before ever reaching save().
_load() now raises OSError immediately so an unreadable registry fails
closed at construction, before any query or side effect is possible.
Added _open_workflow_registry() to give every CLI command a consistent
clean-error boundary around registry construction.
3. _validate_and_install_local's mkdir/copy2 ran before the try/except
that protected registry.add(); a copy2 failure (e.g. a truncating
partial write on a reinstall) was not caught at all, so the existing
backup-restore cleanup never ran and the prior working workflow.yml
was corrupted with a raw traceback surfaced to the user. mkdir/copy2
now run inside the same rollback-protected section as registry.add(),
sharing one _cleanup_failed_install() helper.
4. workflow update's skip message claimed any non-catalog source was
installed "from a local path or URL", which is wrong for the bundled
speckit workflow (source: "bundled"). Message is now source-neutral.
Verified all 4 threads are current (not outdated) via GraphQL review
thread query on PR #3419, HEAD 812050a.
Tests: strict TDD per fix (red test proving each bug, minimal production
change, green). tests/test_workflows.py: 474 passed. Full suite: 3976
passed, 110 skipped. ruff check: all checks passed on touched files and
full src tree.
Assisted-by: GitHub Copilot (model: Claude Sonnet 5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>