Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).

## [0.77.1] - 2026-08-31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the next patch version in the changelog

When this commit is applied to its recorded parent (554e455), the latest release is 0.76.0 and neither 0.76.1/0.76.2 nor 0.77.0 exists, so publishing this heading would skip directly to 0.77.1 and associate this patch fix with a nonexistent release lineage. Change it to 0.76.1, or rebase the commit only after the prerequisite releases have actually landed.

AGENTS.md reference: AGENTS.md:L221-L224

Useful? React with 👍 / 👎.


### Fixed
- **binding を持たない manifest で SessionStart の通知が無音になる問題を修正(#166)**: `check-pending.sh` の `_load_manifest_binding` は、runtime binding の有無を `jq -cse` の結果で判定していた。「binding なし」を表すセンチネルは `null` だが、`jq -e` は最後の出力が `null` のとき終了コード 1 を返す。このため binding を持たない manifest は常に「不正な manifest」と分類され、`_MANIFEST_BINDING_STATE` が `legacy` ではなく `invalid` になっていた。下流の `_resolve_kit_repo` が失敗して `KIT_REPO` が解決されず、カタログを読めないまま hook が exit 0 で終了するため、**新機能・新プラグインの通知が何の表示もなく抑止されていた**
- 対象は `~/.claude/.starter-kit-manifest.json` が存在し、かつ `kit_repo` / `config_file` を持たないインストール。`write_manifest` は MDM 環境で `policy_sha256` 形式を出力するため MDM 管理下の環境が該当し、binding 導入前の古い manifest も同様。`~/.claude-starter-kit.conf` に有効な `KIT_REPO` があっても、既定のチェックアウトが存在しても使われなかった
- `-e` を外して修正した。`null` は正常なセンチネルであって失敗ではない。部分的な binding と壊れた manifest は引き続き `error()` 経由で非ゼロ終了するため、**fail-closed の挙動は変わらない**。同じフィルタを持つ `wizard/runtime-binding.sh` は元から `-e` なしで、そちらが意図した形だった
- 回帰テストを 3 件追加した(MDM 形式 manifest / 旧 v1 manifest で通知が出ること、壊れた manifest では引き続き出ないこと)。既存のテストは manifest を作らない経路と完全な binding・部分的な binding しか通っておらず、「manifest はあるが binding が無い」ケースが未検査だった

## [0.77.0] - 2026-08-31

### Fixed
Expand Down
8 changes: 7 additions & 1 deletion features/feature-recommendation/scripts/check-pending.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ _load_manifest_binding() {
return 0
fi

binding="$(jq -cse '
# No -e: "no runtime binding" is reported as a null result, and `jq -e` treats
# a trailing null as failure. With -e the legacy branch below was unreachable,
# so every manifest without a binding — MDM's policy_sha256 form and every
# pre-binding manifest — was marked invalid and silenced the notification.
# A malformed or partial binding still exits non-zero through error(), so the
# fail-closed path is unchanged. Matches wizard/runtime-binding.sh.
binding="$(jq -cs '
if length == 1 and (.[0] | type == "object") then
.[0]
| if (has("kit_repo") or has("config_file")) then
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/test-check-pending.sh
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,42 @@ else
fail "check-pending: partial manifest binding fell through (got '$_cp_out')"
fi

# A manifest with no runtime binding at all is not an error: MDM writes the
# policy_sha256 form and every pre-binding install predates the pair. Those
# keep the legacy config/default-checkout lookup. The filter reports "no
# binding" as null, and `jq -e` reads a trailing null as failure — which marked
# them invalid and silenced the notification entirely.
_cp_setup '{"version":1,"plugins":["claude-security"]}'
printf '%s' '{"version":2,"mdm_managed":true,"policy_sha256":"abc"}' \
> "$_cp_home/.claude/.starter-kit-manifest.json"
_cp_out="$(_cp_run)"
if [[ "$_cp_out" == *"claude-security"* ]]; then
pass "check-pending: an MDM manifest without a binding keeps the legacy lookup"
else
fail "check-pending: a bindingless MDM manifest must not silence the notice (got '$_cp_out')"
fi

_cp_setup '{"version":1,"plugins":["claude-security"]}'
printf '%s' '{"version":1,"files":[]}' \
> "$_cp_home/.claude/.starter-kit-manifest.json"
_cp_out="$(_cp_run)"
if [[ "$_cp_out" == *"claude-security"* ]]; then
pass "check-pending: a pre-binding manifest keeps the legacy lookup"
else
fail "check-pending: an older manifest must not silence the notice (got '$_cp_out')"
fi

# The fail-closed direction must survive the same change: a manifest that is
# not a single JSON object is rejected, not treated as "no binding".
_cp_setup '{"version":1,"plugins":["claude-security"]}'
printf '%s' 'not json' > "$_cp_home/.claude/.starter-kit-manifest.json"
_cp_out="$(_cp_run)"
if [[ -z "$_cp_out" ]]; then
pass "check-pending: an unparseable manifest still fails closed"
else
fail "check-pending: malformed manifest must not fall through (got '$_cp_out')"
fi

# /update-kit is advertised only when the command was actually deployed from
# this checkout. INSTALL_COMMANDS=false leaves it absent, so the hook points to
# the setup.sh update path instead of promising a nonexistent slash command.
Expand Down
Loading