Skip to content

fix(enrollment): preserve workspace enrollment authority - #1809

Open
jhrozek wants to merge 1 commit into
mainfrom
impl/workspace-enrollment-authority
Open

jhrozek wants to merge 1 commit into
mainfrom
impl/workspace-enrollment-authority

Conversation

@jhrozek

@jhrozek jhrozek commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Workspace enrollment replaced a session's whole tool capability set with the latest broker catalogue. That dropped unrelated authority (core, delegation, memory, skill, global/client-MCP, resource, and latent-member capabilities), and after a restart or clear/fork the server could not tell stale broker keys from capabilities the session should keep.
  • This PR persists a bounded broker-key ledger with a presence bit on the Session aggregate (through sessnap and eventsource). A completed enrollment now replaces only the prior broker bundle.
  • The server sets authority and ledger together on every root-creation and authority-carrying path (clear, fork, and CreateSession with a source session). session.New starts with absent provenance, so a path that forgets the ledger refuses enrollment instead of keeping stale keys.
  • Excluded broker keys stay in the ledger, so an exclusion survives a refresh that omits the key.
  • A broker/non-broker registration collision fails the whole enrollment with a presentation-safe FailedPrecondition naming only the key, clears the pending enrollment, and leaves prior authority and ledger unchanged. A recorded key still held in authority that a non-broker tool later takes fails every engine build for that session until the operator corrects the configuration.

Development stage

  • Plan / Interface — Bounded/Architectural behavioral and exact-interface contract; no implementation
  • Implementation — based on an approved, merged Plan / Interface PR
  • Combined — compact one-task Bounded/Architectural exception; no separate plan PR,
    and the in-PR plan declares **Expected tasks:** 1, a non-placeholder
    **Combined rationale:**, and no runtime/public/operator/persistence/trust-boundary
    interface change (None — rationale; workflow-only meta-changes may review process
    docs/skills here)
  • Spike / Routine / Cleanup — acceptance-plan spine exempt; Spike evidence does not ship as-is

Contract linkage

Interface conformance

  • Matches the approved contract (3e80d41cf): the two added Session accessors, the changed CompleteWorkspaceEnrollment semantics, the sessnap.Snapshot / eventsource.SessionMeta fields, no proto/RPC/config changes. Adds the AC1.5 proof that direct MCP refresh (feat(mcp): reconcile sources and refresh session tools #1673) refuses broker-bound sessions.

Issue relationship

Relates to #1740

Type of change

  • Behavioral/interface plan
  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation/process
  • Other (describe):

Test plan

Baseline checks

  • Acceptance-plan checker
  • Linting (task lint)
  • Fast offline test suite (task test)
  • Pre-review race suite (task test:race)
  • Offline demo (go run ./cmd/mecademo)
  • Markdown changed: docs generation/link checks (task docs)
  • User docs/user-facing behavior changed: site build (task site:build)
  • Guarded engine API affected: compatibility check (task api:check)
  • Intentional engine API change: task api:update + engine/CHANGELOG.md
  • Landed plan: strict acceptance trace (task ac-trace-strict)
  • Final implementation review: /panel-review

Run on this branch before the final rebases, which changed only docs (the tree is otherwise identical); CI re-runs everything:

  • task lint: 0 issues in the root, engine, authn/oidc, and four provider modules (run with --timeout 20m; the default timeout was too short on a cold cache).
  • task test, task api:check, task docs, task site:build, and the offline demo passed.
  • Acceptance-plan checker: passed, with the pre-existing warning that the plan's test names do not use TestADR_NNNN_* / TestInvariant_*.
  • task test:race (run module by module with -p 4): all seven modules passed with no data races, except three subprocess-timing tests in cmd/mecatui/agenthook that hit 10-second waits under full-suite load. That package is untouched by this PR; rerun alone with -race, it passes.
  • Not run yet: task ac-trace-strict (applies once the plan is landed), and /panel-review.

Changes

Area Change
engine/session Ledger + presence bit; absent by default; write-once restore; completion keeps excluded keys and checks bounds before mutating
engine/adapter/sessnap, eventsource Persist and restore the ledger; raw-JSON validation of presence, keys, and bounds
internal/adapter/server carriedAuthority carries authority + ledger together; creation records broker registration keys; typed collision error; collision/overflow clears pending
internal/app Broker tools register last against frozen keys; collision handoff without extra Spec() calls
cmd/mecatui/ui Collision shown as a terminal failure; retained prompt not sent; /tools-connect retry offered

User-facing change

  • /tools-connect no longer strips tools unrelated to the broker from the session.
  • A broker tool name that collides with another tool now reports a setup failure naming the tool, and the session keeps accepting prompts.
  • Sessions saved before this change cannot enroll; create a fresh session.

Special notes for reviewers

  • A later-appearing non-broker tool that takes a recorded broker key blocks the session until the operator fixes the configuration. This trades availability for the guarantee that a durable key never resolves to a different implementation (decision recorded in docs: plan workspace enrollment authority repair #1741).
  • The engine API gate: two accessors are Added; CompleteWorkspaceEnrollment's semantics are classified Changed (breaking, pre-v1 minor) in engine/CHANGELOG.md.

@jhrozek
jhrozek force-pushed the plan/workspace-enrollment-authority branch from bd51e49 to 9bacc04 Compare September 23, 2026 15:11
@jhrozek
jhrozek force-pushed the impl/workspace-enrollment-authority branch from c56d0ee to 9e31ab8 Compare September 23, 2026 15:11
@jhrozek
jhrozek force-pushed the plan/workspace-enrollment-authority branch from 9bacc04 to 943bc9a Compare September 23, 2026 20:43
@jhrozek
jhrozek force-pushed the impl/workspace-enrollment-authority branch from 9e31ab8 to 4a4c628 Compare September 23, 2026 20:43
@jhrozek
jhrozek force-pushed the plan/workspace-enrollment-authority branch from 943bc9a to 4e7142f Compare September 24, 2026 10:05
@jhrozek
jhrozek force-pushed the impl/workspace-enrollment-authority branch from 4a4c628 to b0f6c75 Compare September 24, 2026 10:05
@jhrozek
jhrozek force-pushed the plan/workspace-enrollment-authority branch from 4e7142f to 68be96d Compare September 24, 2026 11:16
@jhrozek
jhrozek force-pushed the impl/workspace-enrollment-authority branch from b0f6c75 to 92f3add Compare September 24, 2026 11:16
Base automatically changed from plan/workspace-enrollment-authority to main September 24, 2026 11:29
@jhrozek
jhrozek force-pushed the impl/workspace-enrollment-authority branch from 92f3add to 234f679 Compare September 24, 2026 11:31
@jhrozek
jhrozek marked this pull request as ready for review September 24, 2026 11:31
Workspace enrollment replaced the session's whole tool capability set with
the latest broker catalogue, discarding unrelated authority. Restart and
successor paths had no durable way to tell stale broker keys from
deliberately retained capabilities, and a cross-source registration
collision could make a durable key resolve to a different implementation.

Persist a bounded broker-key ledger with a presence bit on the Session
aggregate, through sessnap and eventsource. The server sets authority and
ledger together on every root-creation and authority-carrying path (clear,
fork, and CreateSession with a source session); session.New starts with
absent provenance, so a path that forgets the ledger refuses enrollment.
Completion replaces only the prior broker bundle and keeps excluded keys in
the ledger, so an exclusion survives a refresh that omits the key.

Composition registers broker tools last against frozen registration keys
and reports a collision as a typed, presentation-safe error that unwraps to
failed precondition. A recorded broker key still held in authority and now
owned by a non-broker tool fails every engine build. An enrollment-time
collision or ledger overflow clears the pending enrollment; mecatui shows
it as a terminal failure without sending a retained prompt.

Offline tests cover every acceptance criterion in
docs/acceptance/workspace-enrollment-authority.md: aggregate, persistence,
restart, successors, composition (the real Build with a configured deny),
server end to end, and TUI.

Implements the plan in #1741.

Relates to #1740.

Co-Authored-By: mecatl <noreply@mecatl.dev>

This branch was successfully deployed

1 active deployment
Preview — 75230db7 Deployed Sep 25, 2026 by vercel[bot]
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