[AAASM-5752] 🐛 (core): Resolve an audit-sink disposition without raising, and pin the shipped set - #321
Conversation
audit_sink became a computed property under AAASM-5731, which is the right fix for the false `absent` it replaced, but it gave the lookup a failure mode the class attribute did not have: `getattr(x, name, None)` swallows AttributeError and nothing else, so a wrapped client whose `__getattr__` raises surfaced as `ConfigurationError: Failed to initialize assembly runtime: client is not connected` out of `init_assembly`. Guarded at all three sites a disposition is read through, not only the one the ticket names — the reproduction reaches `resolve_delegated_audit_sink` via `RuntimeQueryInterceptor.audit_sink`, which first reads `runtime_can_record`. Both fall to the under-claiming answer: `absent` is the value `init_assembly` warns on, so a handler this SDK cannot read is reported as making no record rather than passing silently for one that does (AAASM-5752).
…arately The gate accepted any of the four vocabulary values, including `caller-supplied` — which the shipped matrix cannot produce and which is exactly what `init_assembly` treats as "do not warn". Measured before/after against one production mutation (RuntimeQueryInterceptor.audit_sink returning caller-supplied), running the gate ALONE: the four-value form passes, the set assertion fails. `discarded` and `caller-supplied` are reachable — just not from this matrix — so each gets its own case rather than a standing waiver, along with the raising client. All three guards proven able to fail by reverting each in turn. (AAASM-5752)
| """ | ||
|
|
||
| class Raises: | ||
| def __getattr__(self, name: str) -> object: |
A `.venv` symlink pointing at a sibling worktree's environment slipped past
`.venv/` — that pattern matches a directory only — and was committed on this
branch. CI then failed at `uv sync`:
error: failed to create directory `.venv`: File exists (os error 17)
Measured both ways: with the old rule alone `git check-ignore .venv` does not
match the symlink; with the added rule it matches at .gitignore:12.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…e union
Review measured why the set form is not enough: it detects a NEW value but not
a WRONG ASSIGNMENT within the set. Flipping GatewayClient.audit_sink from
absent to forwarded — a false retention claim on two shipped configurations,
the defect this type exists to prevent — left the whole suite green, because
{absent, forwarded} was still the union. The per-label mapping reddens the gate
alone under that mutation.
The discarded case now drives the shipped factory instead of a 3-line stub: the
stub pinned the substitution rule inside AssemblyCallbackHandler, and passed
under the same mutation (AAASM-5752).
Review round 1 — findings addressedApproved with three recommendations; all three acted on. Head G2 — the set assertion did not bind the assignment, and that matteredThe review measured the hole: flipping The gate now asserts a per-label mapping. Under the same mutation it reddens alone. That is the second time in this PR that a form which "looks stricter" decided less than it appeared to. The four-value membership was one notch too wide; the set assertion was another. A set detects a new value; only a mapping binds which branch produces which. G1 — the
|
| """ | ||
|
|
||
| class RaisesOnEveryLookup: | ||
| def __getattr__(self, name: str) -> Any: |
|



Description
Two edge cases in the computed
audit_sinkdisposition, both measured during the independent review of AAASM-5731 / PR #315. Neither was a defect in what shipped — the review recommended merge, which happened — but each weakened a property that PR otherwise establishes.1. Resolving a disposition could raise.
audit_sinkbecame a computed property under AAASM-5731, which is the right fix for the falseabsentit replaced. It also introduced a failure mode the previous class attribute did not have:getattr(x, name, None)swallowsAttributeErrorand nothing else, so a wrapped client whose__getattr__raises anything else turned intoout of
init_assembly.Guarded at three sites, not the one the ticket names. The ticket points at
resolve_audit_sink, but the reproduction actually reachesresolve_delegated_audit_sink—RuntimeQueryInterceptor.audit_sinkdelegates there — and that property readsruntime_can_recordfirst, which has the samegetattrshape. Fixing only the named site would have left the reproduction reproducing.Both fall to the under-claiming answer.
absentis the valueinit_assemblywarns on, so a handler this SDK cannot read is reported as making no record rather than passing silently for one that does.2. The gate accepted a value the shipped matrix cannot produce.
test_every_shipped_governance_handler_declares_its_audit_sinkaccepted any of the four vocabulary values. It now asserts the set.Correction to the ticket's premise
The ticket states the shipped matrix yields
{absent, discarded}. Measured, it yields{forwarded, absent}— AAASM-5750 movedRuntimeQueryInterceptortoforwardedafter the ticket was filed. The assertion pins what is actually produced.discardedandcaller-suppliedare real and reachable, just not from this matrix:discardedis the LangChain handler wrapping an interceptor that records nothing,caller-suppliedis an object that declares nothing. Each gets its own case with its own reason, rather than a standing waiver inside the matrix gate.Type of Change
Breaking Changes
Related Issues
Testing
Full suite: 1338 passed, 15 skipped.
The gate was one notch weaker — measured before and after
Same production mutation both times (
RuntimeQueryInterceptor.audit_sinkreturningcaller-supplied), running only the matrix gate so sibling tests cannot stand in for it:With the whole file running, the mutation reddens 9 tests either way; defence-in-depth was never in question. What changed is that the gate now decides something by itself.
Every guard proven able to fail
Each reverted in turn, suite re-run, restored:
resolve_delegated_audit_sinkguardtest_resolving_a_disposition_never_raisesREDresolve_audit_sinkguardruntime_can_recordguardAnd the existing controls still bind in both directions, as the ticket's AC4 requires —
runtime_can_recordhard-wiredTruereddens the never-raises assertion; hard-wiredFalsereddens 8 tests including the matrix gate, the forwarding controls and the init-warning one.One mutation I ran first was worthless and is recorded so it does not get repeated: rewriting the constant
AUDIT_SINK_FORWARDED = "caller-supplied"moves the value the assertion compares against, so it is a tautology and passes. The mutation has to be at the property.Checklist