Date: 2026-06-15
Status: Accepted
M14 adds realtime reactivity on top of the M13 presentation-event fanout boundary. The source contract needs to say which browser DOM regions can listen for presentation events before generated fanout codegen, client patching, reconnect behavior, or examples are implemented.
Existing constraints:
- Presentation events are the only browser-facing event category.
- Domain and integration events stay backend-owned facts.
- Generated JavaScript can enhance static-first pages, but it must not own routes, auth, trusted validation, business rules, page loading policy, or cache policy.
client {}is local UI behavior, not a general app data runtime.- Query references already define bounded, compiler-owned data regions through
g:query.
Issue #147 proposed compiler-derived event-to-query invalidation. M14 keeps the template contract explicit and adds a Go-owned invalidation graph instead of letting templates name domain events directly.
GOWDK accepts explicit view-level subscriptions through:
<section g:query="patients.GetPatientPage" g:subscribe="patients.PatientNotice">
...
</section>
Rules:
g:subscribenames a package-qualified Go presentation-event contract.g:subscribeis valid only on an element that also declaresg:query.- The compiler lowers each subscription into IR with owner, query, event, import alias/path, event type, source file, and source span.
- The compiler validates subscriptions against scanned runtime contract registrations.
- The referenced contract must be a
runtime/contractsevent registration withPresentationEventcategory. - The registration must be available to the generated web role, or unrestricted.
- The project must enable
realtime.Addon()before subscriptions are accepted.
GOWDK also accepts explicit Go invalidation registrations:
contracts.RegisterInvalidation[PatientCreated, GetPatientPage](registry)Rules:
RegisterInvalidation[event, query]says a domain event type invalidates a query type.- The compiler scans invalidation registrations beside normal contract registrations.
- The scanner rejects edges that name an unknown query, an unknown domain event, or a domain event no scanned command emits.
- The compiler joins invalidation edges to
g:queryreferences and records the joined graph inProgram.QueryInvalidations,gowdk-build-report.json, andgowdk graph. - Generated apps send a
gowdk.query.invalidatepresentation event after command event dispatch for affected domain events. - Generated
gowdk.jsrefetches the current document and swaps only matching non-subscribeddata-gowdk-query-typeregions. Regions withg:subscriberemain owned by explicit presentation patches.
This ADR does not make client {} a subscription language and does not let
templates reference domain or integration events directly.
- The source contract is explicit and inspectable.
- The target DOM region is bounded by an existing query-owned element.
- Diagnostics can point at the exact subscription attribute.
- Server fanout and client patch-loop work can consume one IR shape.
- Authors must name each presentation event or invalidation edge explicitly.
- Query invalidation performs a document refetch instead of payload diffing.
- Subscribed regions and invalidated regions have separate update ownership.
- Generated HTML may expose compiler-owned
data-gowdk-*markers for later runtime binding. - Generated HTML may expose validated
data-gowdk-query-typemarkers for exact invalidation matching.
- Extend
client {}with subscription statements. Rejected for M14 because it would mix server-state invalidation with local UI behavior and expand the bounded client language before the server/client ownership contract is ready. - Infer invalidation from handler bodies. Rejected because it would make backend data policy implicit and brittle.
- Allow templates to subscribe to domain or integration events directly. Rejected because those categories are backend-owned facts and must not become browser-facing input.
- #130: lower
g:subscribeto IR and validate presentation-event bindings. Implemented with exact-span diagnostics for missing, invalid, non-web-role, non-presentation, and missing-addon references. - #131: generate server fanout registration using subscription IR. Implemented as generated subscription-filtered SSE fanout.
- #132: implement compiler-owned client patch/refresh loop. Implemented for
explicit
replaceHTMLpatches on subscribed query regions. - #133: define reconnect, backpressure, and guard-gated stream behavior. Configurable SSE retry hints, bounded replay, drop-on-full client buffers, audience revocation, process-local stats, and guard rejection before stream open are implemented.
- #134: add live-updating examples and docs. Implemented in
examples/contracts. - #147: compiler-derived event-to-query invalidation. Implemented through
explicit
RegisterInvalidation[event, query]scan metadata, build-report and graph output, generatedgowdk.query.invalidatepresentation events, and query-region document refetch. - #538: user/session/audience scoping. Implemented for dependency-free SSE
through optional
EventEnvelope.Audiencelabels,contracts.EmitPresentationForAudience, andrealtime.WithSSEAudienceFromRequest. The labels are server-owned; generated streams remain guard-checked and subscription/type filtered, and applications install an audience-aware fanout when they send user- or tenant-specific presentation payloads.