fix(session): honor config.session.metadata on session:start in the Rust kernel - #103
Draft
Brian Krabach (bkrabach) wants to merge 1 commit into
Draft
fix(session): honor config.session.metadata on session:start in the Rust kernel#103Brian Krabach (bkrabach) wants to merge 1 commit into
Brian Krabach (bkrabach) wants to merge 1 commit into
Conversation
…ust kernel
`docs/specs/CONTRIBUTION_CHANNELS.md` lists `session.metadata` as a runtime
passthrough channel; `tests/test_session_metadata.py` specifies it; and
`python/amplifier_core/session.py` implements it. The Rust switchover shipped
without it.
Production does `from amplifier_core import AmplifierSession`, which is
`RustSession` (`python/amplifier_core/__init__.py`). Its lifecycle emit built
the payload from `{session_id, parent_id}` only, so configured metadata was
dropped before any hook saw it. The pure-Rust kernel had the same omission.
`session:fork` was unaffected -- it is emitted by the shared Python helper
`_session_init.py`, which already honored the contract. So one contract had
three payload constructions and two of them disagreed with it.
The metadata tests were green throughout, because they import the *pure-Python*
`amplifier_core.session.AmplifierSession` -- the class no runtime consumer
executes. That blind spot is what let this ship dead.
- `session_metadata_passthrough()` in `crates/amplifier-core/src/session.rs`:
one shared reader, so the Rust emit paths cannot drift apart again. Mirrors
the Python kernel's `if session_metadata:` guard, so absent *or* empty
metadata leaves the payload byte-identical to before.
- Merged into the bindings emit (cached at construction alongside
`session_id`/`parent_id`) and into the pure-Rust emit.
- `bindings/python/tests/test_session_metadata_rust.py`: the parallel suite
against `RustSession` that the existing tests could not provide.
Additive only. No new event, no new top-level payload key, no schema change.
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Brian Krabach (bkrabach)
marked this pull request as draft
August 21, 2026 02:34
Collaborator
Author
|
PARKED at the maintainer's request (2026-08-20) — amplifier-core cannot change right now. Work is complete and live-proven (see body); converted to draft to prevent accidental merge. Unpark order: this PR first, then amplifier-app-cli #278. Tracking: microsoft/amplifier-bundle-attractor#308. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parity bug fix, not a new feature
session.metadatais an already-specified, already-tested, already-used channel. It isdead at runtime.
docs/specs/CONTRIBUTION_CHANNELS.md:76—session.metadata – runtime metadata snapshots.tests/test_session_metadata.py— "CP-SM: Kernel readsconfig.session.metadataand includes it as optional'metadata'key in event payloads. Pure passthrough." Green today.python/amplifier_core/session.py:154-165— implements it.bindings/python/src/session.rs:389-392— builds{session_id, parent_id}. Metadata dropped.crates/amplifier-core/src/session.rs:318-324— same omission.session:forkpython/amplifier_core/_session_init.py:274-291— already correct (shared Python helper, both session classes delegate to it).Production does
from amplifier_core import AmplifierSession, which isRustSession(
python/amplifier_core/__init__.py:16). So one contract has three independent payloadconstructions, and two of them disagreed with it — on
session:startandsession:resumespecifically, while
session:forkquietly kept working.Why the tests didn't catch it:
tests/test_session_metadata.py:13importsfrom amplifier_core.session import AmplifierSession— the pure-Python class. Themetadata tests have been passing, green, on code that no runtime consumer executes. That
switchover blind spot is the actual defect; the missing merge is just its first casualty.
Dead-channel evidence
Measured on a real corpus on one machine (77,553
events.jsonlfiles under~/.amplifier/projects/): across a 3,000-file random draw, 2,983session:startrecords were sampled and 0 carried any
metadatakey. Observeddatakey-sets:amplifier-app-clihas been writingsession.metadatafor spawned children sincesession_spawner.py:440-449("enables kernel CP-SM passthrough on session:start/fork").It has been landing on
session:forkand vanishing onsession:start.The change (~15 LOC of product code)
crates/amplifier-core/src/session.rs—session_metadata_passthrough(), one sharedreader for both Rust emit paths so they cannot drift apart again. Its emptiness guard
mirrors the Python kernel's
if session_metadata:truthiness test, so absent or emptymetadata leaves the payload byte-identical to today.
bindings/python/src/session.rs— cachesession.metadatainnew()alongsidecached_session_id/cached_parent_id, merge it intopre_event_data.crates/amplifier-core/src/session.rs— same merge on the pure-Rust emit.No new event. No new top-level payload key. No change to
crates/amplifier-core/src/events.rs.Tests
bindings/python/tests/test_session_metadata_rust.py(new, 8 tests) — the parallelsuite against
RustSessionthat the existing tests could not provide: start/resume withand without metadata, empty-metadata parity with the Python guard, verbatim nested
passthrough,
session_id/parent_idnot displaced, andsession:forkpinned.crates/amplifier-core/src/session.rs(4 new Rust tests) — the passthrough reader(configured / absent /
{}/[]/""/null) plussession:startemitted with andwithout metadata.
CI that will run on this PR (
.github/workflows/rust-core-ci.yml):rust-tests(cargo test / check / fmt / clippy),
node-tests, andpython-testson 3.11 / 3.12 / 3.13.proto-check.ymlis untouched by this diff (no.protochanges).Back-compat
(
test_session_metadata.py,test_session_metadata_rust.py) and in Rust(
execute_omits_metadata_when_not_configured). Empty metadata is treated exactly likeabsent, matching the Python guard.
config.session.metadatais non-empty, so callers that configure nothing get today's payload byte-for-byte.
data.metadatabecausemetadatais notin hooks-logging's promoted-key list — no hooks-logging change is required or included.
events.jsonlstays exactly as itis. This improves future data only.
Live proof
Built from this branch (
maturin build --release), installed into a scratch venv togetherwith the companion app-cli change (microsoft/amplifier-app-cli#278), then one real
amplifier run --mode single "..."session. The persisted~/.amplifier/projects/<slug>/sessions/<id>/events.jsonlsession:startline, verbatimapart from formatting:
{"ts":"...","lvl":"INFO","schema":{"name":"amplifier.log","ver":"1.0.0"}, "event":"session:start","redaction":{"applied":true,"rules":["secrets","pii-basic"]}, "session_id":"a0122cbb-6438-4014-99cb-c63fefef9985", "data":{"metadata":{"invocation":{"launched_by":"cli","launched_by_session_id":null, "mode":"single","schema":1,"stdin_isatty":false,"stdout_isatty":false}},"parent_id":null}}Negative control — same app-cli, released
amplifier-core==1.6.0swapped back in, samecommand:
{"ts":"...","lvl":"INFO","schema":{"name":"amplifier.log","ver":"1.0.0"}, "event":"session:start","redaction":{"applied":true,"rules":["secrets","pii-basic"]}, "session_id":"bbbbd7c4-8a1b-4b26-ba73-edfcbf9dc577","data":{"parent_id":null}}Metadata configured on the mount plan, dropped by the kernel — the bug, reproduced on
demand. Note also that hooks-logging needed zero changes:
metadataauto-nests underdata.Notes for review
any kernel payload contract, not just this one. Fixing it broadly — e.g. parametrizing
the payload suites across both classes — is out of scope here, but worth a decision.
the same trap armed for the next field added to
session:start.Co-Authored-By: Amplifier 240397093+microsoft-amplifier@users.noreply.github.com