Commit 730dcf5
feat(bundles): first-party bugfix and assess bundles with bundled workflows (#4504)
* feat(bundles): add first-party bugfix and assess bundles with bundled workflows
Implements #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)
* fix(bundles): install bundled workflows offline
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)
* fix(bundles): address review findings for first-party bundles
- 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)
* docs(bundles): show required workflow inputs in bundle README run commands
The run examples showed bare `specify workflow run bugfix|assess`, which
fails immediately because both workflows declare required inputs
(`report`/`idea` and `slug`). Show the repeatable --input flags with the
same example values as the corresponding extension READMEs, and note
that inputs omitted from the command line are prompted interactively.
Assisted-by: opencode (model: glm-5.2, autonomous)
* test(events): pin the PowerShell launcher in the ps-variant argv test
test_ps_variant_prefixed_with_powershell_launcher asserted that a real
pwsh/powershell launcher resolves on PATH, so it failed on machines with
neither installed (a pre-existing, environment-specific failure since
#4340 removed the fake "pwsh" fallback — not related to the bundles
work, but it blocked a clean local full-suite run).
The test's contract is argv construction (launcher, -File, script), not
launcher discovery, so monkeypatch shutil.which to a synthetic pwsh path
— the same pattern the no-launcher sibling test already uses — keeping
the test running (not skipped) on every platform.
Assisted-by: opencode (model: glm-5.2, autonomous)
* docs(bundles): clarify required workflow inputs
Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous)
* Remove unused import of Path
Remove unused import of Path from test_bundled_bugfix_assess_workflows.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(bundle): forward refresh flag in add alias
Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous)
* fix(bundler): fall back to packaged catalog snapshot
Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous)
* fix(bundler): only fall back to snapshot on transient catalog failures
The builtin catalog fallback previously caught every BundlerError from the
online fetch, silently masking malformed JSON, oversized responses, and
redirect/URL validation failures with the packaged snapshot. Classify only
transient failures (connection errors, timeouts, HTTP 5xx) as unavailable,
preserve validation failures, warn when a snapshot is used, and go quiet for
deliberate --offline use. Cover both builtin catalogs in unit and integration
tests, and assert the first-party catalog wheel mapping.
Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
* fix bundler catalog fallback handling
Preserve redirect-policy failures and treat HTTP 408/429 as transient
when falling back to the packaged catalog snapshot, per review on #4504.
Co-authored-by: markuswondrak <245696895+markuswondrak@users.noreply.github.com>
Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
* test(bundler): exercise real redirect handler and refresh fallback docstring
Address review nits on #4504: update the _CatalogUnavailable docstring to
mention 408/429 as transient, and drive the redirect-policy test through the
real _StripAuthOnRedirect handler instead of injecting the exception, with a
snapshot present to prove the fallback is not taken.
Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
* fix(bundler): keep TLS certificate failures out of snapshot fallback
Address review on #4504: urllib wraps ssl.SSLCertVerificationError in
URLError(reason=...) and some paths raise it directly as an OSError. Detect
both before the transient transport branches and raise a hard BundlerError so
a certificate-verification failure is never masked by the packaged snapshot.
Add regression cases for the wrapped and direct forms.
Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
* fix(bundler): treat truncated chunked responses as catalog-unavailable
Address review on #4504: http.client.IncompleteRead is neither URLError nor
OSError, so an interrupted chunked response fell through to the generic branch
and became a hard BundlerError, defeating the packaged-snapshot fallback.
Classify it as _CatalogUnavailable and add a regression test that raises it
from response.read().
Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
---------
Co-authored-by: Markus <markus@example.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: markuswondrak <245696895+markuswondrak@users.noreply.github.com>1 parent 4362335 commit 730dcf5
24 files changed
Lines changed: 1404 additions & 69 deletions
File tree
- bundles
- assess
- bugfix
- docs/reference
- src/specify_cli
- authentication
- bundler/services
- commands/bundle
- tests
- contract
- integrations
- integration
- unit
- workflows
- workflows
- assess
- bugfix
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
14 | 32 | | |
15 | 33 | | |
16 | 34 | | |
| |||
146 | 164 | | |
147 | 165 | | |
148 | 166 | | |
149 | | - | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
150 | 171 | | |
151 | 172 | | |
152 | 173 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
| 50 | + | |
| 51 | + | |
50 | 52 | | |
51 | 53 | | |
52 | 54 | | |
53 | | - | |
| 55 | + | |
| 56 | + | |
54 | 57 | | |
55 | 58 | | |
56 | 59 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
68 | 72 | | |
69 | 73 | | |
70 | | - | |
| 74 | + | |
71 | 75 | | |
72 | 76 | | |
73 | 77 | | |
| |||
100 | 104 | | |
101 | 105 | | |
102 | 106 | | |
103 | | - | |
| 107 | + | |
104 | 108 | | |
105 | 109 | | |
106 | 110 | | |
| |||
0 commit comments