docs: plan workspace enrollment authority repair - #1741
Conversation
Open design question: runtime-only vs durable broker provenanceThe implementation does not restore tool implementations from a session snapshot. On run entry it reconstructs an engine from current server composition. For ordinary tools that is sufficient. Broker tools are intentionally different after a process restart: That creates two possible scopes for #1740:
The existing plan/ADR currently assumes option 2. That should not be merged until we decide whether same-session exact replacement after a broker/process restart is required. If it is not, the plan should be narrowed to option 1 and the durable provenance ADR removed. Related edge case: a broker/non-broker duplicate registration key must either be rejected as a whole-bundle enrollment failure or receive a specified precedence rule. Namespacing makes this unusual, but it is possible when two sources expose the same |
bd51e49 to
9bacc04
Compare
9bacc04 to
943bc9a
Compare
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>
JAORMX
left a comment
There was a problem hiding this comment.
The approach looks good: track the broker-installed keys, replace only that bundle, and preserve unrelated authority and existing exclusions.
Two non-blocking notes:
- Please distinguish clearing the pending enrollment from making the session usable again. An incoming-tool collision can leave the prior session usable, but a conflict involving a recorded broker key still held in authority blocks engine construction until the configuration is corrected. AC3.1's “session accepts prompts again” wording should reflect that distinction.
- We’re moving fast and don’t need upgrade compatibility or migration work here. Refusing enrollment for sessions without provenance and requiring a fresh session is acceptable; a basic actionable error is enough.
Approving the plan. This review does not cover the implementation in #1809.
943bc9a to
4e7142f
Compare
Main now has its own ADR 0356, so the workspace enrollment authority decision record becomes ADR 0357. Review feedback on #1741: - Clearing the pending enrollment is not the same as the session being usable again. AC3.1, the collision-cleanup decision, and ADR decision 5 now say that an incoming-key collision leaves the session usable, while a collision on a recorded broker key still held in authority keeps engine builds refused until the operator corrects the configuration. - No upgrade compatibility work is required. Drop the older-binary re-save caveat; sessions without provenance are refused and a fresh session is the recovery path. Relates to #1740. Co-Authored-By: mecatl <noreply@mecatl.dev>
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>
|
Thanks, Joe. We went with option 2: exact replacement across restart, using durable provenance. Option 1 fixes the clobbering, but only until the process restarts. After a restart, a new enrollment has no record of which authority entries were broker tools. It can then either keep the old broker names (they go stale and may later match a different tool) or drop authority it can't identify. Clear and fork have the same gap. Their successors carry the authority but not the knowledge of which names came from the broker. The plan (now ADR 0358 after renumbering on rebase) keeps the durable part small:
On the duplicate-key edge case: a collision between a broker tool and a non-broker tool rejects the whole enrollment. There is no precedence rule. The error names only the conflicting key, and the pending enrollment is cleared. JAORMX approved the plan with that scope. The implementation is in #1809, stacked on this PR. |
Co-Authored-By: Mecatl <noreply@mecatl.dev>
Co-Authored-By: Mecatl <noreply@mecatl.dev>
Revise the proposed plan and ADR 0350 after tracing the implementation: - Name the engine API: add Session.WorkspaceEnrollmentBrokerKeys and Session.RestoreWorkspaceEnrollmentBrokerKeys, and classify the changed CompleteWorkspaceEnrollment semantics as a breaking change. - Specify how the ledger and its presence bit are encoded in sessnap and eventsource, the key bounds, and validation of raw JSON before decoding. - Add an authority-boundary section: no supported journey lets a child session enroll, so enrollment cannot widen narrowed child authority. - Keep a previously recorded broker key excluded when carried authority no longer holds it; admit keys the ledger has never seen. - Separate restart from successors: a restart keeps the ledger but no broker runtime; clear and fork copy the ledger with authority and start a fresh attachment. - Refuse legacy provenance before any reopen, persist, reset, discovery, or consent side effect. Relates to #1740. Co-Authored-By: mecatl <noreply@mecatl.dev>
A review of the spike implementation found gaps in the contract itself. Record the human decisions of 2026-09-23: - Authority and the broker-key ledger are always set together. Root creation records exactly the broker keys it grants; clear, fork, and CreateSession with a source session copy the source ledger. session.New starts with absent provenance, so a path that forgets the ledger refuses enrollment instead of keeping stale broker keys. The restore method is write-once. - Excluded broker keys stay in the ledger (new ledger = completed bundle plus prior excluded keys), so an exclusion survives a refresh that omits the key. Exceeding the ledger bounds fails enrollment without mutation. - A recorded broker key still held in authority that a non-broker tool now owns fails every engine build (rehydrate, successor, enrollment) with a failed-precondition error naming only the key. Keys that are only excluded never trigger this. - An enrollment-time collision or ledger overflow clears and persists the pending enrollment, so the session accepts prompts again. The acceptance criteria follow: AC1.1 covers creation-time keys, new AC1.4 covers exclusion memory, AC2.1 gains the collision exception, AC2.3 covers carryover, and AC3.1 requires an end-to-end proof that pending is cleared. The compatibility section notes that an older binary re-saving a snapshot drops the ledger. Relates to #1740. Co-Authored-By: mecatl <noreply@mecatl.dev>
Main now has its own ADRs 0350-0355, so the workspace enrollment authority decision record becomes ADR 0356. The plan, the ADR index, and ADR 0335's supersession note follow. #1673 added direct MCP refresh (ADR 0355), a separate add-only authority path through Session.GrantToolAuthority. Name it in the plan's authority boundary: it refuses broker-bound sessions and broker mode clears the global MCP inputs, so the two paths never act on the same session. Add AC1.5 pinning that refusal. In the ADR, link ADR 0355 and record why its union-and-availability model is rejected for broker keys. Relates to #1740. Co-Authored-By: mecatl <noreply@mecatl.dev>
Main now has its own ADRs 0356 and 0357, so the workspace enrollment authority decision record becomes ADR 0358. Review feedback on #1741: - Clearing the pending enrollment is not the same as the session being usable again. AC3.1, the collision-cleanup decision, and ADR decision 5 now say that an incoming-key collision leaves the session usable, while a collision on a recorded broker key still held in authority keeps engine builds refused until the operator corrects the configuration. - No upgrade compatibility work is required. Drop the older-binary re-save caveat; sessions without provenance are refused and a fresh session is the recovery path. Relates to #1740. Co-Authored-By: mecatl <noreply@mecatl.dev>
4e7142f to
68be96d
Compare
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>
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>
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>
Plan / Interface
Relates to #1740.
This plan records the authority repair contract before implementation:
It proposes ADR 0358, which supersedes only ADR 0335's no-refresh-specific-persisted-state clause.
Verification
bash .claude/skills/to-acceptance-plan/scripts/check-acceptance-plan.sh docs/acceptance/workspace-enrollment-authority.mdbash .claude/skills/to-acceptance-plan/scripts/check-acceptance-plan-test.shtask docsThe plan requires human merge before
/plan-orchestrateimplements it.Revisions
mainafter feat(mcp): reconcile sources and refresh session tools #1673 (direct MCP source reconciliation, ADR 0355). The ADR is renumbered from 0350 to 0358 because main now has its own ADRs 0350-0357.RefreshMcpSources/Session.GrantToolAuthority) as a separate add-only path that refuses broker-bound sessions, adds AC1.5 pinning that refusal, and the ADR records why ADR 0355's union-and-availability model is rejected for broker keys.