Modelled on the hook:* family rather than on geolocation:read. The host dispatches INTO the child for every tool call, so it needs what HOOK_PERMISSION buys: providersOf()'s active-and-holds-the-grant check, the second gate in invokeHook, and the acting-user binding. That last one is the point. The user is bound host-side from the supervisor's invocation map, so a tool called by user A reaches A's data whatever the model puts in args. The permission keeps no hook: prefix because the surface it opens is MCP, not a TREK render slot, and nothing keys off that prefix. It is the first HOOK_PERMISSION value shaped that way; `satisfies` still holds. One commit, because it cannot be split. requireTotalCoverage makes a HOOK_PERMISSION key with no @PluginHook consumer a boot failure, not a test failure, and PluginRpcRegistryService.onModuleInit runs in every buildApp() — so a half-landed version breaks every integration test in the repo. The generator is chained to it too: it refuses to run without both the en admin label and the CLI hint. 15s is the longest budget of any read hook. The others back a render someone is waiting on and fall back when they time out; this one backs a chat turn, and the plugin is likely calling a third-party API of its own. Still under the supervisor's unused 30s default, which is a hung client rather than a budget. One callTool fn for every tool, since tool names are runtime data and hookContracts() is keyed by (hook, fn). PluginDefinition exists in two copies with no generator between them, so both get McpToolProvider. grantGaps and the dev mock host need no change: both read HOOK_PERMISSION, which is generated. PLUGHOOK-010, plus the CONTRACTS row.
@trek/shared
Single source of truth for TREK's API contracts, expressed as Zod schemas and consumed by both the server (request validation + inferred DTO types) and the client (typed requests/responses).
This package is part of the incremental NestJS + React 19 migration (see the "Brownfield Rewrite" board). It is intentionally dormant until modules start importing it — adding it changes nothing for users.
Rules
- One folder per domain:
src/<domain>/<domain>.schema.ts(+.spec.ts). - Domain-agnostic building blocks live in
src/common/. - A route is only considered migrated once its contract lives here.
- Schemas are the source of truth; server DTOs and client types are inferred from them
(
z.infer<typeof schema>), never hand-duplicated.
Consumption (dev)
Both apps resolve @trek/shared to this package's TypeScript source:
- Server (
tsx): viapathsinserver/tsconfig.json. - Client (
vite): viaresolve.aliasinclient/vite.config.ts(+pathsfor the type-checker).
Production packaging (Docker / workspace wiring) is introduced in card F2, when the server first depends on this package at runtime. Until then prod builds are untouched.
Not yet here
The canonical error envelope is finalised in card F5 (it must match TREK's current Express error responses byte-for-byte), so it is deliberately not invented in F1.