diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 3d11996..d25cdc0 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -111,7 +111,7 @@ jobs: # Guards the one part of this repo that ships to users without a build step. # See scripts/check_plugin.py for what it checks and, as importantly, what it - # can't check yet (shellcheck and jq are absent from ci-python). + # can't check yet (shellcheck is absent from ci-python). plugin: name: Plugin hooks if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') @@ -133,12 +133,15 @@ jobs: # the only consumer today. Promotion into ci-python is filed as an issue # on CI-runner rather than assumed here. # - # jq is not optional for the smoke test: every hook exits at line 1 - # without it, so the check would pass while exercising nothing. + # jq WAS installed here too, because every hook exited at line 1 without + # it and the smoke test would have passed while exercising nothing. The + # hooks need no jq since #4107 — they use only what POSIX guarantees — so + # the smoke test now runs on a bare image, which is the condition it is + # actually meant to be asserting. - name: Install shell tooling run: | apt-get update -qq - apt-get install -y -qq --no-install-recommends jq shellcheck + apt-get install -y -qq --no-install-recommends shellcheck # On main the comparison would be against itself, so only the syntax and # pattern checks mean anything there. @@ -223,16 +226,12 @@ jobs: UV_PROJECT_ENVIRONMENT: /opt/venv run: uv sync --locked --extra dev - # The hook-EXECUTION tests (test_write_path_trigger's nudge pair) run the - # real bash hook, which exits silently without jq — and those tests skip - # rather than fail when it's absent, so without this step they would - # quietly never be verified anywhere (ci-python ships without jq; same - # install the Plugin hooks job does). - - name: Install jq for hook execution tests - run: | - apt-get update -qq - apt-get install -y -qq --no-install-recommends jq - + # An "Install jq for hook execution tests" step stood here. The hook + # EXECUTION tests run the real bash hook, which exited silently without + # jq and skipped rather than failed, so the step existed to stop them + # being silently unverified. Since #4107 the hooks need no jq, so the + # step is gone and those tests run against a bare image — and the point + # of removing a dependency is lost if CI keeps installing it anyway. - name: Run tests # Integration tests (real Postgres) run in the `integration` job below. run: /opt/venv/bin/python -m pytest tests/ -q -m "not integration" diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 15ac240..07268f1 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "scribe", "description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting), and syncs your saved Scribe Processes as skills (/scribe:sync).", - "version": "2026.09.20.1620", + "version": "2026.09.20.1623", "author": { "name": "Bryan Van Deusen" }, diff --git a/plugin/hooks/scribe_report_check.sh b/plugin/hooks/scribe_report_check.sh index d8311f4..90ce991 100644 --- a/plugin/hooks/scribe_report_check.sh +++ b/plugin/hooks/scribe_report_check.sh @@ -119,7 +119,7 @@ turn_facts() { window | tail -n +"${1:-1}" | scribe_json_flat_lines \ # slow way — correct, and slow, which is the right way round for a check that # can block a stop. start=$(window | grep -n -F '"message":{"role":"user","content":"' 2>/dev/null \ - | cut -d: -f1 | awk 'END { if (NR) print $0 }') + | cut -d: -f1 | awk '{ last = $0 } END { if (NR) print last }') case "$start" in ''|*[!0-9]*) start=1 ;; esac facts=$(turn_facts "$start") diff --git a/tests/test_write_path_trigger.py b/tests/test_write_path_trigger.py index 53d8359..e8d488a 100644 --- a/tests/test_write_path_trigger.py +++ b/tests/test_write_path_trigger.py @@ -1091,8 +1091,9 @@ def test_hook_keeps_sync_and_reuse_dedup_apart(): assert ".sync.ids" in src # its own state file assert "exclude_sync_ids=" in src # its own query channel # The reuse file must NOT swallow sync ids — the write-back subtracts them. - assert "(.note_ids // []) - (.sync_note_ids // [])" in src - assert "(.sync_note_ids // [])[]?" in src + # Spelled with the readers that replaced jq (#4107); the claim is unchanged. + assert "scribe_json_list_minus \"$body_flat\" '.note_ids' '.sync_note_ids'" in src + assert "scribe_json_list \"$body_flat\" '.sync_note_ids'" in src def _hook_runtime_env(): @@ -1668,7 +1669,7 @@ def test_hook_keeps_the_rule_channel_apart_from_the_other_three(): src = HOOK.read_text() assert ".rules.ids" in src # its own state file assert "exclude_rule_ids=" in src # its own query channel - assert "(.rule_ids // [])[]?" in src # its own write-back + assert "scribe_json_list \"$body_flat\" '.rule_ids'" in src # its own write-back # And it rides the same request as the rest, not a second round trip. assert "${rule_exclude_q}" in src