diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f2ea3df..42201c4 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,7 +5,7 @@ }, "metadata": { "description": "Pure-Rust Clean Architecture workflow. Six commands (start, fix, plan, ship, review, install-ci) for axum + sqlx + Dioxus 0.7+ + tokio. Always-latest deps, CI audit gate, anti-slop enforced.", - "version": "4.2.0" + "version": "4.2.1" }, "plugins": [ { diff --git a/CHANGELOG.md b/CHANGELOG.md index 98b2739..d0ff123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to the code-et plugin will be documented in this file. +## [4.2.1] - 2026-05-12 + +### Fixed — TaskCreated hook rejects valid metadata when harness stringifies it + +`scripts/task-created-tag-check.sh` extracted `user_story` / `layer` with `jq -r '.tool_input.metadata.user_story'`, which only works when `metadata` arrives as a JSON object. The Claude Code harness sometimes delivers `tool_input.metadata` as a JSON-encoded **string** instead — `jq` returns empty, and well-formed TaskCreate calls were rejected with "required metadata is missing or invalid" even though the caller passed `user_story: "US-1", layer: "domain"` correctly. + +**Fix.** Normalize before extracting: if `metadata` is a string, parse it via `fromjson?`; otherwise pass through. Both shapes route through the same downstream validation, so `US-` / `AC-.` / `chore:` plus the Rust layer check work regardless of how the harness serialized the payload. Empty/invalid metadata still rejects. + +Verified against five payload shapes (object form, two string forms, empty, bogus tag). + ## [4.2.0] - 2026-05-10 ### Changed — per-task reviewer + review fix-pass moved to Opus 4.7 diff --git a/code-et-implementer/.claude-plugin/plugin.json b/code-et-implementer/.claude-plugin/plugin.json index e0a9ede..885e78c 100644 --- a/code-et-implementer/.claude-plugin/plugin.json +++ b/code-et-implementer/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "code", - "version": "4.2.0", + "version": "4.2.1", "description": "Pure-Rust Clean Architecture workflow. Six commands: start, fix, plan, ship, review, install-ci. Always-latest deps, CI audit gate, anti-slop enforced.", "author": { "name": "Kennet Kusk" diff --git a/code-et-implementer/scripts/task-created-tag-check.sh b/code-et-implementer/scripts/task-created-tag-check.sh index d984cc2..6403ecd 100755 --- a/code-et-implementer/scripts/task-created-tag-check.sh +++ b/code-et-implementer/scripts/task-created-tag-check.sh @@ -9,8 +9,10 @@ payload="$(cat)" prd="$("$here/resolve-prd.sh" 2>/dev/null || true)" -tag="$(printf '%s' "$payload" | jq -r '.tool_input.metadata.user_story // ""' 2>/dev/null || echo '')" -layer="$(printf '%s' "$payload" | jq -r '.tool_input.metadata.layer // ""' 2>/dev/null || echo '')" +# metadata may arrive as an object OR as a JSON-encoded string (harness quirk). +md="$(printf '%s' "$payload" | jq -c '(.tool_input.metadata // {}) | if type=="string" then (fromjson? // {}) else . end' 2>/dev/null || echo '{}')" +tag="$(printf '%s' "$md" | jq -r '.user_story // ""' 2>/dev/null || echo '')" +layer="$(printf '%s' "$md" | jq -r '.layer // ""' 2>/dev/null || echo '')" if [ -z "$prd" ]; then # Bug lane — anything goes