Skip to content

feat: add specify bundle command#3070

Merged
mnriem merged 52 commits into
github:mainfrom
mnriem:mnriem/feat-bundler-spec-dogfood
Jun 19, 2026
Merged

feat: add specify bundle command#3070
mnriem merged 52 commits into
github:mainfrom
mnriem:mnriem/feat-bundler-spec-dogfood

Conversation

@mnriem

@mnriem mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a specify bundle subcommand group that composes existing Spec Kit primitives — extensions, presets, workflows, and steps — into versioned, installable units ("bundles") for role-based project setup.

The command group follows a thin-CLI-over-services design (all logic lives in src/specify_cli/bundler/services/, the CLI layer just wires arguments and rendering), is offline-first, and ships with a catalog stack for discovery.

Commands

  • specify bundle search — discover bundles in the catalog (with a verified/community trust indicator)
  • specify bundle info <name> — inspect a bundle's full component set + trust level
  • specify bundle install <name> — install/compose a bundle into a project
  • specify bundle update <name> — re-resolve and refresh installed components
  • specify bundle remove <name> — cleanly uninstall
  • specify bundle validate / specify bundle build — author-side validation and packaging

Highlights: semantic-version resolution, conflict detection, reference/security-path checking, local-artifact installs, and a verified-vs-community trust badge surfaced across search and info.

Dogfooding

This feature was built using Spec Kit on itself (specify init --integration copilot, then the full specify → clarify → plan → tasks → constitution → analyze → implement → converge loop). The PR was developed with the generated scaffolding present so the dogfooding stayed visible during review.

Scrub completed

All generated dogfooding scaffolding has now been removed from the branch so it does not land on merge:

  • .specify/** (runtime scripts, templates, workflows, integrations, extensions, feature.json, init-options.json, integration.json, extensions.yml) — except .specify/memory/constitution.md, see below
  • .github/agents/speckit.*, .github/prompts/speckit.*, .github/copilot-instructions.md
  • specs/001-spec-kit-bundler/** (spec, plan, tasks, research, data-model, contracts, quickstart)
  • The temporary .gitattributes / markdownlint exemptions that covered the above generated paths (both files reverted to the upstream baseline, plus the retained constitution exemption)

Retained

  • .specify/memory/constitution.md — the one dogfooding artifact we carry forward, as the project constitution governing this and future Spec-Driven work (kept with a single .gitattributes whitespace exemption).

What lands in the tool

src/specify_cli/bundler/**, src/specify_cli/commands/bundle/__init__.py, the bundler test suites (tests/contract, tests/unit, tests/integration, tests/bundler_helpers.py), examples/bundles/**, and docs (docs/reference/bundles.md, overview.md).

Testing

Full suite green (4174 passed, 3 skipped), including after the scrub. The bundler subset (tests/contract tests/unit tests/integration) passes on every push. Verified self-contained in a fresh worktree + venv.

mnriem and others added 17 commits June 19, 2026 08:09
Scaffold Spec Kit (--integration copilot) and run the full SDD workflow
against the `specify bundle` subcommand feature:

- spec.md (4 user stories, 31 FRs, 8 success criteria) + clarifications
- plan.md, research.md, data-model.md, contracts/, quickstart.md
- tasks.md (43 dependency-ordered tasks, organized by user story)
- Spec Kit Constitution v1.0.0 (code quality, testing, UX, performance,
  dependency/security principles) derived from deep codebase analysis
- plan Constitution Check + tasks grounded against the ratified principles

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements the Spec Kit Bundler as a `specify bundle ...` subcommand group
that calls existing primitive machinery in-process with zero new dependencies,
per the v1.0.0 constitution (Principles I-V).

Adds the `specify_cli.bundler` package (models, services, lib helpers) and the
`commands/bundle` Typer group wiring search, info, list, install, update,
remove, validate, build, init, and catalog list/add/remove (with --json and
--offline). Includes manifest/catalog schemas, version + integration-clash
gating, discovery-only refusal, idempotent install with atomic rollback,
non-collateral removal, and offline-first catalog resolution.

Ships an 82-test suite (contract/unit/integration), four sample role bundles
(product-manager, business-analyst, security-researcher, developer), README
"Bundles" docs, and an AGENTS.md pitfall on the test-venv gotcha. Marks
tasks T001-T043 complete and records follow-ups T044 (live in-process
primitive dispatch) and T045 (install from a local artifact path).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….venv

Add a "Running the full test suite" subsection under Automated checks covering
`uv pip install -e ".[test]"` + `.venv/bin/python -m pytest`, with the
shared/global editable-install contamination caveat that mirrors the AGENTS.md
pitfall.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t install

Closes the two follow-ups left after the initial bundler landing.

T044 — DefaultPrimitiveInstaller now performs real installs through existing
machinery instead of raising "use the primitive command" errors:
- presets/extensions install via their reusable managers
  (install_from_directory / install_from_zip); bundled assets install fully
  offline, catalog assets are fetched only when the network is allowed.
- workflows/steps delegate to the existing `workflow add` / `workflow step add`
  command callables in-process (project root as cwd), avoiding any duplicated
  download/validation logic (Principle I).
- `--offline` is threaded through DefaultPrimitiveInstaller(allow_network=…) so
  network-only kinds refuse with an actionable message rather than silently
  reaching out.

T045 — `specify bundle install` now accepts a local path (a built .zip
artifact, a bundle directory, or a bundle.yml) and installs directly without
consulting the catalog stack; bundle-ids still resolve via the stack.

Adds 13 tests (routing, offline gating, local-source resolution, and an
end-to-end offline build → install → list → remove of the bundled
agent-context extension). Bundler suite: 95 passing; ruff clean. Marks T044
and T045 complete in tasks.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ran the converge command: assessed the codebase against spec.md, plan.md,
tasks.md, and the v1.0.0 constitution. Appended 7 traceable gap-closure tasks
(T046–T052) as a new "Phase 8: Convergence" section. Append-only — no existing
tasks were modified and no application code was changed.

Findings: 1 CRITICAL (Constitution III — bundle group undocumented under
docs/reference/), 3 HIGH (FR-005/SC-007 validate references; FR-009/SC-002 info
expansion; FR-012 install-time init), 3 MEDIUM (FR-013 integration precedence;
FR-020 surface overlaps; FR-028 update refresh).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Close the gaps the converge command found between the bundler spec/plan/
constitution and the code:

- T046: add docs/reference/bundles.md documenting the full `specify bundle`
  command group; link it from docs/reference/overview.md (Constitution III).
- T047: wire a reference checker into `bundle validate` (services/references.py);
  online runs fail and name unresolved component references, offline runs warn.
- T048: expand `bundle info` to enumerate the full component set (versions,
  preset priority/strategy) plus the bundle integration — info == install.
- T049/T050: `bundle install`/`bundle init` now scaffold an uninitialized
  project via the existing `specify init` machinery, choosing the integration by
  precedence (override → bundle-declared → Copilot + OS default script type).
- T051: surface foreseeable component overlaps during info and install.
- T052: `bundle update` refreshes already-installed components via a new
  refresh path in install_bundle, preserving primitive-level overrides.

Adds unit/contract/integration coverage (107 tests pass).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-run of converge after Phase 8. The seven Phase 8 tasks are verified closed.
One residual partial gap remains: the `verified`/trust indicator (FR-010,
FR-027) is exposed only in `bundle info --json`, absent from `bundle search`
(the primary discovery surface) and `bundle info` text. Appended as a single
new task for implement to complete. Append-only; no code changed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`bundle search` (text + JSON) and `bundle info` (text + JSON) now expose each
catalog entry's verification/trust level (verified vs community), so users can
judge a bundle's trust before installing, per FR-010 / FR-027. Previously
`verified` was only present in `bundle info --json`.

Adds contract coverage; 108 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scaffold Spec Kit (--integration copilot) and run the full SDD workflow
against the `specify bundle` subcommand feature:

- spec.md (4 user stories, 31 FRs, 8 success criteria) + clarifications
- plan.md, research.md, data-model.md, contracts/, quickstart.md
- tasks.md (43 dependency-ordered tasks, organized by user story)
- Spec Kit Constitution v1.0.0 (code quality, testing, UX, performance,
  dependency/security principles) derived from deep codebase analysis
- plan Constitution Check + tasks grounded against the ratified principles

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements the Spec Kit Bundler as a `specify bundle ...` subcommand group
that calls existing primitive machinery in-process with zero new dependencies,
per the v1.0.0 constitution (Principles I-V).

Adds the `specify_cli.bundler` package (models, services, lib helpers) and the
`commands/bundle` Typer group wiring search, info, list, install, update,
remove, validate, build, init, and catalog list/add/remove (with --json and
--offline). Includes manifest/catalog schemas, version + integration-clash
gating, discovery-only refusal, idempotent install with atomic rollback,
non-collateral removal, and offline-first catalog resolution.

Ships an 82-test suite (contract/unit/integration), four sample role bundles
(product-manager, business-analyst, security-researcher, developer), README
"Bundles" docs, and an AGENTS.md pitfall on the test-venv gotcha. Marks
tasks T001-T043 complete and records follow-ups T044 (live in-process
primitive dispatch) and T045 (install from a local artifact path).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….venv

Add a "Running the full test suite" subsection under Automated checks covering
`uv pip install -e ".[test]"` + `.venv/bin/python -m pytest`, with the
shared/global editable-install contamination caveat that mirrors the AGENTS.md
pitfall.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t install

Closes the two follow-ups left after the initial bundler landing.

T044 — DefaultPrimitiveInstaller now performs real installs through existing
machinery instead of raising "use the primitive command" errors:
- presets/extensions install via their reusable managers
  (install_from_directory / install_from_zip); bundled assets install fully
  offline, catalog assets are fetched only when the network is allowed.
- workflows/steps delegate to the existing `workflow add` / `workflow step add`
  command callables in-process (project root as cwd), avoiding any duplicated
  download/validation logic (Principle I).
- `--offline` is threaded through DefaultPrimitiveInstaller(allow_network=…) so
  network-only kinds refuse with an actionable message rather than silently
  reaching out.

T045 — `specify bundle install` now accepts a local path (a built .zip
artifact, a bundle directory, or a bundle.yml) and installs directly without
consulting the catalog stack; bundle-ids still resolve via the stack.

Adds 13 tests (routing, offline gating, local-source resolution, and an
end-to-end offline build → install → list → remove of the bundled
agent-context extension). Bundler suite: 95 passing; ruff clean. Marks T044
and T045 complete in tasks.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ran the converge command: assessed the codebase against spec.md, plan.md,
tasks.md, and the v1.0.0 constitution. Appended 7 traceable gap-closure tasks
(T046–T052) as a new "Phase 8: Convergence" section. Append-only — no existing
tasks were modified and no application code was changed.

Findings: 1 CRITICAL (Constitution III — bundle group undocumented under
docs/reference/), 3 HIGH (FR-005/SC-007 validate references; FR-009/SC-002 info
expansion; FR-012 install-time init), 3 MEDIUM (FR-013 integration precedence;
FR-020 surface overlaps; FR-028 update refresh).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Close the gaps the converge command found between the bundler spec/plan/
constitution and the code:

- T046: add docs/reference/bundles.md documenting the full `specify bundle`
  command group; link it from docs/reference/overview.md (Constitution III).
- T047: wire a reference checker into `bundle validate` (services/references.py);
  online runs fail and name unresolved component references, offline runs warn.
- T048: expand `bundle info` to enumerate the full component set (versions,
  preset priority/strategy) plus the bundle integration — info == install.
- T049/T050: `bundle install`/`bundle init` now scaffold an uninitialized
  project via the existing `specify init` machinery, choosing the integration by
  precedence (override → bundle-declared → Copilot + OS default script type).
- T051: surface foreseeable component overlaps during info and install.
- T052: `bundle update` refreshes already-installed components via a new
  refresh path in install_bundle, preserving primitive-level overrides.

Adds unit/contract/integration coverage (107 tests pass).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-run of converge after Phase 8. The seven Phase 8 tasks are verified closed.
One residual partial gap remains: the `verified`/trust indicator (FR-010,
FR-027) is exposed only in `bundle info --json`, absent from `bundle search`
(the primary discovery surface) and `bundle info` text. Appended as a single
new task for implement to complete. Append-only; no code changed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`bundle search` (text + JSON) and `bundle info` (text + JSON) now expose each
catalog entry's verification/trust level (verified vs community), so users can
judge a bundle's trust before installing, per FR-010 / FR-027. Previously
`verified` was only present in `bundle info --json`.

Adds contract coverage; 108 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 14:30

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.

Pull request overview

Adds a new Spec Kit Bundler subsystem and wires it into the specify CLI as specify bundle ..., enabling role-based project setup via versioned “bundle” artifacts that compose existing primitives (extensions/presets/steps/workflows). The PR also includes a substantial dogfooding trail (spec artifacts + generated runtime scaffolding) and a comprehensive bundler-focused test suite.

Changes:

  • Introduces src/specify_cli/bundler/** (models/services/lib) implementing manifest validation, catalog stacking, install planning, installation/removal with provenance records, and artifact packaging.
  • Adds extensive unit/contract/integration tests for offline behavior, security path confinement, and install/update/remove lifecycle.
  • Updates documentation and examples to describe/illustrate bundle authoring, discovery, and installation.
Show a summary per file
File Description
tests/unit/test_bundler_versioning.py Unit tests for version parsing/constraints
tests/unit/test_bundler_resolver.py Resolver tests (version gate, integration compatibility)
tests/unit/test_bundler_references.py Reference-checker tests (online vs offline behavior)
tests/unit/test_bundler_records.py Installed-bundle record persistence/removal tests
tests/unit/test_bundler_primitives.py Primitive installer routing/offline gating tests
tests/unit/test_bundler_packager.py Bundle artifact packaging tests
tests/unit/test_bundler_conflict.py Conflict detection tests (integration + overlaps)
tests/integration/test_bundler_security_paths.py Path traversal/symlink confinement security tests
tests/integration/test_bundler_offline.py Offline-first catalog resolution tests
tests/integration/test_bundler_local_install.py Local path/zip install tests + end-to-end offline install
tests/integration/test_bundler_install_flow.py Install/record/remove lifecycle integration tests
tests/integration/test_bundler_init_install.py Init-on-install + integration precedence tests
tests/integration/test_bundler_catalog_stack.py Catalog stack precedence/policy/search integration tests
tests/contract/test_manifest_schema.py Manifest “schema contract” tests via model validation
tests/contract/test_catalog_schema.py Catalog “schema contract” tests + default stack shape
tests/bundler_helpers.py Shared helpers/fakes for bundler tests
src/specify_cli/bundler/services/validator.py Manifest structural + reference validation service
src/specify_cli/bundler/services/resolver.py Manifest → ordered install plan resolver
src/specify_cli/bundler/services/references.py Offline-first component reference resolution
src/specify_cli/bundler/services/packager.py Build bundle zip artifacts from bundle directories
src/specify_cli/bundler/services/installer.py Apply install plans; rollback; provenance record write
src/specify_cli/bundler/services/conflict.py Cross-bundle conflict detection (integration + overlaps)
src/specify_cli/bundler/services/catalog_stack.py Multi-source catalog stack resolution + search
src/specify_cli/bundler/services/adapters.py Concrete fetch/install adapters (network + primitives)
src/specify_cli/bundler/services/init.py Bundler services package init
src/specify_cli/bundler/models/records.py Installed bundle record model + persistence helpers
src/specify_cli/bundler/models/manifest.py Bundle manifest parsing + structural validation
src/specify_cli/bundler/models/catalog.py Catalog source/entry models + stack merge logic
src/specify_cli/bundler/models/init.py Bundler models package init
src/specify_cli/bundler/lib/yamlio.py Confined YAML/JSON IO helpers
src/specify_cli/bundler/lib/versioning.py SemVer + constraint evaluation helpers
src/specify_cli/bundler/lib/project.py Project root detection + active integration lookup
src/specify_cli/bundler/lib/init.py Bundler lib package init
src/specify_cli/bundler/commands_impl/catalog_config.py Project catalog config persistence helpers
src/specify_cli/bundler/commands_impl/init.py Bundler commands-impl package init
src/specify_cli/bundler/init.py Bundler package + BundlerError
src/specify_cli/init.py Registers the new bundle command group on the root CLI
specs/001-spec-kit-bundler/research.md Dogfooding research artifact
specs/001-spec-kit-bundler/quickstart.md Dogfooding quickstart/validation artifact
specs/001-spec-kit-bundler/plan.md Dogfooding implementation plan artifact
specs/001-spec-kit-bundler/data-model.md Dogfooding data model artifact
specs/001-spec-kit-bundler/contracts/cli-commands.md Bundler CLI behavior contract doc
specs/001-spec-kit-bundler/contracts/bundle-manifest.schema.md Bundle manifest contract doc
specs/001-spec-kit-bundler/contracts/bundle-catalog.schema.md Bundle catalog contract doc
specs/001-spec-kit-bundler/checklists/requirements.md Dogfooding requirements checklist
README.md Adds “Bundles” overview + usage examples
examples/bundles/security-researcher/README.md Example bundle documentation
examples/bundles/security-researcher/bundle.yml Example manifest
examples/bundles/product-manager/README.md Example bundle documentation
examples/bundles/product-manager/bundle.yml Example manifest
examples/bundles/developer/README.md Example bundle documentation
examples/bundles/developer/bundle.yml Example manifest
examples/bundles/business-analyst/README.md Example bundle documentation
examples/bundles/business-analyst/bundle.yml Example manifest
docs/reference/overview.md Adds “Bundles” reference entry
docs/reference/bundles.md New Bundles reference documentation
CONTRIBUTING.md Adds guidance for running tests in a local venv reliably
AGENTS.md Markdown formatting fixes + adds test-env gotcha
.specify/workflows/workflow-registry.json Generated workflow registry (dogfooding/runtime)
.specify/workflows/speckit/workflow.yml Generated workflow definition (dogfooding/runtime)
.specify/templates/spec-template.md Generated template (dogfooding/runtime)
.specify/templates/plan-template.md Generated template (dogfooding/runtime)
.specify/templates/constitution-template.md Generated template (dogfooding/runtime)
.specify/templates/checklist-template.md Generated template (dogfooding/runtime)
.specify/scripts/bash/setup-tasks.sh Generated script (dogfooding/runtime)
.specify/scripts/bash/setup-plan.sh Generated script (dogfooding/runtime)
.specify/scripts/bash/check-prerequisites.sh Generated script (dogfooding/runtime)
.specify/integrations/speckit.manifest.json Generated integration manifest (dogfooding/runtime)
.specify/integrations/copilot.manifest.json Generated integration manifest (dogfooding/runtime)
.specify/integration.json Generated runtime integration state (dogfooding/runtime)
.specify/init-options.json Generated init options (dogfooding/runtime)
.specify/feature.json Generated feature pointer (dogfooding/runtime)
.specify/extensions/agent-context/scripts/bash/update-agent-context.sh Generated extension script (dogfooding/runtime)
.specify/extensions/agent-context/README.md Generated extension docs (dogfooding/runtime)
.specify/extensions/agent-context/extension.yml Generated extension manifest (dogfooding/runtime)
.specify/extensions/agent-context/commands/speckit.agent-context.update.md Generated extension command (dogfooding/runtime)
.specify/extensions/agent-context/agent-context-config.yml Generated extension config (dogfooding/runtime)
.specify/extensions/.registry Generated extensions registry (dogfooding/runtime)
.specify/extensions.yml Generated extensions config (dogfooding/runtime)
.github/prompts/speckit.taskstoissues.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.tasks.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.specify.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.plan.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.implement.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.constitution.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.clarify.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.checklist.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.analyze.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/prompts/speckit.agent-context.update.prompt.md Generated Copilot prompt scaffold (dogfooding/runtime)
.github/copilot-instructions.md Generated Copilot context pointer (dogfooding/runtime)
.github/agents/speckit.taskstoissues.agent.md Generated Copilot agent scaffold (dogfooding/runtime)
.github/agents/speckit.plan.agent.md Generated Copilot agent scaffold (dogfooding/runtime)
.github/agents/speckit.constitution.agent.md Generated Copilot agent scaffold (dogfooding/runtime)
.github/agents/speckit.agent-context.update.agent.md Generated Copilot agent scaffold (dogfooding/runtime)

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 110/110 changed files
  • Comments generated: 8

Comment thread src/specify_cli/bundler/services/validator.py
Comment thread src/specify_cli/bundler/services/adapters.py Outdated
Comment thread src/specify_cli/bundler/services/adapters.py
Comment thread src/specify_cli/bundler/models/manifest.py Outdated
Comment thread src/specify_cli/bundler/services/adapters.py Outdated
Comment thread src/specify_cli/bundler/services/adapters.py Outdated
Comment thread tests/unit/test_bundler_packager.py
Comment thread .specify/integration.json Outdated
…errors, reproducible builds

Resolves automated review feedback on github#3070:

- validator: drop redundant string-quoting on ReferenceChecker's
  `str | None` return so the annotation evaluates as a real union under
  `from __future__ import annotations`.
- adapters: normalize Windows drive-letter paths (e.g. C:\...) to the
  local-file branch so offline file catalogs resolve on Windows.
- adapters: enforce HTTPS (HTTP only for localhost) and require a host on
  remote catalog URLs before any network call, mirroring
  specify_cli.catalogs URL validation (MITM/downgrade protection).
- adapters: pass `origin` to loads_json for local files and HTTP payloads
  so JSON parse errors name the real source instead of <string>.
- manifest: parse component `priority` defensively, raising an actionable
  BundlerError on non-integer values instead of a raw ValueError.
- packager: write zip members with a fixed timestamp + permissions so
  identical inputs yield byte-for-byte identical artifacts (genuinely
  reproducible builds), and strengthen the determinism test accordingly.

Adds regression tests for priority validation, plain-HTTP/host rejection,
and byte-level artifact reproducibility (111 bundler tests pass; ruff clean).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. Addressed all seven code findings in b762e4b:

  • validator.py — removed the redundant string-quoting on ReferenceChecker's str | None return so it evaluates as a real union under from __future__ import annotations.
  • adapters.py (Windows paths) — drive-letter paths like C:\catalog.json now normalize to the local-file branch instead of falling through to "unsupported scheme".
  • adapters.py (HTTPS) — remote catalog URLs are now validated up front (HTTPS required, HTTP only for localhost, host required) before any network call, mirroring specify_cli.catalogs._validate_catalog_url.
  • adapters.py (origin)loads_json now receives the real file path / URL as origin, so JSON parse errors name the actual source instead of <string> (both local and HTTP paths).
  • manifest.py (priority)priority is parsed defensively and raises an actionable BundlerError on non-integer input instead of a raw ValueError.
  • packager.py (determinism) — members are now written with a fixed timestamp + permissions, so identical inputs produce byte-for-byte identical artifacts; the test now asserts true byte-level reproducibility rather than just member ordering.

Added regression tests for priority validation, plain-HTTP/missing-host rejection, and byte-level artifact reproducibility. 111 bundler tests pass; ruff clean.

The remaining comment on .specify/integration.json is expected — see the inline reply.

Posted by GitHub Copilot (model: Claude Opus 4.8) on behalf of @mnriem.

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.

Copilot's findings

  • Files reviewed: 110/110 changed files
  • Comments generated: 2

Comment thread src/specify_cli/bundler/services/packager.py Outdated
Comment thread src/specify_cli/bundler/services/adapters.py Outdated
… URLs

- packager: when --output points inside the bundle directory, exclude the
  whole output subtree from collection so previously-built artifacts are
  never re-packaged (prevents broken reproducibility and unbounded growth).
- adapters: resolve file:// catalog URLs via url2pathname and preserve
  netloc, so Windows file URLs (file:///C:/...) and UNC shares
  (file://server/share) resolve correctly instead of dropping the host or
  producing /C:/x.

Adds regression tests for nested-output exclusion and file:// resolution
(113 bundler tests pass; ruff clean).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed both findings in 9f5a542:

  • packager.py (nested output dir) — when --output points to a directory inside the bundle (e.g. dist/), the whole output subtree is now excluded from collection, so previously-built artifacts are never re-packaged. This keeps builds reproducible and bounded.
  • adapters.py (file:// URLs)file:// catalog sources now resolve via url2pathname and preserve netloc, so Windows file URLs (file:///C:/...) and UNC shares (file://server/share) resolve correctly instead of dropping the host or yielding /C:/x.

Added regression tests for nested-output exclusion and file:// resolution. 113 bundler tests pass; ruff clean.

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.

Copilot's findings

  • Files reviewed: 110/110 changed files
  • Comments generated: 5

Comment thread src/specify_cli/bundler/services/packager.py Outdated
Comment thread src/specify_cli/bundler/models/records.py
Comment thread src/specify_cli/bundler/services/installer.py
Comment thread README.md
Comment thread specs/001-spec-kit-bundler/quickstart.md Outdated

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.

Copilot's findings

  • Files reviewed: 114/114 changed files
  • Comments generated: 7

Comment thread src/specify_cli/bundler/lib/versioning.py
Comment thread src/specify_cli/bundler/lib/versioning.py
Comment thread src/specify_cli/bundler/services/validator.py
Comment thread src/specify_cli/bundler/services/validator.py
Comment thread src/specify_cli/commands/bundle/__init__.py
Comment thread src/specify_cli/commands/bundle/__init__.py
Comment thread src/specify_cli/commands/bundle/__init__.py Outdated
- versioning: tolerate an uppercase `V` prefix in `_normalize_semver` and
  `is_semver`, mirroring specify_cli._version tag normalization (V -> v) so
  `V1.2.3` parses and validates consistently.
- validator: import BundlerError and narrow the speckit_version constraint
  except clause to `BundlerError` only, so programming errors are no longer
  masked behind an "invalid constraint" message.
- bundle update: accept `--integration` and thread it through
  resolve_install_plan the same way `bundle install` does (override used only
  when the active integration can't be auto-detected), so integration-pinned
  bundles can be updated where `.specify/integration.json` is missing/unreadable.
- bundle validate: fold reference warnings into `report.warnings` so the
  ValidationReport is the single warning channel at the CLI layer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed review 4535234003 (commit 54b37b2).

  • versioning._normalize_semver / is_semver — Now tolerate an uppercase V prefix in addition to lowercase v, mirroring specify_cli._version tag normalization (Vv), so V1.2.3 parses and validates consistently.
  • validator.validate_manifest — Imported BundlerError and narrowed the requires.speckit_version except clause to BundlerError only. parse_constraint() already wraps parse failures in BundlerError, so unexpected programming errors now propagate instead of being masked behind an "invalid constraint" message.
  • bundle update — Added a --integration option, threaded through resolve_install_plan() the same way bundle install does: the project's detected active integration is authoritative, and the override is used only when detection is indeterminate. This makes the resolver's "re-run with --integration" guidance actionable and unblocks updating integration-pinned bundles where .specify/integration.json is missing/unreadable.
  • bundle validate — Reference warnings are now folded into report.warnings, so ValidationReport is the single warning channel at the CLI layer instead of maintaining two.

Added regressions (uppercase-V semver, validator BundlerError-only catch + non-BundlerError propagation, update --integration presence). Bundler suite: 195 passing; ruff clean.

Rich can split the "--integration" option label with ANSI escape codes
between the two leading dashes, so the literal substring check failed under
CI's terminal settings. Match the un-split option word instead, mirroring how
test_bundle_help_lists_all_commands checks bare command names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem mnriem changed the title feat: add specify bundle command for role-based project setup (dogfooding, scrub generated files before merge) feat: add specify bundle command Jun 19, 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.

Copilot's findings

  • Files reviewed: 115/115 changed files
  • Comments generated: 1

Comment thread src/specify_cli/bundler/services/resolver.py

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.

Copilot's findings

  • Files reviewed: 115/115 changed files
  • Comments generated: 2

Comment thread src/specify_cli/bundler/services/packager.py
Comment thread src/specify_cli/bundler/services/installer.py
…pins

Addresses review 4535280786:

- packager.build_bundle: no longer forces every ZIP member to 0644, which
  stripped the executable bit from bundled scripts (e.g. extension hook
  scripts) and could break them after extraction. Permissions are now
  normalized reproducibly to 0755 when the source file has any execute bit
  set, otherwise 0644 — identical inputs still yield byte-for-byte identical
  artifacts.
- installer.install_bundle + docs/reference/bundles.md: document that version
  pins are enforced install-time only. Because primitive is_installed checks
  are id-based (not version-aware), an already-present component is skipped
  during install without comparing its on-disk version to the manifest pin;
  pins are guaranteed applied only on a real install or `bundle update` refresh.

Added a regression asserting executable sources map to 0755 and plain files to
0644 in the built artifact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed review 4535280786 (commit 9f86e3c).

  • packager.build_bundle — Stopped forcing every ZIP member to 0644, which stripped the executable bit from bundled scripts (e.g. extension hook scripts) and could break them after extraction. Permissions are now normalized reproducibly: 0755 when the source file has any execute bit set, otherwise 0644. Identical inputs still produce byte-for-byte identical artifacts. Added a regression asserting an executable source maps to 0755 and a plain file to 0644.
  • installer.install_bundle + docs/reference/bundles.md — Documented that version-pin enforcement is install-time only. Because the primitive is_installed checks are id-based (not version-aware), an already-present component is skipped during install without comparing its on-disk version to the manifest pin; pins are guaranteed applied only when the bundler actually installs a component or refreshes it via specify bundle update. Both the install_bundle docstring and the user-facing reference now spell this out.

Bundler suite: 196 passing; ruff clean.

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.

Copilot's findings

  • Files reviewed: 115/115 changed files
  • Comments generated: 1

Comment thread src/specify_cli/bundler/lib/versioning.py
mnriem and others added 2 commits June 19, 2026 16:27
Windows filesystems do not carry Unix execute bits, so chmod(0o755) is a no-op
and the source file reports no execute bit — the packager then correctly stores
the member as 0644. The assertion that an executable source maps to 0755 is only
meaningful on POSIX, so skip it on nt rather than asserting platform-specific
behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review 4535327154: parse_version() normalized SemVer prerelease
spellings (e.g. 1.2.3-rc1 -> 1.2.3rc1) but parse_constraint() passed the
constraint to packaging.SpecifierSet unmodified, so ">=1.2.3-rc1" raised
InvalidSpecifier even though the same spelling is accepted for installed
versions. parse_constraint() now normalizes the version portion of each
comma-separated clause via the shared _normalize_semver helper, so prerelease
handling is consistent across versions and constraints.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 21:29
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed review 4535327154 (commit 2b05e8e).

  • versioning.parse_constraint — Previously passed the constraint to packaging.SpecifierSet unmodified, so a SemVer prerelease spelling like >=1.2.3-rc1 raised InvalidSpecifier even though parse_version() accepts the same spelling for installed versions. parse_constraint() now normalizes the version portion of each comma-separated clause through the shared _normalize_semver helper (operator preserved), so prerelease handling is consistent across versions and constraints. Empty/permissive constraints and wildcard specifiers (==1.2.*) are unaffected.

Added regressions covering prerelease constraints (>=1.2.3-rc1, multi-clause, mixed alpha/beta spellings) and the empty-constraint case. Bundler suite: 201 passing; ruff clean.

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.

Copilot's findings

  • Files reviewed: 115/115 changed files
  • Comments generated: 3

Comment thread src/specify_cli/bundler/models/records.py
Comment thread src/specify_cli/bundler/models/records.py
Comment thread src/specify_cli/bundler/commands_impl/catalog_config.py
…ields

Addresses review 4535351596:

- records.load_records: validate the on-disk 'schema_version' (required;
  forward-compatible across same-major minor bumps) and fail fast with an
  actionable error on a missing/unknown version, rather than silently parsing a
  possibly-incompatible format and risking incorrect bundle attribution/removal.
- records.InstalledBundleRecord.from_dict: treat missing 'bundle_id' or
  'version' as corruption and raise BundlerError, instead of coercing them to
  empty strings that let later list/remove/update operations behave
  unpredictably.
- catalog_config._read: validate 'schema_version' when present (same-major
  compatibility) and fail fast on an unsupported version so an incompatible
  future config shape can't be mis-parsed into a wrong effective catalog stack.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed review 4535351596 (commit 02c3489).

  • records.load_records — Now validates the on-disk schema_version: required, with forward-compatible acceptance across same-major minor bumps, and a fail-fast actionable error on a missing/unknown version. Previously the written schema_version was never checked on read, so an incompatible future format (or corruption) could be silently mis-parsed into incorrect bundle attribution/removal.
  • records.InstalledBundleRecord.from_dict — Missing bundle_id or version is now treated as corruption and raises a BundlerError, instead of being coerced to empty strings that let later list/remove/update operations behave unpredictably.
  • catalog_config._read — Now validates schema_version when present (same-major compatibility) and fails fast on an unsupported version, so an incompatible future config shape can't be mis-parsed into a wrong effective catalog stack.

Added regressions for each (missing/unknown/forward-compatible schema versions, missing identity fields) and updated the existing corruption tests to carry a valid schema_version. Bundler suite: 209 passing; ruff clean.

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.

Copilot's findings

  • Files reviewed: 115/115 changed files
  • Comments generated: 3

Comment thread src/specify_cli/bundler/services/installer.py
Comment thread .markdownlint-cli2.jsonc Outdated
Comment thread .gitattributes Outdated
The bundler feature was developed by dogfooding Spec Kit on itself. Now that
the work is complete, remove all generated scaffolding so it does not land in
the repository on merge:

- specs/001-spec-kit-bundler/** (spec, plan, research, data-model, contracts,
  quickstart, tasks, checklists)
- .specify/** (extensions, integrations, scripts, templates, workflows,
  feature/init/integration metadata)
- .github/agents/speckit.*.agent.md, .github/prompts/speckit.*.prompt.md, and
  .github/copilot-instructions.md (Copilot integration scaffold)

Retained: .specify/memory/constitution.md — the single dogfooding artifact
carried forward — with its whitespace exemption in .gitattributes.

.gitattributes and .markdownlint-cli2.jsonc are reverted to the upstream
baseline (plus the constitution whitespace exemption), dropping the now-moot
exemptions for the removed scaffold.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants