From b335512c351726702c3ea0c02fabac33ed7f9d2b Mon Sep 17 00:00:00 2001 From: Kennet Dahl Kusk Date: Tue, 12 May 2026 14:11:53 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20v4.2.1=20=E2=80=94=20TaskCreated=20hook?= =?UTF-8?q?=20tolerates=20stringified=20metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Claude Code harness sometimes delivers tool_input.metadata as a JSON-encoded string rather than an object. The original jq path .tool_input.metadata.user_story silently returned empty in that case, rejecting valid TaskCreate calls with "required metadata is missing or invalid". Normalize first: if metadata is a string, parse it via fromjson?; otherwise pass through. Same downstream validation either way. Verified across five payload shapes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude-plugin/marketplace.json | 2 +- CHANGELOG.md | 10 ++++++++++ code-et-implementer/.claude-plugin/plugin.json | 2 +- code-et-implementer/scripts/task-created-tag-check.sh | 6 ++++-- 4 files changed, 16 insertions(+), 4 deletions(-) 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