Skip to content

Bump kanata engine: report-set sync + stamped telemetry (MAL-57 Layer 3); fix engine cache hash#898

Merged
malpern merged 1 commit into
masterfrom
mal-57-layer3-engine-bump
Jun 11, 2026
Merged

Bump kanata engine: report-set sync + stamped telemetry (MAL-57 Layer 3); fix engine cache hash#898
malpern merged 1 commit into
masterfrom
mal-57-layer3-engine-bump

Conversation

@malpern

@malpern malpern commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Submodule bump pulling malpern/kanata#28 (closes the MAL-57 fix-layer plan; fixes kanata#27):

  • Layer 3 — report-set divergence fix. send_key returned unready before the per-page template mutated the full-state key set, so a dropped Release left a stale key that the next report re-pressed indefinitely. The readiness check now lives after the mutation: dropped events still update intent, only the post is skipped, and the next successful report converges.
  • Telemetry. All VHID client connection lines now carry timestamps, and the previously-silent heartbeat-deadline reconnect path logs a stamped reason — the 2026-06-10 incidents were attributable to it only by elimination.

Also fixes a real build-system bug found while preparing this bump: the engine cache hash in Scripts/build-kanata.sh only covered *.rs/*.toml/*.lock, so this C++-only change served a stale cached engine on first build. The hash now includes C/C++ sources.

Fast-forward proof for the fork bump (6a05a4a → 12b64a6): merge commit's first parent is the old tip; git merge-base --is-ancestor confirms append-only.

Test plan

  • kanata cargo build + cargo test green (42 tests) on the fork branch
  • Engine release build via fixed build-kanata.sh: cache correctly invalidated, binary contains the Layer 3 telemetry marker, signs and smoke-tests
  • Post-deploy: stamped vhid-client: lines in the daemon stdout log

@malpern malpern enabled auto-merge June 11, 2026 18:02
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review — PR #898

Overall: Clean, well-scoped PR. Two targeted fixes with no extraneous changes. Approachable.


Overview

Two changes:

  1. Submodule bump (External/kanata 6a05a4a12b64a6): picks up the Layer 3 report-set divergence fix and stamped VHID telemetry.
  2. Cache hash fix (Scripts/build-kanata.sh): extends calculate_source_hash to include C/C++ source files, which the prior hash omitted despite the fork vendoring a C++ crate (karabiner-driverkit/driverkit/c_src).

What's Good

  • Comment in calculate_source_hash is exactly right. The CLAUDE.md rule is "only comment when the WHY is non-obvious." This comment nails it: it explains the constraint (C++ vendor sources), the crate path, and the silent failure mode (stale engine ships). No elaboration needed.
  • Minimal diff. 10 changed lines for two distinct bug fixes — nothing extra crept in.
  • Fork safety follows CLAUDE.md. The PR description documents fast-forward proof (merge-base --is-ancestor), consistent with the "treat keypath/bundled like master, never force-push" invariant.
  • Test plan is concrete. Cargo tests pass count given (42), cache invalidation is verified in context, telemetry marker confirmed in binary, and post-deploy log pattern called out.

Issues / Suggestions

1. Missing alternative C++ file extensions (minor)

The fix covers .c, .cpp, .h, .hpp but misses:

  • .cc, .cxx — common C++ source alternatives
  • .hh, .hxx, .inl — common C++ header alternatives
  • .S, .s — assembly files sometimes present in vendored C codebases

If karabiner-driverkit/c_src currently uses only .c/.cpp/.h/.hpp this won't bite, but it's worth double-checking. A future contributor adding a .cc file won't get a cache miss.

# Quick audit of what's actually in the crate:
find External/kanata -path "*/c_src/*" | grep -v target | sort

Consider widening slightly:

find . \( -name "*.rs" -o -name "*.toml" -o -name "*.lock" \
          -o -name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.cxx" \
          -o -name "*.h" -o -name "*.hh" -o -name "*.hpp" -o -name "*.hxx" \) \

2. Build scripts excluded from hash (pre-existing, worth noting)

Makefile, CMakeLists.txt, or build scripts inside the C subdir would also cause observable build output changes but aren't hashed. This is pre-existing — not introduced here — but worth keeping in mind if the vendor crate gains a build script.

3. find traversal order (pre-existing, not a regression)

find -exec shasum {} + produces per-file hashes in filesystem traversal order, which may vary across macOS versions. The output is piped to a second shasum so order matters for the final hash. In practice HFS+/APFS traversal order is stable across runs on the same machine, but a | sort | shasum pattern would be strictly more robust. Again, pre-existing — mentioning it only for completeness.


Verdict

Approve with optional follow-up. The cache hash fix correctly closes the silent-stale-engine footgun for the known case (.cpp/.hpp in driverkit). Item 1 above is worth a quick check — if the c_src directory already uses only the covered extensions, this is ready to merge as-is.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

https://github.com/malpern/KeyPath/blob/0e794100c5df7c1a44faa39f78d54e24bf545cee/External/kanata/driverkit/c_src/driverkit.cpp#L37
P1 Badge Clear every report set before returning unready

When sink_ready is false, this now returns 2 after inserting the code into whichever per-page report set was selected. If the event is a press for a non-keyboard page (for example consumer/media, top-case, or generic-desktop) and the recovery loop releases input before the physical release is captured, that set is left dirty; release_input_only() only clears keyboard.keys, so the next successful report for that page can reassert the stale key indefinitely. Either avoid retaining dropped presses across recovery or clear all per-page report sets on the unready/release-input path.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… 3); fix engine cache hash

Pulls malpern/kanata#28: a Release dropped while the VHID sink is
unready no longer desyncs the C++ full-state report set (which could
re-press a released key indefinitely on the next report), and every
client connection event is now timestamped, including the previously
silent heartbeat-deadline reconnect path.

Also fixes Scripts/build-kanata.sh cache invalidation: the source hash
only covered *.rs/*.toml/*.lock, so a C++-only change in the vendored
driverkit crate served a stale cached engine. Include *.c/*.cpp/*.h/*.hpp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@malpern malpern force-pushed the mal-57-layer3-engine-bump branch from 0e79410 to 2fe33c4 Compare June 11, 2026 18:09
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review — PR #898

Overview

Two targeted changes: a submodule bump pulling the MAL-57 Layer 3 fix into External/kanata, and a build-system correctness fix in Scripts/build-kanata.sh that closes a silent stale-cache bug. Both are well-scoped; the diff is exactly as small as it should be.


Scripts/build-kanata.sh — cache hash extension

Correctness: ✅

The original find only matched *.rs, *.toml, and *.lock, so a change confined to the vendored C/C++ sources (the karabiner-driverkit driverkit/c_src/ path) wouldn't invalidate the cache and a stale binary would silently ship. Adding *.c, *.cpp, *.h, *.hpp closes that gap.

Including .h/.hpp is important beyond just the .c/.cpp files — inline functions and interface changes in headers affect the compiled output without touching any .c file.

Comment quality: ✅

The added comment explains why (hidden invariant, specific past incident) rather than what — exactly the right call per project conventions.

Minor suggestion (non-blocking):

Consider whether .s / .S assembly stubs could ever appear in the fork (some driverkit-adjacent code uses assembly thunks). Today the answer is probably no, but if the vendored C layer grows, it would cause the same silent-cache problem. Could add them defensively:

find . \( -name "*.rs" -o -name "*.toml" -o -name "*.lock" \
          -o -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \
          -o -name "*.s" -o -name "*.S" \) \

Not a blocker — just worth noting before the next C extension lands.


External/kanata submodule bump (6a05a4a12b64a6)

Fork safety: ✅

PR description includes fast-forward proof (merge-base --is-ancestor verified). This satisfies the CLAUDE.md invariant: "Before pushing to any remote branch in the fork, verify the push is a fast-forward."

Kanata changes included (per PR description):

  • Layer 3 report-set sync fixsend_key readiness check moved to after the per-page template mutates the full-state key set, so a dropped Release no longer leaves a stale re-pressed key. The fix is narrowly scoped: dropped events still update intent; only the post is skipped.
  • Telemetry — VHID client lines now carry timestamps; the heartbeat-deadline reconnect path logs a stamped reason. Both are observability improvements with no behavioral change.

Test coverage

  • cargo test (42 tests) green on the fork branch — appropriate for a Rust submodule change.
  • Cache invalidation tested manually (confirmed miss + rebuild after the hash fix).
  • No Swift tests needed — no Swift code changed.

Summary

Clean PR, minimal diff, good documentation of root cause. The build-cache bug was a real silent correctness risk (stale engine ships without warning). The submodule history is append-only.

Approve with optional assembly-glob suggestion above.

@malpern malpern merged commit 5a9d9b7 into master Jun 11, 2026
3 checks passed
@malpern malpern deleted the mal-57-layer3-engine-bump branch June 11, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MAL-57 Layer 3: replay key Releases dropped while the output backend is unavailable

1 participant