Releases: vulca-org/vulca-visual-control-sdk
Release list
v0.23.0
First public PyPI release after v0.19.0. This release bundles the v0.20-v0.22 redraw hardening work, visual workflow skills, remote MCP profiles, platform distribution docs, cultural-term benchmark tooling, and hosted provider SDK aliases.
Highlights
- Added
/visual-discovery,/visual-brainstorm,/visual-spec,/visual-plan, and/evaluateskill surfaces for agent-native visual workflows. - Added remote-safe MCP profile support via
vulca-mcp-remotefor hosted clients such as ChatGPT-style remote MCP integrations. - Added target-aware redraw hardening: source-context edit mattes, generated evidence gating, local quality gates, route/geometry advisory fields, and target mask refinement helpers.
- Added cultural-term efficacy benchmark tooling, including real-provider opt-in and signal-strengthening checks.
- Added provider SDK image aliases:
gemini-tools/nb2-toolsfor Google GenAI tool-backed image calls, andopenai-responsesfor the OpenAI Responses API image generation tool. - Added
vulca[providers]optional dependency group for hosted provider SDKs. - Refreshed README install pins from
0.17.11to0.23.0.
Compatibility
- Existing
vulca[mcp]installs continue to work. - Hosted provider SDKs remain optional; install
vulca[providers]only when using Gemini/OpenAI hosted backends directly. layers_redrawkeeps backward-compatible defaults while surfacing new route, model, quality, and advisory controls for advanced callers.
Links
- PyPI: https://pypi.org/project/vulca/0.23.0/
- Pull request: #36
v0.19.0 — detection diagnostics: silent drops + near-miss capture
Two diagnostic improvements to the orchestrated detection pipeline surfaced by a 7-entity dogfood run on IMG_6847.jpg. Detection behavior unchanged — only the reporting of misses is improved so callers can take targeted recovery actions instead of guessing.
Highlights
Person path — distinguish two failure modes that v0.18 lumped into no_detection_after_chain:
chain_returned_zero— chain found nothingrank_exceeded_chain_pool— chain found N < K persons; this entity is rank K-1+. Carriesentities_in_chain_poolandthis_entity_rankso caller knows exactly how many detections exist
Object path — detect_all_bboxes now returns {assigned, near_miss, nms_drops} (BREAKING for direct callers; migration recipe in CHANGELOG). Missed-branch reason refined:
dino_below_threshold(+near_miss_candidatesshowing actual conf range; user can lower threshold)dropped_by_within_label_nms(+nms_drop_candidates)dino_not_matched(true zero — preserved)
Motivation
Real dogfood on roadside photo IMG_6847.jpg surfaced 2 silent drops in 30 minutes:
red_carrank-exceeded silently asno_detection_after_chainwhile siblingyellow_trucksucceeded on identical chain — sameattemptsfield, opposite outcomewildflower_clusters(multi_instance, threshold=0.18) reporteddino_not_matchedwith no way to tell if DINO had below-threshold candidates
Both fixes give users actionable diagnostic signal so they can refine plan.json without reading the source.
Tests
10 new regression tests + existing 22/22 pass + real-DINO+SAM lanterns fixture pass.
Cross-validated by fresh codex GPT-5.4 + superpowers Claude parallel review — both flagged same P1 (id() set comparison fragility against future _nms_bboxes refactor → fixed value-based) and same nit (CHANGELOG test count → fixed).
Out of scope (v0.20 backlog)
- Per-entity
entity.thresholdactually honored at DINO call (4th hidden bug) - Tiled/upscaled paths surfacing
diagnostics_unavailable_tiled_pathflag - Joint-pass threshold side-effect documented in CHANGELOG
- Test boundary tightening (
== 5not<= 5) NEAR_MISS_FLOORconstant migration tosrc/vulca/_segment.py
Migration
grep -rn 'detect_all_bboxes(' --include='*.py' .
# replace `result[label]` → `result["assigned"][label]`
# new diagnostic keys are optional: result["near_miss"], result["nms_drops"]detect_bbox (legacy single-label wrapper) preserves return shape (bbox, score) — no migration needed.
🤖 Authored by Claude Opus 4.7 + Codex GPT-5.4 + Superpowers Claude (parallel review)
v0.18.0 — layers_redraw default flip + layers_split multi_instance
v0.18.0 (2026-04-26)
Two paired changes that close the largest hand-cuts surfaced by the γ Scottish carousel (2026-04-25): layers_redraw defaults flipped to the safe path (no more silent in-place overwrite + no more alpha-sparse hallucination by default), and layers_split orchestrated mode grew first-class multi-instance support so a "row of 6 lanterns" plan no longer collapses into one fragmented union mask.
The migration is two-tier for callers who relied on the legacy layers_redraw behavior: most users only need to add in_place=True to keep their file-overwriting workflow; the small minority who depended on the legacy transparent background passthrough plus alpha drop must add all three kwargs (in_place=True, background_strategy="transparent", preserve_alpha=False) for byte-identical v0.17.x parity. Everything else is additive.
Breaking changes
layers_redraw defaults flipped — three parameters now choose the safe path by default:
| Parameter | v0.17.x default | v0.18.0 default |
|---|---|---|
| Output path | in-place overwrite | <layer>_redrawn.png (input untouched) |
background_strategy |
"transparent" |
"cream" |
preserve_alpha |
False |
True |
A new parameter in_place: bool = False is the explicit opt-out for callers who need the legacy in-place write. Most users only need this single kwarg — pass in_place=True to keep your v0.17.x file-overwriting workflow while inheriting the new safer background_strategy="cream" and preserve_alpha=True defaults (which improve, not regress, output quality on alpha-sparse layers).
To restore v0.17.x behavior byte-for-byte (rare; required only if you depended on the legacy transparent background passthrough plus alpha drop):
layers_redraw(
artwork_dir, layer="lanterns", instruction="...",
in_place=True,
background_strategy="transparent",
preserve_alpha=False,
)Why: v0.17.14 introduced the opt-in defenses (background_strategy="cream", preserve_alpha=True, output_layer_name) but left legacy as the default — making the trap (silent input destruction + scene hallucination on alpha-sparse layers) the path of least resistance. The γ Scottish Part 2 showcase confirmed the trap is easy to fall into even when the implementer knew about it. v0.18 makes the safe path the default; the unsafe path requires explicit opt-out. See commit 7679b488 (kwarg + 3-way path resolution) and 9631adf2 (atomic default flip).
Note:
layers_redraw(merge=True, layers="a,b,...")does not yet honor thein_placekwarg — only the single-layer path. The merge path always writes the merged result to a new file. Tracked for v0.18.1+.
Added
layers_split orchestrated mode now supports multi-instance entities. Plan JSON entities accept an optional multi_instance: true flag. When set, Grounding DINO returns up to 8 bboxes for that label (instead of top-1) and SAM segments each independently. The orchestrator emits N flat sibling layers named <label>_0..N-1 sorted by DINO det_score descending (the ordering is fixed at NMS time in _nms_bboxes before SAM runs, so SAM scores do not influence sibling order).
{
"entities": [
{ "name": "lanterns", "label": "red paper lantern", "multi_instance": true }
]
}(The schema key is entities on the Plan model; some prior design docs used object_entities informally — the actual Plan.from_file() JSON path is entities.)
Edge cases:
- DINO returns 0 bboxes → 0 layers emitted; manifest carries
quality_flags: ["multi_instance_no_detection"]. - DINO returns exactly 1 bbox → 1 layer named
<label>(no_0suffix); manifest carriesquality_flags: ["multi_instance_degraded"]. The keystone naming contract:_0suffix means "instance 0 of N≥2"; never "lone instance". - DINO returns >8 bboxes → top-8 by score retained, rest dropped.
z_indexof subsequent entities in the plan is auto-pushed by(N-1)so the multi-instance fan-out doesn't collide downstream.
Closes the multi-instance gap deferred from v0.17.13. The plan JSON schema extension is additive; existing plans without multi_instance are byte-for-byte unaffected. See commits c82880dd (detection-layer multi_instance), ae6471fe (orchestrator wire + I-1 fail-fast), bdc9d32e (entity-loop refactor).
Limitation: tiled / upscaled detection paths
layers_split orchestrated mode raises NotImplementedError early when multi_instance: true is requested and the input image triggers DINO's tiled or upscaled path (extreme aspect ratio, or dimensions outside the standard window). The error message is actionable: it tells the user to either disable multi_instance for that image or pre-crop to a non-extreme aspect ratio. Forwarding multi_instance kwargs through the tiled/upscaled wrappers is v0.18.1+ scope.
Internal
- New
src/vulca/_segment.pymodule hosts pure-Python NMS helpers (_iou,_nms_bboxes) extracted fromscripts/claude_orchestrated_pipeline.pyso they can be unit-tested withoutimport torch(mirrors the v0.17.14 hotfix discipline for_quality_gate). - New fixture
tests/fixtures/multi_instance/lanterns_6.jpg(440×700, ≈86 KB) — γ Scottish lanterns crop, anchored as the multi-instance regression baseline. - New
l4_localpytest marker for local-only model-weight tests; CI skips automatically viapytest.importorskip("torch"). The pre-existingreal_providerandlocal_providermarkers are also now registered inpyproject.toml, eliminatingPytestUnknownMarkWarningnoise on every run.
v0.17.15 — maintenance: AST test rewrite + Node 24 actions bump
v0.17.15 (2026-04-26)
Maintenance release. Pure internal hardening — no API changes, no user-visible behavior changes. Two items closed from the v0.17.14-CI-hotfix backlog plus one GitHub deprecation tracker.
Changed
tests/test_quality_gate.py: migratedtest_person_path_invokes_compute_quality_flagsfrom string-grep (Path.read_text() + str.index()) toast.parse()walk. Same invariant (the person loop inprocess()must contain aCalltocompute_quality_flags), but structurally rigorous — comments and string literals containing the function name no longer satisfy the assertion. Closes the codex P2 from the 2026-04-26 v0.17.14-CI-hotfix review..github/workflows/ci.yml: bumpactions/checkout@v4 → @v5andactions/setup-python@v5 → @v6. GitHub will force Node.js 24 default on Actions runners 2026-06-02 and remove Node 20 entirely 2026-09-16; v4/v5 are Node-20-pinned. v5/v6 are the first majors that support Node 24.
Notes
compute_quality_flagswas extracted tovulca._quality_gatein the post-tag CI hotfix during v0.17.14 (commitb5088caa, 2026-04-26) — see retro entry below.
v0.17.14 — native mask-inpaint + layers_redraw + paste_back
Surgical 5-patch release closing the P0/P1 gaps surfaced by the parallel codex + superpowers review of the γ Scottish carousel workflow on 2026-04-25. The carousel's slide-4 mask-edit flow is now fully native — no out-of-band Python scaffolding required.
Patches
- P1
inpaint_artwork(mask_path=...)— native precision-mask path via OpenAI/v1/images/edits. Pass an RGBA PNG where alpha=0 marks pixels to edit and alpha=255 marks pixels to preserve. Gemini/ComfyUI fail loud (NotImplementedError with hint). Legacyregion=path unchanged. - P2
layers_redrawrecontract — opt-in viaoutput_layer_name=for non-destructive output;background_strategy=cream|white|sample_median|transparentto flatten alpha-sparse layers before redraw and stop hallucination of new content into empty regions;preserve_alpha=Trueto re-apply source alpha. Provider-aware api_key wiring closes the GOOGLE_API_KEY leak that was forcing the wrong env var into every provider. - P3
layers_paste_back— new MCP glue verb for the open-loop "edit one layer, paste back into source" flow that previously required manualImage.composite()in Python. Three blend modes (alpha / feathered / hard), pure PIL. - P4 Person-path quality gate — mirror v0.17.13
compute_quality_flagsinto the orchestrated pipeline's person loop. Same silent-success bug closed for symmetry. - P5
layers_compositenon-destructive — gate the legacyensure_alphawriteback behindforce_alpha_writeback=True. A preview call no longer mutates source layer files.
Review fixes (post-patch)
5 P1 + 5 P2 fixes from the double-review pass — z_index round-trip parity, bbox-from-mask (no more hardcoded {0,0,100,100}), paste_back RGB clamp against alpha=0 black-bleed, provider default rollback for legacy region path (silent billing flip averted), sample_median warning, redraw_merged aspect symmetry, HARD_THRESHOLD module constant, mask convention docstring simplification.
Stats
7 commits, 14 files changed. 35 new tests + 5 review-driven regression tests. 82/82 passing across the v0.17.14 surface.
v0.17.13 — transparency gate symmetry for orchestrated DINO-object path
Transparency fix surfaced from the same γ Scottish dogfood session as v0.17.12. Parallel superpowers:code-reviewer + Codex review of the orchestrated decompose pipeline found that the DINO-object path was missing the SAM-quality gate that the hint-entity path had. Result: low-confidence detections (sam_score < 0.70, bbox_fill < 0.30) were silently marked status: \"detected\" and overall success_rate: 1.0 — leaving calling agents with no signal to inspect the bad mask.
Real-world impact: the lanterns entity in our dogfood plan returned a mask of building structure (sam_score 0.609, bbox_fill 0.256) but reported success.
Fixed
scripts/claude_orchestrated_pipeline.py: extract DINO-object quality gate intocompute_quality_flags()helper; mirror the hint-path semantics (empty_mask,low_sam_score,low_bbox_fill,mask_outside_bbox) onto the DINO branch. Low-confidence object detections now correctly downgrade tostatus: \"suspect\"withquality_flags: [...], and the overall manifest status cascades to\"partial\"when any object is suspect.
Added
tests/test_quality_gate.py: 8 regression tests pinning the gate's threshold calibration (sam_score 0.70/bbox_fill 0.30/inside_ratio 0.60/pct 0.05) against the γ Scottish 9-entity baseline. 8 clean entities pass at sam ≥ 0.93 / fill ≥ 0.55; lanterns (sam 0.609 / fill 0.256) flips to suspect. Future refactors must update this test explicitly to change the gate.
Out of scope (deferred to v0.18)
The underlying multi-instance segmentation gap (single-bbox-per-label structural limit in detect_all_bboxes) is not fixed here — that's v0.18 scope. v0.17.13 only fixes the transparency bug so the gap is now visible to callers instead of silent.
Other v0.18 items already triaged from the same review:
inpaint_artwork(mask_path=...)mask overload — currently the tool only accepts a region-string and runs VLM bbox-detect → rectangular crop → feathered paste; cannot consume a precise mask.layers_redrawalpha-aware preprocessing — currently the prompt explicitly asks the model to fill the entire canvas, which causes hallucinated content for alpha-sparse layer inputs.layers_paste_backglue verb for "edit one layer of a foreign image, paste back preserving everything else."- Person-path quality gate (also missing — codex finding).
Install
```bash
pip install vulca==0.17.13
```
For the orchestrated decompose pipeline:
```bash
pip install "vulca[sam,mcp,layers]==0.17.13"
```
Provenance
Surfaced and reviewed in the same dogfood session as v0.17.12, by parallel superpowers:code-reviewer + Codex audit. Both reviewers independently identified the asymmetric gate as a transparency bug separate from the multi-instance architectural gap. The fix is ~30 LOC of code + 8 LOC of regression tests.
v0.17.12 — bugfix rollup + parallel-review hardening
Bugfix rollup release from the 2026-04-24 γ Scottish showcase Part 1 session (dogfood of Vulca's brainstorm → spec → plan triad on a real Scottish street → Song-dynasty gongbi additive overlay), validated against parallel superpowers:code-reviewer + Codex review of PR #17.
Fixed
- openai_provider: gate
input_fidelity/quality/output_formatby per-model capability (#12) —gpt-image-2GA rejectedinput_fidelityunconditionally before this fix. - openai_provider: capture actual
cost_usdfromresponse.usage(#12); log at INFO when a model has no pricing entry instead of silently returning None. - openai_provider: normalize
qualityvocabulary across model families — DALL-E-3 receives `"standard"/"hd"` while gpt-image-* keeps `"high"/"auto"/"medium"/"low"` (post-review hardening). - prompting: `compose_prompt_from_design` now handles `tradition: null` (a valid resolved-design state) and reads frozen `C.tradition_tokens` from the artifact when present rather than re-deriving from the live registry (post-review hardening — the artifact is the source of truth for a resolved spec).
- mcp_server: `compose_prompt_from_design` MCP wrapper raises an actionable error when given a relative path that does not resolve against the server CWD (post-review).
- [sam] extra: pulls `segment-anything` + `timm>=1.0` so the orchestrated decompose pipeline imports cleanly; see `docs/INSTALL-RECIPE.md`. Critical: do NOT install `[sam,sam3]` together — `transformers==4.49` vs `>=4.50` hard pin conflict; pick one extra per environment until v0.18.
Added
- MCP tool `compose_prompt_from_design` (#13) — expose Vulca's structured prompt-composition value standalone for non-MCP consumers. Also re-exported as `from vulca import compose_prompt_from_design` for SDK usage.
- `evaluate_artwork` `mode="rubric_only"` (#14) — agent-native scoring without VLM backend dependency (consumer agents already have vision).
- `evaluate_artwork` `vlm_model` runtime kwarg (#15) — bypass `VULCA_VLM_MODEL` env + MCP restart for runtime provider switching.
Provenance
- 4 issue fixes implemented by Codex (task-modjrdmx-br2641, 9m 34s background dispatch).
- Foundation commit `6e5c38bc` (segment-anything + timm dep gap) closed orchestrated-pipeline import gap surfaced in showcase Part 1 sanity-check.
- 2 post-review fix commits added regression tests (`design_null_tradition.md` + `design_with_artifact_tokens.md`) so the P1 paths can not silently re-regress.
A/B evidence
The session captured the data point that motivates #13: same `gpt-image-2` API, same source photo, same target intent — Vulca-mediated structured prompt produces additive gongbi overlay (photo preservation honored); bare-curl with naive prompt produces unified-wash filter. That is the buyable differentiator.
Install
```bash
pip install vulca==0.17.12
```
For the full orchestrated decompose pipeline:
```bash
pip install "vulca[sam,mcp,layers]==0.17.12"
```
(See `docs/INSTALL-RECIPE.md` for SAM checkpoint + MCP-Python setup.)
v0.17.11 — honesty + quality-of-life patch
Summary
Dogfooding-driven honesty + quality-of-life release. 4 commits addressing real bugs + UX gaps surfaced by today's (2026-04-23) Scottish-Chinese fusion showcase + parallel codex + superpowers:code-reviewer product audit. User asked "我们的产品好不好?使用起来有bug吗?" — here's the answer.
What's in
1. Style-Treatment 7th question-bank dimension (commit `69dbd5d1`)
- `/visual-brainstorm` now asks a mandatory 7th dimension: how the tradition is applied — `additive` (photo preserved, elements painted on top) / `unified` (whole-image transformation) / `collage` (visible cut-outs) / `wash` (global filter).
- Closes the UX gap where gpt-image-1.5 smoke test produced a whole-image painterly overlay when user wanted additive treatment — the 6-dim question bank simply didn't ask about style application mode.
- Propagated to `/visual-spec` C.prompt derivation + S4 invariant. `proposal.md` frontmatter now 8 fields (`style_treatment` added).
2. Provider error messages normalized (commit `437f4dc0`)
- Gemini — classifies empty `response.candidates` into quota-exhaustion vs content-policy vs generic via `prompt_feedback.block_reason`. Points to aistudio.google.com for free-tier quota hits.
- OpenAI — user-facing messages for 402 billing + 400 content-policy + 429 rate-limit on top of existing 403 Org-verify. Retryability preserved via (fragile) string-match — hardening in v0.17.12.
- ComfyUI — execution-error extractor surfaces first meaningful message from `status.messages`; invalid-image hint explains likely checkpoint/VAE failure.
3. MCP surface cleanup (commit `5bf4b1ca`)
- BREAKING: `create_artwork.ref_type` parameter removed — dead code that silently dropped user input. SDK-level `ref_type` unchanged in this release (scoped v0.18+).
- `layers_split.plan` now documented in Args block; `layers_edit` visible/locked split into 2 lines.
- Hardcoded `/Users/yhryzy/` paths in shipped docs replaced with `` placeholders.
4. README + BP honesty (commit `856ce54c`)
- README: "Try it in 60 seconds" → "Quick start" with realistic time breakdown (~1 min if Claude Code + `uv` ready, ~5-10 min from scratch).
- BP §5.4 moat claim softened: "24 个月以上难以复制" → "12 个月以上需专门投入" + explicit "3 cultural consultants for a quarter" reality check.
- `.gitignore` adds `/gen_.png`, `/gen_.jpg`, `tmp-shipgate-/` (75 gen_.png removed from repo root).
Review consensus
Both reviewers say ship-with-caveats, no hard blockers.
- Code: B+ → A-
- Skill design: B → A-
- UX: C+ → B
- Docs: C → B
Known follow-ups (v0.17.12)
- `/visual-plan` Phase 3 reads `C.style_treatment` (currently declared in design.md but inert in execution; `/visual-spec` governance puts the directive in `base_prompt` so effect exists, just undocumented)
- `openai_provider._is_retryable` robustness (replace string-match with exception subclass chain)
- SDK-level `create_artwork.ref_type` cleanup (mirror MCP surface removal)
Tests
1920 passed / 12 pre-existing baseline failures / zero regressions (same baseline as v0.17.10).
Install
```bash
pip install vulca[mcp]==0.17.11
```
Or via uvx in Claude Code MCP config:
```json
{"mcpServers": {"vulca": {"command": "uvx", "args": ["--from", "vulca[mcp]==0.17.11", "vulca-mcp"]}}}
```
v0.17.10 — Superpowers skill alignment + gpt-image-2 bundle
Summary
Two dogfooding-driven changes bundled in one release.
1. OpenAI gpt-image-2 support (v0.17.9, never shipped separately)
- `OpenAIImageProvider` now accepts `model="gpt-image-2"` (released by OpenAI 2026-04-21).
- New Optional kwargs plumbed through MCP `generate_image` + provider + Protocol:
- `input_fidelity: "high" | "low"` — preserves reference composition (edits endpoint only)
- `quality: "low" | "medium" | "high" | "auto"`
- `output_format: "png" | "webp" | "jpeg"`
- Size set extended with portrait/landscape: `(1024, 1536)`, `(1536, 1024)`.
- Friendly error detection on OpenAI 403 with "organization must be verified" message.
2. Skill alignment with Superpowers design patterns (v0.17.10)
- 3 skill descriptions rewritten from descriptive `Triggers: /slash` to imperative `Use when X / You MUST use this before X` — mirrors `superpowers:brainstorming` pattern.
- New meta-skill `.claude/skills/using-vulca-skills/SKILL.md` (~50 lines) — intent-routing for the triad, normalized finalize vocabulary, red-flag checklist.
- SessionStart hook (`hooks/hooks.json` + `.claude-plugin/plugin.json`) preloads the meta-skill automatically at session start so agents match user intent without requiring slash commands.
- Finalize vocabulary normalized: Brainstorm/Spec use `{finalize, done, ready, lock it, approve}`; Plan uses `accept all` exact-match.
- Cap-hit prompt fix in Plan: `"Turn cap reached. 'accept all' or 'deep review'?"` (was incorrectly `"finalize or deep review?"`).
Why 0.17.8 → 0.17.10 (skipping 0.17.9)
v0.17.9 commit (`8d1a84f8`) was prepared locally as the gpt-image-2 patch but never shipped to PyPI. v0.17.10 bundles both. Users upgrading from v0.17.8 get both in one jump.
Both changes came from real user dogfooding
2026-04-23 attempted to run `/visual-plan` on a real Scottish street photo for a Xiaohongshu showcase. Two blockers surfaced:
- openai_provider hardcoded to gpt-image-1 → v0.17.9 fix
- Skills required slash commands (broke the "AI agent auto-invokes" UX promise) → v0.17.10 fix
Saved as memory `feedback_dogfood_showcase_through_triad.md` — user-facing showcases MUST run through the triad, never one-off scripts.
Test gate
- 81/81 pass on visual-plan + visual-spec + MCP-ext tests
- Full repo: 1920 passed (+3 from v0.17.8 baseline) / 12 pre-existing baseline failures / zero regressions
Deferred to v0.18
Protocol body compression (200-430 lines → 60-170 lines matching Superpowers) flagged by code-reviewer as "long bodies dilute their own descriptions". Not blocking auto-invoke fix; monitor only.
Install
```bash
pip install vulca[mcp]==0.17.10
```
Or via uvx in Claude Code MCP config:
```json
{"mcpServers": {"vulca": {"command": "uvx", "args": ["--from", "vulca[mcp]==0.17.10", "vulca-mcp"]}}}
```
v0.17.8 — MCP metadata forward fix + 12-item clarity patch
Summary
Bug fix + clarity-gap backlog closure release. Two coupled changes:
1. MCP generate_image metadata forwarding (BUG FIX)
v0.17.6 landed mock-provider echo of `seed/steps/cfg_scale/negative_prompt` into `ImageResult.metadata`, but the MCP wrapper at `src/vulca/mcp_server.py:1216-1221` only extracted `cost_usd` and dropped everything else. Meaning: agents calling MCP could never actually observe the 4 new kwargs round-trip — the v0.17.6 feature was half-shipped.
Surfaced by /visual-plan Layer C v2 live ship-gate on 2026-04-23. Fix is a 1-line addition: `"metadata": dict(result.metadata) if result.metadata else {}` in the return dict. Backward-compat: additive; existing 4-key callers unaffected.
2. /visual-plan SKILL.md clarity patch (12 items)
All 13 clarity-gap candidates from v0.17.7 Layer C v2 (8 items) + v0.17.5 Layer B simulated (5 items) folded in. One item absorbed into `_version.py` fix. Notable:
- §Handoff 8 → 9 variants: Err #16 content-guard abort split from user-interrupt for downstream grep.
- `evaluate_artwork` dimensions shape pinned: mock flat-float vs live nested-dict contract with unwrap recipe.
- MCP metadata agent-hint: Phase 3 pseudocode now notes mock provider echoes MCP kwargs for round-trip verification.
- Phase 1 tightenings: slug path convention (reject `/` and absolute), traceback guard on Err #1, FIRST-violation precedence on Err #4 (tradition wins over domain), Err #3 + fresh-lockfile rule, stale-lock K=0 semantic.
- Phase 2 tightenings: Err #8 Write suppression bold, "compact form" definition (`[unchanged sections: …]` 1-liner), redundant-Write symmetry note.
- Phase 4: overage_pct negative formatting (`"under budget (-%)"` vs raw `-0.9988`).
- Iter `` semantic paragraph: aborted variants 7-9 use K = iter_idx that WOULD have run.
3. Stale _version.py drift fix
`src/vulca/_version.py` had been stuck at `"0.15.1"` since v0.15.1; `pyproject.toml` was authoritative. `python -c "import vulca; print(vulca.version)"` now correctly reports `0.17.8`.
Test coverage delta
- New: `test_mcp_wrapper_passes_metadata_through` + `test_mcp_wrapper_metadata_backward_compat_empty_dict` — covering the wrapper-level contract that direct-provider tests missed.
- 1917 passed full-repo (+2 from v0.17.7 baseline) + 12 pre-existing baseline failures (unchanged).
Backlog status
Clarity-gap backlog from v0.17.5 + v0.17.7 ship-gates: CLOSED. All 13 items folded.
Install
```bash
pip install vulca[mcp]==0.17.8
```
Or via uvx in Claude Code MCP config:
```json
{"mcpServers": {"vulca": {"command": "uvx", "args": ["--from", "vulca[mcp]==0.17.8", "vulca-mcp"]}}}
```