Authority model spec + remove willow-channel crate#142
Merged
intendednull merged 13 commits intoApr 12, 2026
Merged
Conversation
Closes #132. Adds docs/specs/authority-model.md documenting that willow-state::apply_unchecked is the sole authority boundary, with permission tier table, catch-all variant inventory, and checklists for adding new permissions and event kinds. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
The old name implied no checks were performed, but this function is the sole authority boundary where all permission checks happen. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Replace the single authority-model spec with two focused specs: - state-authority-and-mutations: defines willow-state as sole source of truth, permission tiers, pre-check before event creation to prevent rejected events from growing the DAG, and checklists - willow-channel-removal: migration plan to delete the legacy crate and consolidate all types into willow-state https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
- Add plan for state authority pre-check (extract check_permission, add PermissionDenied to InsertError, pre-check in create_and_insert) - Add plan for willow-channel removal (8 steps: move ChannelKind, move invite types, rewrite mutations, remove Server from client, rewrite join path, rewrite tests, delete crate, update docs) - Patch channel removal spec with audit findings: Channel struct field differences, test breakage inventory, Cargo.toml glob pattern https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Authority plan: note catch-all annotation is already in place. Channel removal plan: add step ordering diagram, serde backward compat for ChannelKind (alias attributes), genesis_author in invite signature, servers.rs as step 3e, explicit step dependencies, parse_permission call site, join path approach decision, fuller test enumeration. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Extract check_permission() from apply_event() so callers can verify authorization before signing an event. Add PermissionDenied variant to InsertError. ManagedDag::create_and_insert() now rejects before signing if the author lacks the required permission — no sequence number is advanced and no event enters the DAG. Implements docs/specs/2026-04-12-state-authority-and-mutations.md. 10 new tests: check_permission tier coverage (admin propose, non-admin reject, granted permission, missing permission, admin implicit pass, unrestricted events, admin-only reject) and create_and_insert pre-check (rejection, seq stability, success with grant). https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Move channel kind from String to a proper enum in willow-state::types.
Uses serde aliases ("text"/"voice") for backward compatibility with
existing serialized events. Update all 22 files across the workspace:
state, client, web, worker, replay, agent, common.
Step 1 of the willow-channel removal plan.
https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Change generate_invite() to take raw data (server_name, server_id, genesis_author, keys, topic_map) instead of &Server. Rewrite all 6 invite tests to construct keys directly instead of via Server::new(). Update 3 call sites (joining.rs, listeners.rs, lib.rs test). Step 2 of the willow-channel removal plan. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Remove Server from ServerEntry, replacing it with server_id + name. Delete topic_map (derived from ServerState.channels on demand). Rewrite all mutations to use event pipeline only — no more dual Server+event mutation. Rewrite invite join path to build ServerEntry directly from invite data. Rewrite test_client() to use ManagedDag exclusively. Delete reconcile_topic_map(). Replace parse_permission to use willow_state::Permission. 14 files changed, net -214 lines removed. Zero willow_channel references remain in crate source. Steps 3-6 of the willow-channel removal plan. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Remove crates/channel/ entirely and drop the dependency from willow-client/Cargo.toml. The workspace uses members = ["crates/*"] so deleting the directory is sufficient. Step 7 of the willow-channel removal plan. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
Remove willow-channel from repository structure, dependency graph, and "Adding a new permission" checklist. Update permission checklist to reference willow-state paths. Step 8 of the willow-channel removal plan. https://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #132. Addresses #118, #109.
docs/specs/2026-04-12-state-authority-and-mutations.md): documentswillow-stateas the single source of truth, permission tiers, event flow, therequired_permission()catch-all safety, and checklists for adding new permissions/event kindscheck_permission()fromapply_event(), addInsertError::PermissionDenied, gatecreate_and_insert()so rejected events never enter the DAG or advance the sequence numberwillow-channelentirely: delete the crate (-1,188 lines), consolidateChannelKindenum intowillow-state, decouple invite system, rewrite all client mutations to use the event pipeline exclusively, remove the dualServer+ServerStaterepresentationKey changes
crates/state/src/materialize.rscheck_permission()public function;apply_event()calls it;required_permission()catch-all has exhaustive variant commentcrates/state/src/managed.rscreate_and_insert()pre-checks permissions before signingcrates/state/src/dag.rsInsertError::PermissionDeniedvariantcrates/state/src/types.rsChannelKindenum with serde aliases for backward compatcrates/client/(14 files)willow_channel::ServerfromServerEntry, rewrite mutations to event-only, decouple invite, rewrite testscrates/channel/CLAUDE.mdNet result
43 files changed, +1,547 / -1,892 lines. ~1,400 lines of legacy code removed.
Test plan
cargo test -p willow-state— 166 tests pass (10 new forcheck_permission+ pre-check)cargo test -p willow-client— 68 tests pass (invite tests rewritten,test_client()usesManagedDagexclusively)cargo clippy --workspace --tests— zero warningscargo check --workspace— clean compilationwillow_channelreferences in source or Cargo.tomlhttps://claude.ai/code/session_01RpQAhYK9mVQrNB3nzTayjE