fix: let agent keys send hooks without project:read
Why
An agent key holding org:hooks_ingest on a project got 403 when sending hooks there. Resolving Gram-Project also ran the generic project:read gate, which org:hooks_ingest does not imply. Sending hooks should depend only on org:hooks_ingest bounded to the project. Pairs with #8015, which gives new agents org:hooks_ingest: together, an agent key with only that grant can send hooks.
What changed
Before: every hooks route resolved its project through Authorize, so an agent needed project:read and then org:hooks_ingest.
After:
authorizeHooksProjectresolves the project for hooks ingestion. For an agent actor (an agent key, or a workload acting for an agent) it usesAuthorizeWithHandlerProjectAccess, which keeps tenant scoping and key project binding, then requires project-boundedorg:hooks_ingest. Other callers keepAuthorizeand itsproject:readgate.- Cursor, codex, OTEL, the optional-auth plugin endpoints, AHP and workloads use it. An agent on any other hooks method is refused before its project resolves.
project:readstays required everywhere else.- Route-level tests with real agent keys: admitted for project P with only the hooks grant, refused for project Q, refused without the grant.
Platform MCP
No tool change: this corrects runtime hooks ingestion by agent credentials on a selected project. Existing management tools do not send hook events or require a contract change; route-level tests cover project bounds and missing grants.
Narrated before/after demonstration
Real local integration results using the same agent-key tests against the parent and PR implementations. Before: hooks-only keys are refused. After: all eight tests pass, including wrong-project and missing-grant refusal. The recording presents captured Go test output; it does not demonstrate a dashboard or OpenClaw interaction.
https://github.com/user-attachments/assets/9c847044-8f63-46f2-a1a7-3113f9fd14cb