Skip to content

fix: harden bundle and workflow step integrity - #4470

Open
marcelsafin wants to merge 14 commits into
github:mainfrom
marcelsafin:fix/bundle-step-version-pin
Open

fix: harden bundle and workflow step integrity#4470
marcelsafin wants to merge 14 commits into
github:mainfrom
marcelsafin:fix/bundle-step-version-pin

Conversation

@marcelsafin

@marcelsafin marcelsafin commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Description

Consolidate five related bundle and workflow-step integrity fixes at maintainer
request:

  • Scope dynamically loaded custom steps and imported modules to the current
    project, including same-path reloads that could otherwise reuse stale
    bytecode.
  • Reject duplicate bundle component declarations within the same kind while
    preserving valid identical IDs across different kinds.
  • Reject downloaded step packages whose declared version differs from trusted
    catalog metadata.
  • Roll back component installs, refreshes, and removals when saving bundle
    provenance fails, using actual installed metadata rather than bundle-record
    pins and reporting incomplete restoration explicitly.
  • Enforce bundle-declared workflow-step version pins before installation.

Each behavior has focused regression coverage and remains in a separate commit.
This PR supersedes #4466, #4467, #4468, and #4469.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

Validation on the consolidated current head:

  • Consolidated affected test files: 1,099 passed.
  • Full suite: 7,662 passed, 195 skipped, 1 deselected.
  • The deselected PowerShell-launcher test requires pwsh, which is unavailable
    locally and fails identically on unchanged upstream.
  • uvx ruff@0.15.0 check src tests
  • uvx --from pip-audit==2.10.0 pip-audit --disable-pip --require-hashes -r .github/security-audit-requirements.txt --progress-spinner off
  • git diff --check upstream/main...HEAD
  • Complete consolidated diff review against current upstream/main.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

GitHub Copilot (GPT-5.6 Sol) autonomously reproduced the bugs, wrote the
regression tests and implementations, consolidated the reviewed commits, and
ran verification under @marcelsafin's direction and review.

Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@marcelsafin
marcelsafin requested a review from mnriem as a code owner September 8, 2026 14:37
Copilot AI balanced review requested due to automatic review settings September 8, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The reviewed changes have appropriate regression coverage and no unresolved issues.

Pull request overview

Enforces bundle-declared version pins when installing workflow steps.

Changes:

  • Validates requested step versions against catalog metadata.
  • Adds regression coverage ensuring mismatches prevent installation.
File summaries
File Description
tests/unit/test_bundler_primitives.py Tests rejection of mismatched step versions.
src/specify_cli/bundler/services/primitives.py Adds step version-pin validation before installation.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mnriem mnriem added author-over-cap Over the 3-open-PR cap or repetitive batch submissions — please consolidate triage-must-have Verdict: high-value, important work for Spec Kit — do first labels Sep 9, 2026
@mnriem
mnriem requested a balanced review from Copilot September 9, 2026 13:51
@mnriem

mnriem commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

This is excellent work — you've filed a focused, well-tested set of bundle-integrity fixes (#4466#4470: component scoping, duplicate rejection, step-version mismatch, record-save rollback, and version-pin enforcement), all green with regression coverage and clear disclosure. Genuinely high-value stuff.

One process note: that's 5 open PRs, past the 3-open-PR guidance in CONTRIBUTING, and since they're all the same theme touching the bundler, they'd be much faster to review as a single consolidated PR (or a smaller stack) rather than five separate ones. Could you group the related ones? I want to land these — consolidating just helps them move quicker. Marking this one author-awaiting on that.

@mnriem mnriem added the author-awaiting Waiting on author response label Sep 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused implementation correctly mirrors existing workflow validation and includes adequate regression coverage.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

marcelsafin and others added 9 commits September 10, 2026 10:30
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 08:43
@marcelsafin marcelsafin changed the title fix: enforce bundle step version pins fix: harden bundle and workflow step integrity Sep 10, 2026
@marcelsafin

Copy link
Copy Markdown
Contributor Author

Consolidated all five fixes here on 6a254b6b, preserving each reviewed change as a separate commit. The five targeted regression groups pass (139 tests); the full suite passes with 7,647 passed, 195 skipped, and the known no-pwsh launcher test deselected. Ruff 0.15.0, pinned dependency audit, and diff checks are clean. #4466-#4469 are closed as superseded. Posted on behalf of @marcelsafin by GitHub Copilot (GPT-5.6 Sol).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Missing package versions and catalog lookup failures can still bypass the new version-integrity checks.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/specify_cli/bundler/services/primitives.py
Comment thread src/specify_cli/workflows/_commands.py Outdated
marcelsafin and others added 2 commits September 10, 2026 10:52
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 10:47
@marcelsafin

Copy link
Copy Markdown
Contributor Author

Addressed both current-head review findings in e0f957f and 42208be. Pinned bundle steps now fail closed when catalog/version resolution is unavailable, and downloaded step packages must provide a usable string version before normalized or opaque comparison. Added failing-first coverage for lookup failure, missing catalog versions, omitted package versions, and the opaque None collision. Final targeted run: 146 passed; full suite: 7,654 passed, 195 skipped, 1 known no-pwsh test deselected. Ruff 0.15.0, pinned dependency audit, CLI smoke, and diff checks pass. Posted on behalf of @marcelsafin by GitHub Copilot (GPT-5.6 Sol).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Provenance-save failures during bundle updates can leave refreshed or removed components inconsistent with the retained record.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

tests/unit/test_bundler_primitives.py:123

  • The new step-pin tests cover only rejection paths (mismatch, absent metadata, and lookup failure). Add a matching catalog-version case that asserts workflow_step_add is invoked, so a regression that rejects every valid pinned step cannot satisfy this test group.
  • Files reviewed: 11/11 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/specify_cli/bundler/services/installer.py
Comment thread tests/test_workflows.py Outdated
marcelsafin and others added 2 commits September 10, 2026 12:56
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GitHub Copilot (model: gpt-5.6-sol, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 11:17
@marcelsafin

Copy link
Copy Markdown
Contributor Author

@marcelsafin — GitHub Copilot (GPT-5.6 Sol) addressed the latest review round
on current head 130cb6ec7653c027c1a124e344a281655e59f761.

  • Bundle-update rollback now covers refreshed and dropped components when
    provenance saving fails.
  • Rollback snapshots actual installed component metadata, so shared components
    are restored to the version on disk rather than another bundle's recorded
    pin.
  • A failed best-effort rollback now surfaces an explicit inconsistent-state
    warning even when the triggering error is already a BundlerError.
  • Added positive exact/normalized step-version tests plus regressions for
    record-save failure, mismatched shared pins, and failed restoration.

Validation on this exact SHA: 1,099 affected-file tests passed; full suite
7,662 passed, 195 skipped, 1 known no-pwsh test deselected; Ruff 0.15.0,
pinned pip-audit, CLI smoke, and diff checks passed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Direct registry consumers can still leak project A’s custom step types into project B without rescanning.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 13/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +101 to +105
# Custom steps are project-scoped even though the registry and Python module
# cache are process-global. Clear the previous project's classes and package
# modules before every scan so removed or updated code cannot remain active.
for _type_key in tuple(STEP_REGISTRY):
if _type_key not in BUILTIN_STEP_TYPES:
@mnriem
mnriem requested a balanced review from Copilot September 10, 2026 13:19
@mnriem mnriem removed author-awaiting Waiting on author response author-over-cap Over the 3-open-PR cap or repetitive batch submissions — please consolidate labels Sep 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Rollback can misreport restoration, removal rollback remains incomplete, and project/version isolation has unresolved gaps.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

src/specify_cli/workflows/init.py:105

  • Clearing stale entries only when load_custom_steps is called leaves other step commands project-unsafe in a long-lived process. After scanning project A, workflow_step_add in project B still checks the stale global registry and can reject A's custom ID as a built-in; workflow_step_list/info can similarly report it as built-in. Update built-in-only consumers to use BUILTIN_STEP_TYPES (and ensure project-dependent consumers load the current root).
    # Custom steps are project-scoped even though the registry and Python module
    # cache are process-global. Clear the previous project's classes and package
    # modules before every scan so removed or updated code cannot remain active.
    for _type_key in tuple(STEP_REGISTRY):
        if _type_key not in BUILTIN_STEP_TYPES:
  • Files reviewed: 13/13 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment on lines +72 to +74
Atomicity is scoped, not global: completed component mutations are reversed
on failure, and the provenance record is written solely on full success.
Rollback is best-effort because primitive restoration can itself fail.
Comment on lines +154 to +158
metadata = metadata or {}
actual_version = version.strip() if isinstance(version, str) else None
source = metadata.get("source")
priority = metadata.get("priority")
return ComponentRef(
f"is disabled; re-run without --offline or install it first with "
f"'specify workflow step add {component.id}'."
)
self._assert_pinned_version(component)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants