d18075925d
The exemption added in 6a8a7b31 was broader than the invariant it stood on. "Tool
declares a SOURCE_PATH" is a claim about the parameter list; the safe property is
"every byte written derives from an existing source object rather than from
model-supplied content". A future transform or import tool could name a source and
still write model-controlled output, and would have inherited the exemption.
ToolCapability.CONTENT_FROM_SOURCE now carries that provenance claim explicitly.
file_copy declares it; ReadBeforeWriteRule.appliesTo stands down only for calls that
do, so ToolCallAssessor skips the rule rather than the rule skipping itself. The
capability is recorded on the invocation event like every other one, so replay
classifies a call by what it actually claimed instead of re-deriving it from
parameters.
Tool availability is by declared tool name, not capability-set containment, so the
extra capability does not narrow which stages can reach file_copy.
Tests: the exemption is asserted through ToolCallAssessor, plus a source-naming tool
WITHOUT the provenance capability that stays gated. ./gradlew check green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
core/events — AGENTS.md
Purpose
Foundational event-sourcing infrastructure: all EventPayload definitions for every domain, the serialization registry, the EventStore interface, the EventReplayer contract, and shared identity/vocabulary types used across the entire core/ layer.
Ownership
CORREX kernel team. This is the most cross-cutting module in the codebase — changes here can break every other module. Coordinate before adding or renaming event types.
Local Contracts
EventPayload— sealed interface that every domain event implements.StoredEvent/EventEnvelope/EventMetadata— envelope types wrapping payloads for storage and replay.EventStore— interface for appending and querying events. Implementations ininfrastructure/.EventReplayer<S>/DefaultEventReplayer<S>— replays a filtered event stream through aProjection<S>to rebuild state.Projection<S>— interface:initial(): S+apply(state, event): S.StateBuilder/DefaultStateBuilder— convenience builder overEventReplayer.Serialization.kt— theeventModulepolymorphic block. Every newEventPayloadsubclass must be registered here. Missing registration = silent deserialization failure.JsonEventSerializer/EventSerializer— serialize/deserializeStoredEventto JSON.EventDispatcher— broadcasts events to in-process listeners.- Domain event files:
ApprovalEvents,ArtifactEvents,ContextEvents,InferenceEvents,OrchestrationEvents,RouterEvents,SessionEvents,TaskEvents,ToolEvents,IntentEvents,RiskAssessedEvent,JournalCompactedEvent, and many more — all payload definitions live here. FailureAttribution/FailureAttributor— the terminal-failure taxonomy (AGENT,HARNESS,WORKFLOW,ENVIRONMENT,PROVIDER,OPERATOR,UNKNOWN) carried asWorkflowFailedEvent.attribution, plus the deterministic reason→layer mapping used both at emission and when classifying historical events. One primary attribution per terminal event; a multi-cause chain is the session'sFailureTicketOpenedEvents, not a second structure.LspDiagnosticsCompletedEventrecords pulled language-server diagnostics or a graceful skip reason; replay consumes this observation and never contacts the server.ToolCapability.CONTENT_FROM_SOURCE— content-provenance claim: the bytes a call writes derive entirely from an existing source object it names, never from model output. Recorded on the invocation event like every other capability, so replay classifies the call by what it actually claimed. Only declare it on a tool whose output is a faithful reproduction of its source.- Shared vocabulary:
IdentityTypes(SessionId, TaskId, etc.),Tier,TokenUsage,ToolReceipt,ToolRequest,RiskLevel,RetryPolicy,GrantScope,GrantLedger.
Work Guidance
- SILENT FAILURE TRAP: After adding any
EventPayloadsubclass, immediately add it toSerialization.kteventModuleblock. Run./gradlew checkto verify. Tests may pass without it but runtime replay will fail silently. AnyMapSerializer— custom serializer forMap<String, Any?>; use it for dynamic payloads, don't roll another.- Event classes are
@Serializable data classwith no mutable state. No methods beyond data accessors. RunBranchPushedEventrecords an optional server Git transport push only after it succeeds; its branch/base/head SHAs are observations, not values replay recalculates.RepoMapEntry.descriptoris a bounded source-purpose observation recorded with the repo map and used when constructing semantic L3 embeddings.- Do not add domain logic to events. They are records, not actors.
FailureAttributoris the one exception by design: a pure reason→layer function that must be identical for live emission and for historical classification, so it lives beside the enum it returns. WorkflowFailedEvent.attributiondefaults toUNKNOWNso pre-field events replay unchanged. Classify those at READ time (seeFailureAttributionInspectionService); never rewrite history to backfill them.EgressAllowlistProjection— special projection kept in this module because it is used by bothcore:toolintentandcore:eventsconsumers; it is a shared cross-cutting projection.
Verification
./gradlew :core:events:test --rerun-tasks
Tests in testing/contracts/ (EventsTest, EventStoreContractTest) and testing/replay/ (serialization round-trips, replay integration).
Child DOX Index
No child AGENTS.md (leaf module).