509 ctx.log() sites across 164 files were reported as no-ops, and the
obvious reading was that HookContext dependency injection had never been connected.
It had been connected since v7.30.0. The defect was two defaults sitting on opposite sides of one
comparison, which is why the wiring looked absent from every angle except the gate itself.
logHook(name, msg, level = 'debug') <- what a bare ctx.log() sends
getLogLevel() = 'warn' <- the threshold it is compared against
shouldLog('debug') vs 'warn' = false <- so it returned before touching disk
427 of the 503 call sites pass no
level at all. Those are the record-keeping calls: the ones that state what a hook decided. Every one
of them was guaranteed-dropped in production and landed only when a
developer had explicitly set ORCHESTKIT_LOG_LEVEL=debug.
A hook whose ctx was never injected writes nothing. A hook whose ctx is
injected correctly but whose level loses the comparison also writes nothing. Nothing in
hooks.log distinguishes them, so the absent-output symptom pointed at the wrong layer
and the real one-line cause survived every earlier pass.
| knob | was | now | why |
|---|---|---|---|
logHook default level | debug | info | bare calls are records, not verbosity |
getLogLevel() default | warn | info | the gate must admit the default level |
| stale-team DELETE record | implicit | explicit warn | survives an operator-tightened level |
The invariant worth keeping: the
default level and the default threshold must stay on the same side of the gate.
'debug' remains opt-in verbosity; it is no longer the level that record-keeping
accidentally used.
Unit-testing ctx.log with a mock proves the call happened, not that anything landed.
Section 9 of the dispatcher suite backdates a team fixture 48h, runs the real destructive
stale-team-cleanup hook through the real built bundle with no log-level env set, then
asserts the physical bytes.
9. HookContext logging lands on disk (#3386) ✓ stale team actually deleted (destructive action executed) ✓ DELETE record landed on disk at default log level ✓ bare ctx.log() summary line landed (info admitted by default gate)
The first assertion exists so the other two cannot pass vacuously: if the fixture stops being stale, the hook never deletes, and a log assertion against an empty file would otherwise be meaningless rather than failing.
dispatcher suite 29 passed, 0 failed npm test --quick 25 categories passed, 0 failed tsc --noEmit clean npm run build plugins regenerated, only the 4 source files + dist churn
Class of bug, for the v10 ledger: this is a false signal, not a missing feature. The machinery existed, ran, and reported nothing, so every session that debugged through these hooks was reading an empty file as evidence of quiet success. Sibling instances in the same milestone are hooks registered but never dispatched, guards that pass because they cannot observe, and telemetry fields that look like measurements. The repair is the same shape each time: assert the effect, not the call.