Usage telemetry alive — readout fix, canaries, and the snippet-recording seam #110

Merged
bvandeusen merged 3 commits from dev into main 2026-08-14 22:36:43 -04:00
Owner

Three commits closing the loop on issues #2663 and #2664:

  • fix(telemetry) 77acee9 — strong task references for both fire-and-forget telemetry writers, WARNING+AppLog canaries in note_usage, and the previously-missing real-Postgres integration tests for _insert_eventsusage_for_notes.
  • feat(reuse) fb757fb — the prior-art hook's local arm asks for create_snippet when duplication is proven and unrecorded; floor bullet names the recording triggers; plugin 0.1.29.
  • fix(telemetry) 4107b17 — the root cause the new tests caught on their first run: usage_for_notes rebuilt the ambient CASE in GROUP BY, asyncpg's expanding IN-params made it a different expression, Postgres rejected every readout with a GroupingError, and the swallow rendered it as zeros. One labelled expression, grouped by its label. Writes were never broken — accumulated usage history appears on deploy.

CI run 3748 green across all lanes on dev HEAD 4107b17.

🤖 Generated with Claude Code

Three commits closing the loop on issues #2663 and #2664: - **fix(telemetry) 77acee9** — strong task references for both fire-and-forget telemetry writers, WARNING+AppLog canaries in note_usage, and the previously-missing real-Postgres integration tests for `_insert_events` → `usage_for_notes`. - **feat(reuse) fb757fb** — the prior-art hook's local arm asks for `create_snippet` when duplication is proven and unrecorded; floor bullet names the recording triggers; plugin 0.1.29. - **fix(telemetry) 4107b17** — the root cause the new tests caught on their first run: `usage_for_notes` rebuilt the ambient CASE in GROUP BY, asyncpg's expanding IN-params made it a different expression, Postgres rejected every readout with a GroupingError, and the swallow rendered it as zeros. One labelled expression, grouped by its label. Writes were never broken — accumulated usage history appears on deploy. CI run 3748 green across all lanes on dev HEAD 4107b17. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bvandeusen added 3 commits 2026-08-14 22:36:37 -04:00
The deployed instance ran with every usage counter at zero while surfacing
demonstrably fired. Every unit test was green because every unit test mocked
either _schedule or the session — the two functions that touch the database
ran against real Postgres nowhere. Both telemetry writers also held no
reference to their fire-and-forget tasks (the loop keeps only weak ones), and
swallowed every failure into logger.debug, so a total outage was
indistinguishable from an unused corpus.

- note_usage + retrieval_telemetry keep strong task references until done
- failures log at WARNING; note_usage additionally drops one AppLog error row
  per process per site, so the admin UI shows the outage without host access
- integration tests cover _insert_events -> usage_for_notes and the full
  record_pulled chain on a running loop, splitting the write and read halves
  so a failure names its side

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(reuse): recording gets a seam — the prior-art hook asks for create_snippet when duplication is proven and unrecorded (#2664)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 25s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / integration (push) Failing after 21s
CI & Build / Python tests (push) Failing after 31s
CI & Build / Build & push image (push) Skipped
fb757fb4ba
Zero snippets were ever recorded outside sessions already thinking about
snippets: the read side had a real seam (the PreToolUse hook) and the record
side had a trailing clause of a floor bullet. The trigger moment — 'I just
wrote the second copy' — is mid-Write/Edit, so the nudge now rides the same
hook: when the local arm proves the definition exists elsewhere in the repo
AND Scribe returned no record of it, the context block asks for
create_snippet. Both gates or silence, so a brand-new helper and an
already-recorded one stay nudge-free.

Floor bullet promoted to name the trigger moments (extract, hoist, second
copy); guarded by test the same way the Systems reflex is. Plugin 0.1.29 so
the cache picks up the hook (#2209).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(telemetry): usage readout grouped by a rebuilt CASE — group by the label instead (#2663 root cause)
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / Python tests (push) Successful in 1m0s
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Build & push image (push) Successful in 39s
4107b17727
CI's new integration tests reproduced the outage on a clean database and
named the half: the writes land fine, and usage_for_notes fails on EVERY
call. The GROUP BY rebuilt the ambient case() expression, and asyncpg's
expanding IN-parameters give each instance its own bind names — so Postgres
sees a SELECT expression the GROUP BY doesn't cover and rejects the query
with a GroupingError, which the old code swallowed into zeros. One labelled
expression, grouped by its label. The deployed table has been accumulating
events all along; history appears as soon as this deploys.

Also: the two hook-execution tests now run with the real PATH and skip
when the hook's tools are absent (the restricted-PATH convention next door
is for silence contracts, where empty-for-the-wrong-reason still passes) —
and the unit lane installs jq so 'skip' never quietly becomes 'nowhere'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bvandeusen merged commit e05a660471 into main 2026-08-14 22:36:43 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledScribe#110