feat(bundles): first-party bugfix and assess bundles with bundled workflows - #4504
feat(bundles): first-party bugfix and assess bundles with bundled workflows#4504markuswondrak wants to merge 3 commits into
Conversation
… workflows Implements github#4495. Two first-party bundles pairing the bug and assess extensions with orchestrated, resumable pipelines: - workflows/bugfix: bug assess -> human review gate -> bug fix -> bug test - workflows/assess: intake -> research -> define -> shape -> decide -> verdict review gate (specify handoff stays manual) Both workflows are registered in workflows/catalog.json (bundled: true) and force-included in the wheel's core_pack like speckit. The bundles live in bundles/{bugfix,assess} and are listed in a new repo-shipped first-party catalog bundles/catalog.json (verified: true). The reserved builtin://default catalog source now resolves from that catalog — online from the repository, offline from the packaged wheel snapshot — so `specify bundle add bugfix|assess` resolves by id. Also adds `bundle add` as an alias for `bundle install`. Tests: workflow validation and structure guards, wheel force-include contract (mirrors test_wheel_bundled_presets.py), first-party catalog/manifest/pin consistency, and workflows/catalog.json <-> workflow.yml id/version/url consistency. Assisted-by: opencode (model: glm-5.3, autonomous)
There was a problem hiding this comment.
🟡 Changes recommended
Bundled workflow installation still accesses the network in offline local-bundle flows, and key workflow and alias contracts lack complete regression coverage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds first-party bugfix and assess bundles that compose existing extensions with new bundled workflows.
Changes:
- Adds, catalogs, and packages both workflows and bundle manifests.
- Wires online and offline first-party catalog discovery.
- Adds the
bundle addalias, documentation, and contract tests.
File summaries
| File | Description |
|---|---|
workflows/README.md |
Documents bundled workflow layout. |
workflows/catalog.json |
Registers bundled workflows. |
workflows/bugfix/workflow.yml |
Defines the bug-fix pipeline. |
workflows/assess/workflow.yml |
Defines the assessment pipeline. |
tests/workflows/test_bundled_bugfix_assess_workflows.py |
Tests workflow structure. |
tests/unit/test_bundler_adapters.py |
Tests first-party catalog loading. |
tests/integration/test_bundler_offline.py |
Tests offline catalog discovery. |
tests/contract/test_wheel_bundled_workflows.py |
Verifies wheel inclusion. |
tests/contract/test_firstparty_bundle_catalog_consistency.py |
Guards catalog and version consistency. |
tests/contract/test_bundle_cli.py |
Updates catalog-aware CLI tests. |
src/specify_cli/commands/bundle/__init__.py |
Adds the bundle add alias. |
src/specify_cli/bundler/services/adapters.py |
Resolves first-party catalog sources. |
pyproject.toml |
Packages catalogs and workflows. |
docs/reference/bundles.md |
Documents first-party bundles. |
bundles/catalog.json |
Defines the first-party bundle catalog. |
bundles/bugfix/README.md |
Documents the bugfix bundle. |
bundles/bugfix/bundle.yml |
Composes bug extension and workflow. |
bundles/assess/README.md |
Documents the assess bundle. |
bundles/assess/bundle.yml |
Composes assess extension and workflow. |
Review details
Suppressed comments (2)
tests/workflows/test_bundled_bugfix_assess_workflows.py:57
- This check accepts either primary input for both workflows, so
bugfixcould exposeideainstead ofreport(or vice versa) and still pass. Parameterize the expected primary input per workflow and verify that it is required.
@pytest.mark.parametrize("workflow_id", ["bugfix", "assess"])
def test_bundled_workflow_has_required_inputs(workflow_id: str) -> None:
definition = _load_workflow(workflow_id)
assert "report" in definition.inputs or "idea" in definition.inputs
tests/workflows/test_bundled_bugfix_assess_workflows.py:38
- As with the bugfix test, step IDs alone allow the assessment workflow to dispatch the wrong command or omit its slug/idea bindings without failing CI. Assert the command sequence and input arguments that define this pipeline.
step_ids = [step["id"] for step in definition.steps]
- Files reviewed: 19/19 changed files
- Comments generated: 3
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Install bundled workflow components from their packaged workflow.yml instead of falling through to the network-backed workflow catalog. Validate the bundled workflow ID and the bundle's pinned version before delegating to the local workflow install path. Add coverage for offline first-party bundle installs, bundle add alias execution, version pin rejection, and command/input workflow contracts. Assisted-by: opencode (model: glm-5.3, autonomous)
|
Posted on behalf of @markuswondrak by opencode (model: glm-5.3). Addressed the Copilot review in commit
Verification: |
There was a problem hiding this comment.
🟡 Changes recommended
The primary catalog-by-ID fresh-project flow lacks automated coverage, and several documentation and assertion-message issues remain.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
bundles/assess/README.md:38
- This sentence incorrectly implies both components are removed whenever no other bundle depends on them. An independently installed component is intentionally not attributed to this bundle and survives removal even with no other bundle dependency.
bundles/bugfix/README.md:36 - This sentence incorrectly implies both components are removed whenever no other bundle depends on them. An independently installed component is intentionally not attributed to this bundle and survives removal even with no other bundle dependency.
tests/contract/test_firstparty_bundle_catalog_consistency.py:108 - The second string is a separate no-op expression because the assertion message is not parenthesized, so failures end at “does not point at the” and omit the actionable expected location. Parenthesize the complete message.
- Files reviewed: 21/21 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Thanks @markuswondrak — the shape here matches what we agreed in #4461, so I'm happy with the direction. Before I do the full architecture review of the
Once those are in, re-request a Copilot review and I'll take the architectural pass and kick off CI. |
- Correct README wording for bundle remove behavior (FR-022). - Parenthesize full assertion message in first-party catalog consistency test. - Add contract coverage for `bundle add <id>` from an empty directory, mocking first-party catalog/manifest HTTP responses and verifying fresh-project init plus bundled extension + workflow installation. Assisted-by: opencode (model: kimi-k2.7-code, supervised)
|
Posted on behalf of @markuswondrak by opencode (model: kimi-k2.7-code). Addressed the remaining review findings in commit 9164b1c:
Verification: |
There was a problem hiding this comment.
🔵 Needs a closer look
The change spans catalog resolution, packaging, offline installation, and workflow execution, warranting final human validation despite broad test coverage.
Review details
- Files reviewed: 21/21 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Thanks @markuswondrak — verified all four are addressed, including the empty-dir by-ID test and the online/offline |
There was a problem hiding this comment.
🟡 Changes recommended
Both bundle READMEs show run commands that omit required inputs and therefore fail immediately.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 21/21 changed files
- Comments generated: 2
- Review effort level: Balanced
| ```bash | ||
| specify workflow run assess | ||
| ``` | ||
|
|
||
| You will be prompted for an idea and a slug. The slug is used as the working directory under `.specify/assessments/<slug>/` for all artifacts. |
| ```bash | ||
| specify workflow run bugfix | ||
| ``` | ||
|
|
||
| You will be prompted for a bug report and a slug. The slug is used as the working directory under `.specify/bugs/<slug>/` for all artifacts. |
Summary
Implements #4495.
Adds two first-party bundles, each pairing its extension with a bundled workflow, following the layering agreed in #4461 (workflows live in the workflow layer, bundles compose them — no new contribution types):
bugfix—bugextension +bugfixworkflow:speckit.bug.assess→ human review gate (reject aborts) →speckit.bug.fix→speckit.bug.test; inputs:report,slugassess—assessextension +assessworkflow: intake → research → define → shape → decide → verdict review gate (workflow stops there; the/speckit.specifyhandoff stays manual); inputs:idea,slugWhat's included
workflows/bugfix/workflow.ymlandworkflows/assess/workflow.yml, both registered inworkflows/catalog.json(bundled: true) and force-included in the wheel'score_packalongsidespeckit. Nothing is auto-installed at init.bundles/bugfix/andbundles/assess/manifests (pinned versions, roledeveloper) plus bundle READMEs.bundles/catalog.json— repo-shipped first-party catalog, both entriesverified: true.builtin://defaultcatalog wiring: resolves frombundles/catalog.jsononline and from the packaged wheel snapshot offline (mirrors the community catalog pattern). The previously empty stub is replaced by explicit URL/snapshot maps.specify bundle addas an alias forspecify bundle install.docs/reference/bundles.md(first-party bundles + catalog sources),workflows/README.md(layout).Acceptance criteria mapping
validate_workflowpasses for both workflow YAMLs; steps/gates/inputs match the schema —tests/workflows/test_bundled_bugfix_assess_workflows.pyworkflows/catalog.jsonentries match the bundled YAMLs' ids/versions/urls; contract test mirrorstest_wheel_bundled_presets.py—tests/contract/test_wheel_bundled_workflows.pyandtests/contract/test_firstparty_bundle_catalog_consistency.pyspecify bundle validate --path bundles/{bugfix,assess} --offlinepasses with zero unresolved referencesspecify bundle add bugfix|assessinstalls extension + workflow in a fresh project;workflow infoshows the expected step graph (verified end-to-end with a locally simulated post-merge catalog, since the raw.githubusercontent URLs only resolve after merge)specify bundle removeuninstalls only contributed components — an independently installed extension survives (FR-022, verified end-to-end)test_firstparty_manifest_pins_match_shipped_versions(drift-catching verified via mutation)test_workflows.py)Out of scope (tracked in #4495)
Fully offline
bundle install --offline— the bundle manifest still resolves via its catalogdownload_url(documented indocs/reference/bundles.md). Hosting of bundle artifacts resolves via the repository URLs, consistent with the community catalog.AI Disclosure
This PR was authored with AI assistance. The implementation, tests, and review verification were generated by opencode (model: glm-5.3) acting autonomously under direction from @markuswondrak, who specified the task, reviewed the findings, and approved submission. The commit carries the corresponding
Assisted-by:trailer.