diff --git a/src/commands/cli.py b/src/commands/cli.py
index 481aea67..b27b2b58 100644
--- a/src/commands/cli.py
+++ b/src/commands/cli.py
@@ -1,4 +1,3 @@
-import inspect
 from contextlib import nullcontext
 import io
 import os
@@ -10,16 +9,15 @@ from controllers.types import CONTROLLERS
 import features
 import migrations
 from providers import DRIVERS, workspace_folders
-from engine import bus
 from engine.record import Record
 from engine.sessions import Sessions, allowed
 from resources.base import OWNER, Refused
 from resources.shapes import typed
-from engine import runtime
+from commands.invoke import invoked
 from commands.parser import PRINTED, QUERIES, Misused, parser, words
 from features.command_line import CommandLine, wire
-from engine.stored import undoable
-from engine.paths import environments
+from engine.timing import measured
+from engine.binding import bound_environment
 from engine.worktree import checkout
 from typing import TypedDict
 
@@ -37,18 +35,20 @@ class CommandContext(TypedDict):
     sessions: Sessions
 
 
-def context(args: dict) -> CommandContext:
-    root = Path(args.pop("root")).resolve()
+def bootstrap(root: Path) -> None:
     if root not in MIGRATED:
         migrations.run(root)
         MIGRATED.add(root)
     features.load(root)
+
+
+def context(args: dict) -> CommandContext:
+    root = Path(args.pop("root")).resolve()
+    bootstrap(root)
     sessions = Sessions(root)
     session = args.pop("as_session")
-    fallback = runtime.renamed(root, args.pop("fallback"))
     top = checkout(Path(args.pop("cwd") or os.getcwd()), workspace_folders())
-    worked = top.name if top and (environments(root) / top.name).is_dir() else ""
-    env = args.pop("bound") or (sessions.environment(session) if session else "") or worked or fallback or runtime.env(root)
+    env = bound_environment(root, sessions, session, args.pop("bound"), top, args.pop("fallback"))
     session = session or sessions.holder(env)
     return {"record": Record(root, env, memo=True), "session": session, "actor": args.pop("as_actor"), "agent": args.pop("as_agent"),
             "plugin": args.pop("as_plugin"), "force": "", "sessions": sessions}
@@ -63,20 +63,6 @@ def served() -> frozenset:
     return frozenset(CONTROLLERS) - LOCAL
 
 
-def invoke(fn, args: dict, extra: dict):
-    params = list(inspect.signature(fn).parameters.values())
-    named = {p.name for p in params if p.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY)}
-    for key in [key for key in extra if key in named and args.get(key) is None]:
-        args[key] = extra.pop(key)
-    at = next((i for i, p in enumerate(params) if p.kind is inspect.Parameter.VAR_POSITIONAL), None)
-    positional = []
-    if at is not None:
-        positional = [args.pop(p.name) for p in params[:at] if p.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)]
-        positional.extend(args.pop(params[at].name))
-    with undoable():
-        return fn(*positional, **args, **extra)
-
-
 TAKES = {"--root", "--env", "--default-env", "--as", "--session", "--agent", "--force", "--cwd", "--plugin"}
 
 
@@ -153,7 +139,7 @@ def run(argv: list[str], out=None, err=None) -> int:
             print(query({**ctx, **args}), file=out)
             return 0
         method = args.pop("method")
-        args.pop("action", None)
+        word = args.pop("action")
         extra = {k: typed(v) for k, v in (kv.split("=", 1) for kv in args.pop("set", []))}
         if ctx["plugin"] and method == "create":
             extra = {OWNER: ctx["plugin"], "locked": True, **extra}
@@ -162,9 +148,8 @@ def run(argv: list[str], out=None, err=None) -> int:
             if why:
                 raise Refused(why)
         controller = CONTROLLERS[command](ctx["record"], actor=ctx["actor"], session=ctx["session"], agent=ctx["agent"], force=ctx["force"])
-        faults = features.FEATURES.get("dev_faults")
-        with bus.settled(), faults.reports.watched(ctx["record"].root, ctx["record"].env, "command", f"{command} {method}") if faults and not networked(command, method) else nullcontext():
-            got = invoke(controller.action(method), args, extra)
+        with measured(ctx["record"].root, ctx["record"].env, "command", f"{command} {method}") if not networked(command, method) else nullcontext():
+            got = invoked(controller, word, named=args, extra=extra)
     except Refused as e:
         print(f"! {e}", file=err)
         return 1
diff --git a/src/commands/dispatch.py b/src/commands/dispatch.py
index 16b2fc74..c5843cdd 100644
--- a/src/commands/dispatch.py
+++ b/src/commands/dispatch.py
@@ -1,19 +1,17 @@
 import json
 import mimetypes
 import re
-import time
 from dataclasses import dataclass, field
 from pathlib import Path
 from typing import Callable, Iterator
 from urllib.parse import unquote
-import features
 from controllers.types import CONTROLLERS
 from engine import bus, runtime
-from engine.collecting import collecting
+from engine.timing import Stopwatch, profiler
 from engine.record import Record
 from controllers.faults import threw
 from features.format import shaped
-from resources.base import USER, Refused
+from resources.base import USER, Missing, Refused
 from engine.package import data
 from engine.fields import Loaded
 from engine.paths import contained, environment_home
@@ -79,19 +77,17 @@ class Reply:
         return self.body.encode() if self.kind == PLAIN else json.dumps(self.body).encode()
 
 
-class Missing(Exception):
-    pass
-
-
 @dataclass
 class Route:
     method: str
     pattern: str
     handler: Callable[[Request], Reply]
     regex: re.Pattern = field(init=False)
+    rank: tuple[bool, ...] = field(init=False)
 
     def __post_init__(self):
         self.regex = re.compile("^" + re.sub(r"{(\w+)}", r"(?P<\1>[^/]+)", self.pattern) + "$")
+        self.rank = tuple(segment.startswith("{") for segment in self.pattern.split("/"))
 
 
 ROUTES: list[Route] = []
@@ -99,6 +95,7 @@ ROUTES: list[Route] = []
 def route(method: str, pattern: str):
     def register(fn):
         ROUTES.append(Route(method, pattern, fn))
+        ROUTES.sort(key=lambda r: r.rank)
         return fn
     return register
 
@@ -122,15 +119,11 @@ def later(reply: Reply, then) -> Reply:
     return reply
 
 
-def timed(reply: Reply, root: Path, env: str, method: str, path: str, began: tuple, profile=None) -> Reply:
-    faults = features.FEATURES.get("dev_faults")
-    if not faults or not reply.timed:
+def timed(reply: Reply, root: Path, env: str, method: str, path: str, began: Stopwatch, profile=None) -> Reply:
+    if not reply.timed:
         return reply
-    took = (time.perf_counter() - began[0]) * 1000
-    working = (time.thread_time() - began[1]) * 1000
-    garbage = (collecting() - began[2]) * 1000
     name = f"{method} {path}" if reply.named is None else f"{method} {path} ({reply.named})"
-    return later(reply, lambda: faults.reports.spent(root, env, "hook" if "/hook/" in path else "request", name, took, working, profile, garbage))
+    return later(reply, lambda: began.announce(root, env, "hook" if "/hook/" in path else "request", name, profile))
 
 
 def known_environment(root: Path, env: str) -> bool:
@@ -152,9 +145,8 @@ def dispatch(method: str, path: str, root: Path, query: dict, body: dict) -> Rep
     r, params = found
     if method == "GET" and "env" in params and not known_environment(root, params["env"]):
         return Reply(404, {"error": f"no environment {params['env']}"})
-    faults = features.FEATURES.get("dev_faults")
-    profile = faults.reports.profiler(root) if faults else None
-    began = (time.perf_counter(), time.thread_time(), collecting())
+    profile = profiler(root)
+    began = Stopwatch()
     req = Request(root, params, query, body)
     try:
         with bus.held() as queued:
@@ -174,9 +166,6 @@ def dispatch(method: str, path: str, root: Path, query: dict, body: dict) -> Rep
         return Reply(404, {"error": str(e)})
     except Refused as e:
         return Reply(400, {"error": str(e)})
-    except (TypeError, AttributeError) as e:
-        threw(root, req.env, f"{method} {path}")
-        return Reply(400, {"error": f"not an action here: {e}"})
     except Exception as e:
         threw(root, req.env, f"{method} {path}")
         return Reply(500, {"error": f"{type(e).__name__}: {e}"})
diff --git a/src/controllers/base.py b/src/controllers/base.py
index d1801020..5b9ea478 100644
--- a/src/controllers/base.py
+++ b/src/controllers/base.py
@@ -48,6 +48,18 @@ def actions(controller: type) -> tuple[str, ...]:
                   if not name.startswith("_") and not getattr(f, "internal", False)))
 
 
+def word_names(controller: type) -> set[str]:
+    return {*actions(controller), *COMMANDS.get(controller.resource.type, {})}
+
+
+def word_function(controller: type, name: str):
+    return COMMANDS.get(controller.resource.type, {}).get(name) or getattr(controller, name)
+
+
+def word_parameters(controller: type, name: str) -> list[inspect.Parameter]:
+    return list(inspect.signature(word_function(controller, name)).parameters.values())[1:]
+
+
 class Controller(Stored, Files, Links, Discussed):
     resource = Resource
     actor = "user"
@@ -282,16 +294,13 @@ class Controller(Stored, Files, Links, Discussed):
 
     @internal
     def method(self, name: str):
-        for method, alias in self.resource.command_names.items():
-            if alias == name:
-                return getattr(self, method)
-        if name in self.resource.command_names:
+        if name in self.resource.command_names and name not in self.resource.command_names.values():
             raise Refused(f"a {self.type} calls that {self.resource.command_names[name]}")
         return self.action(name)
 
     @internal
     def action(self, name: str):
-        if name.startswith("_") or name not in {*actions(type(self)), *COMMANDS.get(self.type, {}), *self.resource.command_names.values()}:
+        if name.startswith("_") or name not in {*word_names(type(self)), *self.resource.command_names.values()}:
             raise Refused(f"{self.type} has no action {name!r}")
         command = COMMANDS.get(self.type, {}).get(name)
         if command:
diff --git a/src/engine/record.py b/src/engine/record.py
index cf95a87d..78a332e0 100644
--- a/src/engine/record.py
+++ b/src/engine/record.py
@@ -3,9 +3,11 @@ import os
 import threading
 import time
 from contextlib import contextmanager
+from functools import partial
 from pathlib import Path
+from typing import Callable
 
-from engine import bus, runtime
+from engine import bus, runtime, waits
 from engine.event_log import EventLog
 from engine.settings_file import SettingsFile
 from resources.base import ACTIONS, ACTORS, PROJECT, SYSTEM, Event
@@ -53,9 +55,10 @@ class Record:
         self.root = Path(root)
         self.env = env
         self.home = environment_home(self.root, env)
-        self.home.mkdir(parents=True, exist_ok=True)
         self._held: dict[Path, int] = {}
-        self._threads = threading.RLock()
+        self._threads = waits.Lock("record", threading.RLock())
+        self._depth = 0
+        self._pending: list[Callable[[], None]] = []
         self.memo = {} if memo else None
         self._made: set[Path] = set()
         self.event_log = EventLog(self.home, self.locked)
@@ -75,23 +78,40 @@ class Record:
     @contextmanager
     def locked(self, scope: str = ""):
         path = (self.root / RESOURCES if scope == PROJECT else self.home) / ".lock"
-        with self._threads:
-            if self._held.get(path):
-                self._held[path] += 1
+        pending: list[Callable[[], None]] = []
+        try:
+            with self._threads:
+                self._depth += 1
                 try:
-                    yield
+                    with self._flocked(path):
+                        yield
                 finally:
-                    self._held[path] -= 1
-                return
-            path.parent.mkdir(parents=True, exist_ok=True)
-            with path.open("a+") as fh:
+                    self._depth -= 1
+                    if not self._depth:
+                        pending, self._pending = self._pending, []
+        finally:
+            for announce in pending:
+                announce()
+
+    @contextmanager
+    def _flocked(self, path: Path):
+        if self._held.get(path):
+            self._held[path] += 1
+            try:
+                yield
+            finally:
+                self._held[path] -= 1
+            return
+        path.parent.mkdir(parents=True, exist_ok=True)
+        with path.open("a+") as fh:
+            with waits.waited("record"):
                 fcntl.flock(fh, fcntl.LOCK_EX)
-                self._held[path] = 1
-                try:
-                    yield
-                finally:
-                    self._held[path] = 0
-                    fcntl.flock(fh, fcntl.LOCK_UN)
+            self._held[path] = 1
+            try:
+                yield
+            finally:
+                self._held[path] = 0
+                fcntl.flock(fh, fcntl.LOCK_UN)
 
     def emit(self, type: str, n: int, action: str, actor: str, quiet: bool = False, **data) -> Event:
         if action not in ACTIONS or actor not in ACTORS:
@@ -107,12 +127,9 @@ class Record:
             with self.locked():
                 e = stamped(self.event_log.last_id() + 1, quiet or bus.listening())
                 self.event_log.append(e)
-            if self.memo is not None:
-                self.memo.clear()
-            if quiet:
-                bus.tell_watchers(e, self)
-            else:
-                bus.emit(e, self)
+                self._pending.append(partial(bus.tell_watchers if quiet else bus.emit, e, self))
+                if self.memo is not None:
+                    self.memo.clear()
             return e
         if held_back(release):
             if self.memo is not None:
@@ -120,30 +137,6 @@ class Record:
             return stamped(0)
         return release()
 
-    def events(self, since: int = 0, last: int = 0) -> list[Event]:
-        return self.event_log.events(since, last)
-
-    def recent_events(self) -> list[Event]:
-        return self.event_log.recent()
-
-    def last_event(self) -> int:
-        return self.event_log.last_id()
-
-    def trim_events(self, keep: int, readers_since: float) -> int:
-        return self.event_log.trim(keep, readers_since)
-
-    def cursor_text(self, name: str) -> str:
-        return self.event_log.cursor_text(name)
-
-    def set_cursor_text(self, name: str, text: str) -> None:
-        self.event_log.set_cursor_text(name, text)
-
-    def cursor(self, name: str) -> int:
-        return self.event_log.cursor(name)
-
-    def set_cursor(self, name: str, n: int) -> None:
-        self.event_log.set_cursor(name, n)
-
     def state(self, owner: str, session: str | None = None) -> State:
         folder = self.home / "state" if session is None else runtime.sessions(self.root) / session
         return State(folder / f"{owner}.json")
@@ -159,9 +152,6 @@ class Record:
             self.memo["settings"] = found
         return found
 
-    def settings_version(self) -> int:
-        return self.settings_file.held()[0]
-
     def reread_settings(self) -> None:
         self.settings_file.reread()
         if self.memo is not None:
diff --git a/src/engine/stored.py b/src/engine/stored.py
index 90f4fb65..b62a5d8d 100644
--- a/src/engine/stored.py
+++ b/src/engine/stored.py
@@ -6,7 +6,9 @@ import time
 from contextlib import contextmanager
 from contextvars import ContextVar
 from pathlib import Path
-from typing import Any, Callable, TypeVar
+from typing import IO, Any, Callable, TypeVar
+
+from engine import waits
 
 UNDO = threading.local()
 MIGRATIONS = ContextVar("migrations", default=())
@@ -99,19 +101,41 @@ def journal_roots(path: Path) -> tuple[Path, ...]:
     return tuple(parent for parent in path.parents if parent.name == ".journal")
 
 
-SHARED: dict[Path, tuple] = {}
+class SharedWrites:
+    def __init__(self, held: IO):
+        self.held = held
+        self.guard = waits.Lock("writes")
+        self.writers = 0
+
+    @contextmanager
+    def joined(self):
+        with self.guard:
+            if not self.writers:
+                with waits.waited("writes"):
+                    acquire(self.held, fcntl.LOCK_SH)
+            self.writers += 1
+        try:
+            yield
+        finally:
+            with self.guard:
+                self.writers -= 1
+                if not self.writers:
+                    fcntl.flock(self.held, fcntl.LOCK_UN)
+
+
+SHARED: dict[Path, SharedWrites] = {}
 SHARING = threading.Lock()
 
 
-def shared_lock(root: Path) -> tuple:
+def shared_writes(root: Path) -> SharedWrites:
     with SHARING:
         if root not in SHARED:
             root.mkdir(parents=True, exist_ok=True)
-            SHARED[root] = ((root / MIGRATION_LOCK).open("a"), threading.Lock())
+            SHARED[root] = SharedWrites((root / MIGRATION_LOCK).open("a"))
         return SHARED[root]
 
 
-def waited(held, operation: int) -> None:
+def acquire(held, operation: int) -> None:
     deadline = time.monotonic() + LOCK_WAIT
     while True:
         try:
@@ -128,7 +152,7 @@ def hold_record_writes(root: Path):
     root = Path(root)
     root.mkdir(parents=True, exist_ok=True)
     with (root / MIGRATION_LOCK).open("a") as held:
-        waited(held, fcntl.LOCK_EX)
+        acquire(held, fcntl.LOCK_EX)
         token = MIGRATIONS.set((*MIGRATIONS.get(), root))
         try:
             yield
@@ -147,13 +171,8 @@ def writing(path: Path):
     if not guarded(path, roots):
         yield
         return
-    held, guard = shared_lock(roots[0])
-    with guard:
-        waited(held, fcntl.LOCK_SH)
-        try:
-            yield
-        finally:
-            fcntl.flock(held, fcntl.LOCK_UN)
+    with shared_writes(roots[0]).joined():
+        yield
 
 
 def write_text(path: Path, text: str) -> None:
