Skip to content

refactor(e2e-ui): session guards on user command mounts - #173

Closed
patrickleet wants to merge 35 commits into
mainfrom
feat/e2e-command-session-guards
Closed

refactor(e2e-ui): session guards on user command mounts#173
patrickleet wants to merge 35 commits into
mainfrom
feat/e2e-command-session-guards

Conversation

@patrickleet

Copy link
Copy Markdown
Collaborator

Summary

  • Wire e2e-ui todo/blob/chat commands with .guarded(causal_has_user, …) (and causal_is_admin for force-archive) instead of discovering missing identity mid-handler.
  • Handlers use principal(ctx) for domain owner/author args only; input parse, load, and domain invariants stay in the body.
  • Expose CausalCommandContext::session(); treat empty user id as missing for user_id().
  • Clarify blob move docs (provisional client pure vs Atomic seal); suite unauth case accepts guard rejection (400/errors).

Stack

Stacked on tasks--contract-lifecycle-tooling-1 (PR #172).

Test plan

  • cargo check -p e2e-service
  • cargo test -p e2e-service --lib
  • session/guard unit tests
  • CI offline suite on PR

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 71f0618d-45e0-4033-8eb7-6d826d844506

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Implements [[tasks/contract-lifecycle-tooling-2]]
Implements [[tasks/contract-lifecycle-tooling-2]]
Implements [[tasks/contract-lifecycle-tooling-2]]
Implements [[tasks/contract-lifecycle-tooling-2]]
Implements [[tasks/contract-lifecycle-tooling-3]]
Implements [[tasks/contract-lifecycle-tooling-3]]
Implements [[tasks/contract-lifecycle-tooling-3]]
Implements [[tasks/contract-lifecycle-tooling-9]]
Close the next-major surface identity cutover for task 9: application
surfaces carry distinct eligible_roles (who may open) and schema_roles
(privilege intersection), end-to-end across Surface IR, client manifest,
protocol authority, projection deltas, CLI selectors, and TS replica
validation/clone.

Also land remaining application composition fixtures (contract-only
artifact bin, runtime/unlisted packages, renamed-dependency trybuild)
and remove the dual manifest owner path.

Implements [[tasks/contract-lifecycle-tooling-9]]
Add the pure deployment-plan compiler for task 10: composable
MountSelector presets (Full/Writer/Projector/QueryApi), Atomic
collocation validation, explained capability closure with single
schema-lifecycle owner, topology intent inventory, and
byte-deterministic plan identity linked to ApplicationManifest.

Implements [[tasks/contract-lifecycle-tooling-10]]
Add contract lifecycle snapshot flattening, path-level semantic diffs,
distinct manifest-wire vs protocol decision classification, and no-write
predecessor chain diagnostics with owner/path/expected/observed facts.

Implements [[tasks/contract-lifecycle-tooling-4]]
Rename the standalone binary from dctl to distributed, add top-level
DistributedArgs with contracts check/accept, and implement exact-scope
accept transactions with staging, atomic replace, and rollback. Update
in-repo product docs, workflows, skills, and generated client banners
to invoke distributed.

Implements [[tasks/contract-lifecycle-tooling-5]]
Introduce the versioned command dispatch boundary used by API-only and
writer processes: object-safe CommandDispatcher, LocalCommandDispatcher
over Service, and the task-20 approved HTTPS mTLS remote profile with
loopback parity tests that strip forwarded role claims.

Implements [[tasks/contract-lifecycle-tooling-11]] and
[[tasks/contract-lifecycle-tooling-20]]
Introduce the task-12 process host skeleton: bind one DeploymentPlan
process against explicit capability providers and an optional
CommandDispatcher, failing closed when required capabilities or dispatch
are missing.

Implements [[tasks/contract-lifecycle-tooling-12]]
Add an explicit application composition module for e2e-ui surface and
module identities, re-export it from the service crate, migrate read
models to ReadModelCatalog, and update GraphQL surface registration for
eligible/schema role split.

Partial [[tasks/contract-lifecycle-tooling-13]]
Expose graphql_router_with_dispatcher as the host-facing mutation path,
keep graphql_router_with_service as the local adapter implementation, and
update dispatcher-not-configured diagnostics.

Implements [[tasks/contract-lifecycle-tooling-11]]
Add make contracts-check, e2e-ui Makefile routing, an opt-in pre-push
template, and a PR contracts job that other broad jobs can depend on.

Implements [[tasks/contract-lifecycle-tooling-6]]
Add deterministic ClientProgramDescriptor building, asset hashing, and
three-way Current / asset-only / incompatible classification, with
program:<id> accept scopes.

Implements [[tasks/contract-lifecycle-tooling-7]]
Verify application → plan → optional program predecessor links and
release program classification for CTL closeout outside K8s/Hops work.

Implements [[tasks/contract-lifecycle-tooling-8]]
Move process wiring into e2e_service::run_e2e_host with LocalCommandDispatcher
at the host boundary, shrink the runner binary to env selection + host call,
and check in selectable deployment-plan inventories for full/api/writer/
projector/mixed/split cuts.

Implements [[tasks/contract-lifecycle-tooling-13]]
Replace the ~900-line central service.rs wiring file with explicit
todo/chat/blob modules, a compose root listing them into one Service,
GraphQL/surface exports, and a thin compatibility façade. Runner remains
one-screen host invocation.

This is the DX outcome of the application-composition epic for e2e-ui:
module inventory is review-visible; infrastructure stays in host.

Implements [[tasks/contract-lifecycle-tooling-13]]
Re-export compose/graphql APIs from lib.rs and keep client-surface tests
next to graphql module. No intermediate service.rs re-export layer.

Implements [[tasks/contract-lifecycle-tooling-13]]
Add LocalProjectionMountsBuilder so apps declare program+model+epoch only,
Routes::for_aggregate to hide QueuedRepository wiring, and shared outbox/
consumer worker helpers. Rewrite e2e projection mounts and host loops to
use them — product code keeps command intent, not topology ceremony.

Implements [[tasks/contract-lifecycle-tooling-13]]
Derive client projection previews from command input, defaults, and
row-policy claims when .applies is omitted. Add command_transition and
sourced domain_commands witnesses so emit sets come from domain methods.
Migrate todo/chat/blob to that path, thin blob move to game_id+direction,
delete client simulate_move, and fix distributed client --surface to read
roles from the manifest for gen-client.
Drop simulate-move unit test, stop requiring fat blob preview fields,
and assert eligible/schema application roles on generated clients.
…tests

Workers require tokio spawn/sleep — compile them only when a feature enables
the optional tokio dep so default-feature checks pass. Rename integration
test env! paths from CARGO_BIN_EXE_dctl to CARGO_BIN_EXE_distributed after
the binary rename.
Defer --surface kind/name mismatches to the client compiler so CLI stderr
keeps client.manifest.surface_mismatch. Use Session::new() in the default-
feature typed guard dispatch test so session_with_role is not required
without the graphql feature.
describe no longer wraps DistributedManifestEnvelope { project }; it emits
ApplicationManifest with name/modules/surfaces. Align CLI validation so
cli_manifest describe_emits_manifest_json matches the composition contract.
After the eligible/schema role split, ClientSurfaceIdentity expects
eligible_roles (not legacy roles). Update GraphQL surface parity fixtures
and the e2e suite helper so multi-role and anonymous application opens
deserialize and authorize correctly.
Update typed_commands to expect auto-derived preview occurrences when
.applies is empty. Silence svelte-check on the inventory config script,
fix the HowItsBuilt dialog markup, and relax Playwright assertions that
required pre-wire board/status paint (thin move + domain status constants
are not client-derivable without a residual applies map).
Without domain status constants in auto-optimism, a double-click on Done
is not client-suppressed before Eventual seals. Use one click and assert
exactly one todos_complete request.
Reopen list membership depends on status, which auto-optimism does not
constant-map. Assert Open-column paint after the delayed wire returns.
Add projection.pureReduces: named pure functions run over a known cache
row at optimistic-apply time (fail closed if missing). Register
blob.simulate_move (TS twin of domain simulate_move), wire the user
command artifact, and restore pre-wire board paint in Playwright.

Next: emit pureReduces from Rust TypedCommand + gen-client so generated
artifacts stay durable across regenerate.
Wire TypedCommand.preview_reduce_known_record through the client
manifest, CLI pure-reduce IR, and durable generated pures.ts so
blob.simulate_move is no longer hand-patched into GENERATED commands.

Blob move registers the domain pure (client twin at $lib/blob/simulate-move);
createCommands spreads PURE_FUNCTIONS into the replica runtime.
Move session admission (has user / is admin) onto TypedRouteBuilder
.guarded mounts so handlers only bind principal and call domain.
Add CausalCommandContext::session, causal_has_user/causal_is_admin
helpers, and keep input/domain checks in the handler body.
@patrickleet
patrickleet force-pushed the tasks--contract-lifecycle-tooling-1 branch from 8b265f4 to 367f191 Compare August 13, 2026 02:51
@patrickleet
patrickleet force-pushed the feat/e2e-command-session-guards branch from 2de52b4 to 055a041 Compare August 13, 2026 02:51
@patrickleet
patrickleet changed the base branch from tasks--contract-lifecycle-tooling-1 to main August 13, 2026 03:11
patrickleet added a commit that referenced this pull request Aug 13, 2026
Squash the complete stacked change set through PR #178 after GitHub's
stack merge operation landed only the bottom layer.

Includes PRs #172, #173, #174, #177, and #178.

BREAKING CHANGE: the standalone CLI binary is named distributed instead
of dctl, and application surface roles use the eligible/schema split.
@patrickleet

Copy link
Copy Markdown
Collaborator Author

Stack merge recovery: GitHub’s stack operation partially landed the stack. The complete stack through #178 is now preserved on main as aggregate squash b97f96a, whose tree matches the original #178 head exactly. This PR is superseded by that commit; its source branch is being retained. The prior broken main is preserved at recovery/github-stack-broken-main-20260813-031051.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant