Theme
Lesson 15 — Normalize events without lying
Outcome
Create a small normalized AgentEvent vocabulary with mandatory RawRef and Fact/Claim provenance.
Why this comes now
The UI and domain cannot understand every provider's native event names. Normalization gives Forge one language, but normalization must not erase uncertainty or invent conclusions.
Understand
A normalized event is a projection over retained raw evidence. Observed tool/process/file results are Facts. Agent-authored intent/conclusions are Claims. Unknown complete messages remain recoverable; unknown types that Forge would act on must fail closed. High-frequency token deltas do not automatically deserve permanent normalized rows.
Build the real project
- Define a deliberately small AgentEvent set needed by the fake: assistant message completed, tool call lifecycle, file/action fact, provider result, error, optional intent claim.
- Require
EvidenceKindandRawRef. - Persist/query normalized events.
- Implement adapter-to-canonical mapping for fake events.
- Add unknown-message diagnostic behavior.
- Keep live partial text as a separate transient path or clearly mark it non-historical.
Completion gate
Compile/tests make it difficult or impossible to create a normalized event without provenance and RawRef. Unknown action/control event does not get guessed into a known command.
Pitfalls to avoid
Do not create 100 canonical event types by mirroring one provider. Do not label an agent statement as an observed Fact. Do not persist every text chunk forever.
References
Sculptor structured integration: https://github.com/imbue-ai/sculptor/blob/main/docs/help/integrated_harnesses.md ; T3 architecture: https://github.com/pingdotgg/t3code/blob/main/docs/internals/overview.md
Checkpoint
M5 is complete when raw and normalized histories have clearly different responsibilities.