Epic: Internal read-model mutations and portable projectors - #166
Conversation
Introduce the public mutation authoring path from the domain-event-projections spec: versioned MutationProgram IR, mutation! macro, ReadModel capabilities metadata, portable handler catalog, server/cache/preview interpreters, and shared Rust/JS golden vectors. Wire SAVE_TODO/DELETE_TODO, SAVE_CHAT_MESSAGE, and SAVE_BLOB_GAME mutations into e2e fixtures while retaining projection! as the dual-path runtime mount until full cutover. Implements [[tasks/graphql-qs-mutation-projectors-1]] Implements [[tasks/graphql-qs-mutation-projectors-2]] Implements [[tasks/graphql-qs-mutation-projectors-3]] Implements [[tasks/graphql-qs-mutation-projectors-4]] Implements [[tasks/graphql-qs-mutation-projectors-5]] Implements [[tasks/graphql-qs-mutation-projectors-6]] Implements [[tasks/graphql-qs-mutation-projectors-7]] Implements [[tasks/graphql-qs-mutation-projectors-8]] Implements [[tasks/graphql-qs-mutation-projectors-9]] Implements [[tasks/graphql-qs-mutation-projectors-10]]
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…rvice path - Add placement-selected direct registry so commands call commit()?.projected() without naming a projection selector (Blob handlers updated). - Register BlobGames executor at e2e service construction. - Drive SAVE_TODO/DELETE_TODO/SAVE_CHAT_MESSAGE/SAVE_BLOB_GAME mutation programs from service registration and event handlers (not test-only stubs). - Hide event-owning projection! authoring from public docs; document .project(...) as non-preferred for application commands. - Unit proof: placement_selected_projected_uses_registered_executor_without_project_selector Implements [[tasks/graphql-qs-mutation-projectors-1]] Implements [[tasks/graphql-qs-mutation-projectors-10]] Implements [[tasks/graphql-qs-mutation-projectors-11]]
Replace event-owning projection! for TODO/CHAT/BLOB e2e mounts with mutation-backed ProjectionDescriptor factories: - program/resolve built via program_from_mutation_arms (SAVE_*/DELETE_*) - lower via shared lower_single_model ORM path - service construction asserts descriptor program bytes == mutation rewrite - handlers apply those descriptors without id()-only theater - Blob eligibility fixtures still use projection! for compile_fail only Also expose ResolvedProjectionPlan::resolve publicly for dual-path factories. Implements [[tasks/graphql-qs-mutation-projectors-1]]
Hard-delete competing projector authoring surfaces: - Remove event-owning `projection!` proc-macro export and source module - Delete distributed_macros projection unit/compile_fail suites - Remove CausalCommitBuilder/CausalRepository `.project(...)` selectors - Drop crate-root `ReadModelWritePlanBuilder` re-export (adapter remains at `distributed::read_model::ReadModelWritePlanBuilder` and TableWritePlan) - Migrate residual framework/test descriptors to mutation-backed factories - Add tests/legacy_authoring_absence.rs structural gate Task-11 matrix (fmt, clippy -D, workspace tests, npm quality, e2e-ui check-client/test) green at this cutover; browser/Postgres live env gates remain environment-dependent. Implements [[tasks/graphql-qs-mutation-projectors-1]] Implements [[tasks/graphql-qs-mutation-projectors-11]]
…or ORM export
Hard-delete separately authored command_effects!/command_confirmations! and
TypedCommand::{effects,confirmations}; demote ProjectionReadModelWorkspace to
crate-private; docs teach mutation! + .emits/.preview; expand structural gate;
restore command_input_defaults trybuild suite under a dedicated name.
Full task-11 matrix green (fmt, clippy -D, cargo test workspace all-features,
npm quality, e2e-ui offline, legacy_authoring_absence).
Implements [[tasks/graphql-qs-mutation-projectors-1]]
Implements [[tasks/graphql-qs-mutation-projectors-11]]
Replace residual repo.project(BLOB_GAMES).commit(...).projected() examples in e2e-ui README and walkthrough with the live repo.commit(game)?.projected() path. Keep compile_fail/migration notes only for the removed selector. Implements [[tasks/graphql-qs-mutation-projectors-1]] Implements [[tasks/graphql-qs-mutation-projectors-11]]
Delete the crate-private graph_workspace ORM path, strip dead command_effects authoring types/re-exports and constructors, rename macros module to command_input_defaults, and tighten structural gates. Keep protocol lifecycle primitives on CausalProjectorContext and low-level TableWritePlan adapters. Full matrix green (fmt, clippy -D, cargo test workspace, npm quality, e2e-ui). Implements [[tasks/graphql-qs-mutation-projectors-1]] Implements [[tasks/graphql-qs-mutation-projectors-11]]
Release-ready cleanup landed @
|
Framework owns resolve/lower/inventory factory glue via mutation_projector! and arm_state_upsert_for_model / arms_state_upsert_for_model / arm_delete_pk_from_envelope / build_mutation_projector_program. e2e todos/chat/blob projections shrink to mutations + event arms + mount. Implements [[tasks/graphql-qs-mutation-projectors-1]]
Implements [[tasks/graphql-qs-mutation-projectors-1]]
App authors declare mutations and which events apply them. Dual-path projection IR compile stays internal (compile_portable_handlers). Rename public bind_* helpers; hide arm_* vocabulary from the crate root docs. Rewrite e2e todos/chat/blob to portable_handlers! / bind language. Implements [[tasks/graphql-qs-mutation-projectors-1]]
…ers only Delete arm_* / build_mutation_projector_program shims and MutationProjectionArm. Public API is mutations + bind_* + portable_handlers! / compile_portable_handlers. Rename opaque compile unit to PortableHandler. Strip e2e program wrappers and service dual-path equality asserts. Implements [[tasks/graphql-qs-mutation-projectors-1]]
Matches BLOB_GAMES and CHAT_MESSAGES — the mount is the Todos model handlers, not a "reads" collection. Implements [[tasks/graphql-qs-mutation-projectors-1]]
…emos Inline todo.complete preview with the other commands via state_preview! on the typed command. Remove complete_preview from projections. Align event handler comments and UI walkthrough with mutation + portable_handlers. Implements [[tasks/graphql-qs-mutation-projectors-1]]
… commits Ship the revised domain-event projection authoring model for the mutation projectors cutover: - GraphQL-looking mutation documents via mutation_file! / mutation! (syntax-only IR) - Event-first portable_handlers! (on <events> apply <mutation>) - Command .applies for known mutation-input mapping (preview kept as alias) - Blob projected path: Mutation::from_state + readmodel(row).commit()?.projected() - e2e command modules renamed with aggregate prefix (todo_reopen, …) Implements [[tasks/graphql-qs-mutation-projectors-1]]
Live queries that join unowned tables (e.g. chat_messages.author → auth_users) no longer force live.supported=false; index plans skip zero-projector join tables so the WebSocket can stay active. Nullable missing relationship edges stay complete so optimistic rows remain materializable, and the chat UI no longer blanks the list while incomplete. Also: application surface uses user grants only so Todos/BlobGames keep a portable owner row policy for optimistic list inserts; chat unit partition for lobby resume; iMessage-style Sent/Delivered footers and AuthUsers display names on chat. Implements follow-ups for [[tasks/graphql-qs-mutation-projectors-1]]
Same-scope soft-nav rehydrate merges route SSR seeds instead of wiping confirmed records/indexes omitted from the page. Lobby chat uses a fixed scroll panel, page size 25, infinite history, and Chromium-correct column-reverse scroll geometry (negative scrollTop).
Gate causal_direct_v1_program behind the graphql feature so default/sqlite lib tests compile, and send the exact e2e-ui application surface roles list expected by select_protocol_surface.
Local Zitadel bootstrap and offline suite tokens assert both admin and user for admin humans/machines so they can use normal app surfaces and elevated paths without changing model permissions.
Command-substitution of create_human was capturing "reusing human …" lines into E2E_HUMAN_*_UID. Keep only the user id on stdout.
Make multi-role principals first-class for named application surfaces:
eligible roles control protocol open (any asserted role may open), while
schema privilege roles shape the portable client contract. e2e-ui keeps
eligible {admin,user} with schema {user} so owner-portable optimism is
preserved without collapsing model permissions.
Also stop treating incomplete/empty history pages as end-of-history while
the live chat window is still filling, and harden the Playwright history
scroll test under column-reverse.
Implements multi-role surface selection for [[tasks/graphql-qs-mutation-projectors-1]]
Point the fixture source gate at surface_for_application_contract and
eligible {admin,user} schema {user}. Widen the chat revalidation
optimism paint window so CI headroom stays under the delayed mutation
response without weakening the stale-while-revalidate assertion.
Summary
Implements [[tasks/graphql-qs-mutation-projectors-1]] — the mutation-projector authoring model from
specs/query-layer/v1/domain-event-projections.Delivered work packages
MutationProgramIR, field-presence lattice, sugar≡explicit, golden fixturetests/fixtures/mutation-program-v1.json,mutation!macro, typedReadModelMutationCapabilities(not GraphQL fields).lowerMutationCache) with auth/absent-base fail-closed vectors.SAVE_TODO/DELETE_TODO,SAVE_CHAT_MESSAGE, multi-model relationship fixture,SAVE_BLOB_GAME; client call shapes unchanged (await commands.todo.complete({ todo_id })).projection!proc-macro removed.project(...)selectors removed; Blob uses placement-selectedcommit()?.projected()ProjectionReadModelWorkspace/ graph load builder no longer exported (crate-private)command_effects!/command_confirmations!macros removedTypedCommand::effects/::confirmationsbuilders removed; commands use.emits/.preview+ mutation IRmutation!pathtests/legacy_authoring_absence.rsRetained low-level adapters (explicitly allowed)
TableWritePlan/ repository traitsdistributed::read_model::ReadModelWritePlanBuilder(not crate-root authoring)command_input_defaults!(input generators, not effect authoring)Base
tasks--graphql-qs-epic@abe05e3e0d2e35beb13db96dc4e221481f3cab37tasks--graphql-qs-mutation-projectors-1Test plan
cargo fmt --all --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace --all-targets --all-featuresnpm --prefix js run qualitymake -C tests/e2e-ui check-clientmake -C tests/e2e-ui test(offline)legacy_authoring_absence+ inventory empty for forbidden authoring surfacesNotes
Does not merge to
main, publish, or deploy. PR base is intentionallytasks--graphql-qs-epic, notmain.