Conversation
The audit taxonomy is the trail's vocabulary, and a name in it reads as a promise that this deployment can produce that row. Four of them could not. `connector.sync_succeeded`, `connector.sync_failed`, `knowledge.searched` and `agent.invoked` are declared in `auditEventTypes` and written by nothing in `server/src`. They outlived the code that wrote them — `server/src/knowledge` and `server/src/connectors` are both empty directories now, and `git log -S` shows `knowledge.searched` arriving in CopilotKit#113 and its only writer leaving in CopilotKit#118. They stayed because the taxonomy test named them explicitly, so the list kept agreeing with itself. That is not cosmetic for an operator. The Audit screen filters by type, so filtering for `knowledge.searched` returns an empty page, and an empty page in an audit trail reads as "this did not happen" rather than "this cannot happen". A record that can say nothing is worse than a record that says nothing, because the reader cannot tell which one they are looking at. Removing them is safe to read back: the Audit UI does not enumerate `auditEventTypes` — it uses the curated `REFUSED_EVENT_TYPES` and `DID_NOT_HAPPEN_EVENT_TYPES` groups, and neither names these — and `AuditEvent.eventType` is a plain `string`, so any historical row still loads and renders. This narrows what the type is allowed to claim, not what the table is allowed to hold. The guard is the point of the change. `declares nothing this deployment cannot write` reads the event types off `server/src` rather than off a hand-kept list, because a hand-kept list is exactly what just failed. It matches literal strings only: every writer today passes the type as a literal, and a writer that computed one would fail here and should — an event type a reader cannot grep for is worse than this test being strict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hotragn
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso,
mxmzb and
tylerslaton
as code owners
September 14, 2026 22:45
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.
What this changes
Four event types are declared in
auditEventTypesand written by nothing inserver/src:connector.sync_succeededserver/src/connectorsis an empty directoryconnector.sync_failedknowledge.searchedserver/src/knowledgeis an empty directory;git log -Sshows it arriving in #113 and its only writer leaving in #118agent.invokedThey stayed for months because the taxonomy test named them explicitly, so the list kept agreeing with itself rather than with the code.
This removes them, and adds the guard that would have caught it.
Where it runs
server/src/audit.ts— the declaration only. No writer changes, because there are none to change.Boundary and audit
This is the audit trail, so the reason is the operator's, not the type checker's.
The Audit screen filters by event type. Filtering for
knowledge.searchedreturns an empty page, and an empty page in an audit trail reads as this did not happen rather than this cannot happen. Those are very different answers to give someone checking whether a search touched a document, and the record currently cannot distinguish them. A trail that can say nothing is worse than one that says nothing, because the reader cannot tell which they are looking at.Reading old rows is unaffected, and I checked rather than assumed:
auditEventTypes. It uses the curatedREFUSED_EVENT_TYPESandDID_NOT_HAPPEN_EVENT_TYPESgroups, and neither names any of the four.AuditEvent.eventTypeis a plainstring, not the union, so a historical row carrying one of these still loads and renders.So this narrows what the type is allowed to claim, not what the table is allowed to hold — which is the right direction for a record you are not allowed to rewrite.
Changelog
Not user-facing — no operator can currently see these types produce anything, which is the bug.
Proof
The new test,
declares nothing this deployment cannot write, reads the event types offserver/srcrather than off a hand-kept list, because a hand-kept list is exactly what just failed. Run againstmainand against this branch:It fails on the bug and passes on the fix, which is the only property a guard has to have.
It matches literal strings only. That is deliberate: every writer today passes the type as a literal, and a writer that computed one would fail here and should — an event type a reader cannot grep for is worse than this test being strict.
I could not run
bun test server/tests/audit.test.tsend to end on this machine. The file importscreateApp, and mynode_modulesis stale —cron-parser,@ag-ui/mastraand@mastra/client-jsare declared inpackage.jsonbut absent locally, so the import fails before any test runs. I confirmed this failure is identical on unmodifiedmain, and ran the guard's logic standalone against both trees to produce the numbers above.bunx tsc --noEmitinserver/reports only those same three missing packages and nothing from these files.What is not covered
server/srcbut missing from the taxonomy — is not checked here. It is a real gap, but catching it means recognising writer call sites rather than grepping strings, and that is its own change.🤖 Generated with Claude Code