defineService froze the procedures map but not each procedure inside it, so a
ProcedureDef built by hand rather than through procedure.build() stayed
mutable: svc.procedures.foo.permission = 'hacked' silently succeeded. The
contract is shared between two apps as a single source of truth, and the
guarantee rested on every call site remembering to use the builder.
Same class as the authz catalog's frozenMap, which froze the Map's mutators
but not the values it handed out.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>