Skip to content

feat(bundles): allow bundles to reference other bundles - #5067

Open
markuswondrak wants to merge 17 commits into
github:mainfrom
markuswondrak:feat/5047-bundle-references
Open

markuswondrak wants to merge 17 commits into
github:mainfrom
markuswondrak:feat/5047-bundle-references

Conversation

@markuswondrak

@markuswondrak markuswondrak commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

Implements #5047: a bundle.yml can reference an exact version of another bundle via provides.bundles. References are expanded into the parent's component plan before installation (manifest expansion; no child-bundle records), reusing existing catalog precedence, release verification, install policy, conflict checks and primitive installers.

  • Validation, bundle info, install, update and refresh resolve the same expanded plan.
  • bundle info shows referenced bundles, versions and the components they introduce.
  • Cycles, duplicate direct references, incompatible component requirements and incompatible Spec Kit version constraints are rejected with actionable errors; compatible shared components are installed once.
  • Updates follow the pins in the selected manifest and never advance a referenced bundle independently.
  • Manifests without bundle references behave as before.

Deviations from the issue text

Field name and expansion-only/install-time design follow the proposed resolutions that @mnriem approved. Beyond or different from the issue text:

  • Integration of referenced bundles warns instead of erroring. The issue asks for incompatible integration constraints to error. This is intentional: an installation has one project integration, and only the parent (root) bundle establishes or validates that boundary. Referenced bundles are expanded into the parent's component plan; they cannot select or switch the project's integration. A root integration mismatch—or a root pin when the project's integration cannot be determined without explicit confirmation—remains a hard error. A referenced bundle's differing or unknown integration is instead reported as a warning with its reference chain. In other words, references do not need to be independently installable for the selected project integration; their declared target remains visible to the user. The Spec Kit version requirement stays a hard error for every bundle in the graph.
  • Parent declaration wins over references. The issue says to reject incompatible requirements. Here, a component declared in the parent overrides the same component from any reference, with a warning naming the discarded value and its origin. Conflicts between two referenced bundles remain errors.
  • Added limit: at most 50 distinct referenced bundles per expansion (besides the depth limit of 10 and cycle detection).
  • Manifest schema: a non-empty provides.bundles requires schema_version: "1.1"; reference entries also accept an optional source (expected winning catalog name).

Testing

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

.venv/bin/python -m pytest tests/specify_cli/bundles → 1104 passed. New positive/negative coverage in test_expansion.py, test_manifest.py, test_sources.py, test_command_info.py, test_command_validate.py, and install/update/conflict/installer tests.

AI Disclosure

  • I did use AI assistance

AI disclosure: Implemented with pi coding agent (model: claude-sonnet-5.5 via GitHub Copilot, reasoning effort: high) on behalf of @markuswondrak. AI generated the code, tests and docs; commit carries an Assisted-by: trailer.

Closes #5047

Add `provides.bundles` so a bundle.yml can reference an exact version of
another bundle. References are expanded into the parent's component plan
before installation, using existing catalog precedence, install policy,
conflict checks and primitive installers. Validation, `bundle info`,
install, update and refresh resolve the same expanded plan.

Closes github#5047

Assisted-by: pi (model: claude-sonnet-5.5, autonomous)
@markuswondrak
markuswondrak requested a review from mnriem as a code owner October 23, 2026 04:18
Copilot AI max review requested due to automatic review settings October 23, 2026 04:18

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

Referenced bundles can bypass required integration compatibility checks before installation.

3 open issues
What changed in this PR

Implements #5047 by allowing bundles to reference exact versions of other bundles, expanding their components into the existing installation and lifecycle flows without creating child-bundle records.

Changes:

  • Adds schema 1.1 references, recursive expansion, conflict detection, and provenance.
  • Connects expanded plans to validation, preview, installation, updates, and installed records.
  • Adds graph and lifecycle tests, plus documentation of resolution rules and limitations.

Validation was limited to targeted, dependency-free checks; the project test suite was not run.

AI disclosure: GitHub Copilot (model unknown; autonomous review mode) generated this review for the requester.

File Description
tests/​specify_cli/​bundles/​test_validator.py Tests expanded-component validation.
tests/​specify_cli/​bundles/​test_sources.py Tests reference loading and catalog policies.
tests/​specify_cli/​bundles/​test_resolver.py Tests expanded plans and requirement reporting.
tests/​specify_cli/​bundles/​test_records.py Tests reference serialization and corrupt records.
tests/​specify_cli/​bundles/​test_packager.py Tests reference-only packaging and structural rejection.
tests/​specify_cli/​bundles/​test_manifest.py Tests reference parsing and schema validation.
tests/​specify_cli/​bundles/​test_installer.py Tests expanded-component ownership and lifecycle.
tests/​specify_cli/​bundles/​test_expansion.py Tests graph expansion, conflicts, and provenance.
tests/​specify_cli/​bundles/​test_conflict.py Tests expanded conflicts and setting comparisons.
tests/​specify_cli/​bundles/​test_command_validate.py Tests online expansion and offline warnings.
tests/​specify_cli/​bundles/​test_command_update.py Tests reference-pin updates and component removal.
tests/​specify_cli/​bundles/​test_command_list.py Tests reference metadata in JSON output.
tests/​specify_cli/​bundles/​test_command_install.py Tests local and catalog parent expansion.
tests/​specify_cli/​bundles/​test_command_info.py Tests reference previews and provenance rendering.
tests/​specify_cli/​bundles/​test_command_build.py Tests reference-only artifact creation.
tests/​specify_cli/​bundles/​test_catalog_stack.py Tests structured missing-release errors.
tests/​specify_cli/​bundles/​helpers.py Adds reference fixtures and fake loader.
src/​specify_cli/​bundles/​validator.py Accepts expanded components for reference checks.
src/​specify_cli/​bundles/​sources.py Loads pinned references through catalog resolution.
src/​specify_cli/​bundles/​resolver.py Carries references and aggregates requirements.
src/​specify_cli/​bundles/​records.py Persists resolved bundle references.
src/​specify_cli/​bundles/​manifest.py Adds schema 1.1 and reference validation.
src/​specify_cli/​bundles/​installer.py Uses expanded components and records references.
src/​specify_cli/​bundles/​expansion.py Implements recursive expansion and provenance.
src/​specify_cli/​bundles/​conflict.py Shares setting comparisons and accepts expanded components.
src/​specify_cli/​bundles/​command_validate.py Adds phased expansion and component validation.
src/​specify_cli/​bundles/​command_update.py Resolves references before refreshing components.
src/​specify_cli/​bundles/​command_install.py Expands references before compatibility checks.
src/​specify_cli/​bundles/​command_info.py Displays references, origins, and overrides.
src/​specify_cli/​bundles/​catalog_stack.py Introduces structured release-resolution errors.
src/​specify_cli/​bundles/​_commands.py Shares expansion and expanded-overlap helpers.
docs/​reference/​bundles.md Documents reference semantics, lifecycle, and limitations.

Review effort: Max

Comment thread src/specify_cli/bundles/expansion.py
Comment thread src/specify_cli/bundles/resolver.py
Comment thread src/specify_cli/bundles/expansion.py Outdated
Address extension-priority conflicts and provenance merging, clarify\noffline/local-preview behavior, and document validation's runtime-version\ngate boundary.\n\nAssisted-by: pi (model: gpt-5.6-terra, autonomous)
Copilot AI max review requested due to automatic review settings October 23, 2026 05:05
@markuswondrak

Copy link
Copy Markdown
Contributor Author

Review-follow-up — 2dd334e

  • Treat extension priority as an install-affecting compatibility setting, with sibling-conflict and parent-override coverage.
  • Preserve deduplicated override provenance from agreeing reference paths while retaining the shortest component origin.
  • Clarify offline catalog/local-root behavior, report an offline reference-resolution limitation instead of a misleading catalog miss, and make local bundle info preview unsupported explicitly.
  • Document that bundle validate does not evaluate the runtime Spec Kit version gate.

Validation: .venv/bin/python -m pytest tests/specify_cli/bundles -q → 1104 passed; ruff check and git diff --check passed. The sample-project test was reported by the contributor.

AI disclosure: Posted on behalf of @markuswondrak by pi coding agent (GitHub Copilot; model: gpt-5.6-terra; reasoning: high; autonomous). The agent implemented the changes, tests, documentation, and this summary; the contributor directed the review dispositions and supplied the sample-project test result.

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.

Comment thread src/specify_cli/bundles/command_info.py Outdated
Comment thread src/specify_cli/bundles/expansion.py Outdated
Comment thread src/specify_cli/bundles/installer.py Outdated
Markus Wondrak and others added 3 commits October 23, 2026 08:59
Remove the local-path guard from bundle info so a bare catalog ID is not
rejected when a same-named file or directory exists. Add a regression.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Identify an override by the discarded component and its declaring bundle
and keep the shortest chain, so shared graphs no longer grow override
histories exponentially. Applies to merged and inherited histories.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cross-bundle installed-pin checks compare priority for presets only, as
before. Reference expansion still compares extension priority strictly.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI max review requested due to automatic review settings October 23, 2026 06:59
@markuswondrak

Copy link
Copy Markdown
Contributor Author

Review-follow-up — bba1038

  • bundle info: removed the local-path guard; a bare catalog ID is no longer rejected when a same-named path exists (9d538b7). Regression added for info and info --versions.
  • Overrides: records are now keyed by the discarded component and its declaring bundle, keeping the shortest chain, for both merged and inherited histories (5d7501e). Layered-sharing regression (1/4/7 layers, both sibling orders) fails before and passes after; distinct declarations stay separate.
  • Extension priority: the installed-pin check compares priority for presets only, as before; reference expansion stays strict (bba1038). Installer-level tests cover matching, None vs 10, and differing priorities, with and without --refresh; source mismatch is still rejected.

Validation: .venv/bin/python -m pytest tests/specify_cli/bundles → 1123 passed.

AI disclosure: GitHub Copilot CLI (model: Claude Sonnet 5.5, supervised step by step by the contributor) posted this on behalf of @markuswondrak; analysis, code, tests and this summary are AI-generated. Commits carry Assisted-by: trailers.

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.

🔵 Needs a closer look

Recursive catalog resolution and ownership-sensitive lifecycle changes need final maintainer validation beyond the isolated verification performed here.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Normalize omitted and default extension priorities before comparison

src/​specify_cli/​bundles/​conflict.py:36

Compare effective extension priorities rather than their raw declarations. _ExtensionKindManager._do_install in primitives.py:424 maps an omitted priority to DEFAULT_PRIORITY (10), but this comparison treats None and 10 as different. Two sibling references declaring the same extension and version with omitted versus explicit default priority therefore fail expansion, although they install identically. A parent declaration also produces a spurious override warning. Normalize missing extension priorities before comparing, preserve preset comparisons and extension_priority=False, and add regression coverage for both reference orders and the parent-override case.

3 resolved since last review

Review effort: Max

An omitted extension priority installs as DEFAULT_PRIORITY (10), but
component_setting_differences compared the raw None against 10. Sibling
references declaring the same extension with omitted vs. explicit default
priority failed expansion, and a parent declaration emitted a spurious
override warning. Preset comparison and extension_priority=False are
unchanged.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@markuswondrak

markuswondrak commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Addressed the previously missed finding from review 5625099694 in 7462c92.

component_setting_differences now treats an omitted extension priority as DEFAULT_PRIORITY (10) before comparing, so sibling references with omitted vs. explicit default priority no longer fail expansion and a parent declaration no longer produces a spurious override warning. Preset comparison and extension_priority=False are unchanged. New tests cover both reference orders, the parent-override case, and negatives (non-default priority still conflicts; presets still compare raw). The new tests fail without the change; tests/specify_cli/bundles passes (1133).

No threads were resolved on the reviewer's behalf.

AI disclosure: this comment and the commit were authored by GitHub Copilot CLI (Claude Sonnet 5.5) on behalf of the PR author.

Copilot AI max review requested due to automatic review settings October 23, 2026 07:45

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.

🔵 Needs a closer look

Recursive catalog resolution and ownership-sensitive lifecycle changes need final human review and confirmation of lifecycle test results.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Align bundle info settings comparison with effective component values

src/​specify_cli/​bundles/​command_info.py:294

bundle info compares raw settings here, while expansion compares their effective, kind-specific values. For example, if a child declares extension style@1.0.0 without a priority and the parent overrides it with style@2.0.0, priority: 10, the preview reports priority=<unset> as overridden even though both priorities resolve to 10. It also reports extension strategy changes, although strategy only affects presets.

Match component_setting_differences: normalize omitted extension priorities and ignore settings that do not apply to the component kind. Preserve the existing comparison behavior for dictionaries without kind, and cover omitted/default priorities in both directions with regression tests.

Review effort: Max

bundle info compared raw priority/strategy values, so an omitted
extension priority showed as overridden by the default 10, and extension
strategy changes were reported though only presets use strategy.

Move the kind-specific rules into conflict.effective_setting and use it
from both component_setting_differences and bundle info. Provenance
without a kind keeps the raw comparison.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI max review requested due to automatic review settings October 23, 2026 08:14
@markuswondrak

Copy link
Copy Markdown
Contributor Author

Addressed the previously missed finding from review 5625522768 in 5570973.

The kind-specific setting rules now live in one place, conflict.effective_setting, which both component_setting_differences and bundle info use. In bundle info, an omitted extension priority equals the default (10), extension strategy is ignored, and presets still compare raw values. Provenance without kind keeps the raw comparison. New tests cover omitted/default priority in both directions, a non-default extension priority, extension strategy, presets, and agreement between bundle info and component_setting_differences; the behavioral ones fail without the change. tests/specify_cli/bundles passes (1153).

No threads were resolved on the reviewer's behalf.

AI disclosure: this comment and the commit were authored by GitHub Copilot CLI (Claude Sonnet 5.5) on behalf of the PR author.

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.

🔵 Needs a closer look

Recursive resolution crosses catalog trust and ownership-sensitive lifecycle boundaries, so final human validation is needed.

1 open issue

Review effort: Max

Comment thread src/specify_cli/bundles/command_info.py
A verified root could reference a community bundle with no indication in
`bundle info`. Carry the catalog entry's `verified` flag through
LoadedBundle and ResolvedReference, and show it per reference in both
the text label and JSON (`verified`, `trust`). Root trust fields stay
scoped to the root.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI max review requested due to automatic review settings October 23, 2026 08:45
@markuswondrak

Copy link
Copy Markdown
Contributor Author

AI-generated comment (GitHub Copilot CLI, Claude Sonnet 5.5), posted on behalf of @markuswondrak.

Addressed review 5625791400 (trust status of referenced bundles) in the latest commit on this branch.

  • LoadedBundle and ResolvedReference now carry the catalog entry's verified flag, including when a shorter route replaces a cached reference.
  • bundle info JSON references[] entries gain verified and trust. The text label shows community or verified per reference. The root's trust fields are unchanged.
  • Docs updated in docs/reference/bundles.md.
  • Tests: a verified root with a verified: false reference, checked in both text and JSON. Without the source change it fails. The existing reference-provenance test now also covers a verified reference. Test fakes for catalog entries gained verified.

.venv/bin/python -m pytest tests/specify_cli/bundles: 1154 passed.

Copilot AI max review requested due to automatic review settings October 23, 2026 12:39

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.

🔵 Needs a closer look

Recursive expansion changes catalog-policy enforcement and shared-component lifecycle behavior, warranting final maintainer validation.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Low severity Clarify that presets require an integer priority

docs/​reference/​bundles.md:156

An omitted preset priority is invalid: BundleManifest.structural_errors() rejects it, including in downloaded referenced manifests. Describing omission as a comparable setting suggests bundle authors can leave it out. State that presets must declare an integer priority; only extensions use the default value of 10.

Review effort: Max

@mnriem mnriem added the triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review label Oct 23, 2026
Only extensions may omit priority (default 10). Drop the sentence that
described an omitted preset priority as a comparable setting.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@markuswondrak

Copy link
Copy Markdown
Contributor Author

AI-generated comment (GitHub Copilot CLI, Claude Sonnet 5.5), posted on behalf of @markuswondrak.

Addressed the wording finding from review 5628500172 (preset priority) in the latest commit. Docs only, no behavior change.

docs/reference/bundles.md now says extensions may omit priority (an omitted value means the default, 10, and agrees with an explicit 10) and presets must declare an integer priority. The previous sentence described an omitted preset priority as a comparable setting, which suggested authors could leave it out. structural_errors() rejects that.

Copilot AI max review requested due to automatic review settings October 23, 2026 13:20

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.

🔵 Needs a closer look

Recursive expansion spans catalog protections, provenance, and shared-component lifecycle behavior that warrants final human validation.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Clarify component JSON overrides and source fields

docs/​reference/​bundles.md:58

Please clarify the component JSON fields here. overrides belongs to the winning entry, including a parent's own declaration that has no origin, as covered by test_info_renders_reference_provenance. Also, components[].source is included when set only for bundles with references; flat bundles still omit it. Document both conditions so consumers do not assume overrides requires origin or expect source on flat entries.

Review effort: Max

Document that component `source` appears only for bundles with
references, and that `overrides` does not require `origin`.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@markuswondrak

Copy link
Copy Markdown
Contributor Author

AI-generated comment (GitHub Copilot CLI, Claude Sonnet 5.5), posted on behalf of @markuswondrak.

Addressed the wording finding from review 5629015873 (component JSON fields) in the latest commit. Docs only, no behavior change.

docs/reference/bundles.md now states that, for bundles with references, a component entry includes source when the manifest declares one (flat bundles omit it), origin only when the component comes from a referenced bundle, and overrides whenever the entry replaced other declarations, including a parent's own declaration, which has no origin. It also lists the fields each overrides item carries. Checked against _manifest_component_view and _component_provenance in command_info.py.

Copilot AI max review requested due to automatic review settings October 23, 2026 13:58

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.

🔵 Needs a closer look

Recursive catalog resolution and shared-component lifecycle changes need final maintainer review and runtime validation.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Preserve declaring bundles in override provenance

src/​specify_cli/​bundles/​expansion.py:391

Merging agreeing entries here loses distinct declaring bundles before a later override can record them. For example, if root references a and b, both declare e@1.0.0, and root declares e@2.0.0, the final version is correct, but the override warning and bundle info override history name only a. The discarded declaration from b is missing. This differs from deduplicating two routes to the same shared declaration. Preserve each distinct declaring bundle until override provenance is built, keeping the shortest route per declaration, and add a regression case for agreeing siblings followed by a parent override.

Review effort: Max

Agreeing siblings were merged into one entry that kept a single origin,
so a later parent override named only one of them. Entries now carry a
record per distinct declaring bundle, merged by one shared rule
(_merge_declarations) that also governs override history, and one
function (_declare_own) builds an own-declaration entry and its notice.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@markuswondrak

Copy link
Copy Markdown
Contributor Author

AI-generated comment (GitHub Copilot CLI, Claude Sonnet 5.5), posted on behalf of @markuswondrak.

Addressed review 5629523384 (declaring bundles lost when agreeing siblings are merged) in the latest commit, by moving the rule into one place instead of patching another site.

  • Cause: "one declaration = one component + one declaring bundle, shortest route kept" was encoded only in _merge_overrides. The agreeing-copy merge in _merge_candidate kept a single origin, so a later parent override could only record a, not b.
  • Change: an _Entry now carries declarations, one record per distinct declaring bundle whose copy agrees. _merge_overrides became _merge_declarations and is the only merge, used for entry declarations and override history alike (order-independent tie-break). A new _declare_own builds the entry, the recorded overrides and the warning notice for a bundle's own declaration, and _OverrideNotice.replaced holds declarations. _prefix_* share one helper. Routes to the same declaring bundle still collapse, so the earlier exponential-growth fix holds.
  • Result: for root → a, b (both e@1.0.0) with root declaring e@2.0.0, the override history and the warning name both a and b.
  • Tests: agreeing siblings then parent override (both orders), order independence, nested routes (mid → a, b), and a negative case where two routes reach one declaring bundle and stay one record. The four tests for the dropped declarer fail without the change; the negative case guards the earlier fix and passes either way.

.venv/bin/python -m pytest tests/specify_cli/bundles: 1165 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.

🔵 Needs a closer look

Recursive catalog expansion affects compatibility and shared-component lifecycle guarantees across several commands and warrants final human validation.

1 open issue

Review effort: Max

Comment thread src/specify_cli/bundles/sources.py
Copilot AI max review requested due to automatic review settings October 23, 2026 14:55

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.

🔵 Needs a closer look

Recursive catalog resolution and shared-component lifecycle changes retain unresolved defects and need final human validation.

2 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Compare equivalent expanded requirements during reinstall checks

src/​specify_cli/​bundles/​expansion.py:375

[P2] Compatible reference reordering can incorrectly require refresh. If A provides e@1.0.0 with no priority and B provides the same extension with priority 10, [A, B] keeps priority=None, while [B, A] keeps priority=10. Expansion treats these settings as equivalent, but the exact ComponentRef comparison in installer.py:123–129 rejects reinstalling the same-version parent after the reorder and demands --refresh. Compare equivalent expanded requirements semantically at the reinstall guard, while still requiring refresh for real changes. Add an install → reorder references → reinstall regression.

AI disclosure: GitHub Copilot generated this finding for the requesting reviewer (identity, model, and settings unavailable; automated mode).

Review effort: Max

Comment thread src/specify_cli/bundles/expansion.py
@mnriem

mnriem commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback. If not applicable or undesired, please explain why

@mnriem mnriem added the author-awaiting Waiting on author response label Oct 23, 2026
…ases

Offline, a pin missing from the built-in snapshot may still resolve online,
so advise re-running without --offline instead of updating the reference.
The advice depends on the winning source. CatalogSource.is_builtin is now
the single definition of a built-in source, shared with the adapter dispatch.

Assisted-by: GitHub Copilot CLI (model: Claude Sonnet 5.5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI max review requested due to automatic review settings October 23, 2026 16:01
@markuswondrak

Copy link
Copy Markdown
Contributor Author

AI-generated comment (GitHub Copilot CLI, Claude Sonnet 5.5), posted on behalf of @markuswondrak.

Fixed in aa414a1 (review 5630087044).

  • Offline, a pin missing from the builtin:// snapshot now advises "the built-in catalog may be outdated, so the pin may resolve online. Re-run without --offline." instead of "Update the reference".
  • The advice depends on the source that won the lookup, matched by source object, not by id. A project catalog named default keeps the "update the reference" advice.
  • CatalogSource.is_builtin is now the single definition of a built-in source. The adapter dispatch and the loader both use it.
  • Tests: new offline built-in, non-built-in (offline and online) and winning-source cases, plus parametrized is_builtin cases. The new loader test and the is_builtin tests fail without the fix.
  • Two existing tests moved from offline=True to offline=False, since they assert the non-built-in advice.
  • A separate subagent review (gpt-6-astra) found no significant issues.

Bundle suite: 1175 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.

🔵 Needs a closer look

Recursive composition spans catalog resolution, installation lifecycle, and stored provenance, warranting final maintainer review.

1 open issue
2 previously missed issues

In code that hasn't changed since last review

Low severity Clarify bundle fetch failures fail online validation

docs/​reference/​bundles.md:294

The paragraph now covers both component and bundle references, but the warning rule applies only to component references. Online _validate() adds bundle-expansion failures to errors, including an unreachable bundle catalog or a failed child-manifest download, so validation exits 1. Qualify the warning rule and state that bundle-reference fetch failures fail online validation.

AI disclosure: GitHub Copilot generated this comment and suggested fix for the requester (identity and model unavailable; automated review mode).

Low severity Include bundle origin chains in inherited-component errors

src/​specify_cli/​bundles/​command_validate.py:104

Optional: When a referenced bundle contributes an unresolved component, validation reports the component label and checker error but not the bundle that introduced it. For example, the existing parent → child test reports child-extension without identifying child. This makes failures harder to trace, especially because local parents cannot be previewed with bundle info. Append the shortest chain from expanded.origins to inherited-component errors, while leaving errors for the parent's own declarations unchanged.

AI disclosure: GitHub Copilot generated this comment and suggested fix for the requester (identity and model unavailable; automated review mode).

1 resolved since last review

Review effort: Max

@mnriem

mnriem commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback, both open and previously missed. If not applicable, please explain why

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-awaiting Waiting on author response triage-nice-to-have Verdict: evidence-backed fix or greenlit feature — land after review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Allow bundles to reuse other bundles’ component declarations

3 participants