feat(cmd,graph): wire /query onto core-go's server for webhooks (Phase 5a) The faithful runtime wiring. /query moves from spec's anon-router handler onto core-go's authenticated router, so the webhook engine gets the auth/database/server context it requires. - cmd: build the executable schema via graph.NewSchema and hand it to both webhooks.NewQueue and server.WithSchema (one schema, both). The server is coreserver.New().WithDefaultMiddleware() (core-go auth + database + server context + the delivery worker via WithQueues). web, MCP and REST stay on the anon router with spec's own authn — only /query changes. The owner "user" row is seeded at startup so core-go's LookupUser stays local. - ownerOnly middleware on /query: 403s any non-owner (core-go auth admits any meta user; spec is single-owner) and remaps the owner to AUTH_INTERNAL so the webhook engine's NewAuthConfig/FilterWebhooks (which refuse AUTH_COOKIE) accept them. - graph: NewSchema exposes the raw executable schema; the webhook resolver ACL now requires AUTH_INTERNAL (only the owner gets it, via ownerOnly) instead of spec's authn, which is no longer in the /query chain. Accepted trade-off: agents lose GraphQL /query reads (they keep MCP + REST). New deploy requirement: WithDefaultMiddleware needs [mail] smtp-from (core-go's notification queue). Verified against a live daemon on Postgres: owner cookie creates and lists webhooks (row stored INTERNAL/user_id 1); non-owner 403; unauth 401; web UI 200 on the anon router.
feat(db): webhook + user tables (Phase 5a foundation) The DB foundation for GraphQL-native webhooks, matching the core-go / pages.sr.ht convention: - "user" table — spec is single-owner, but the webhook subscription is user-scoped in core-go's model, so a user row is the owner's identity and the FK target. Columns mirror core-go auth.LookupUser so the table is ready if the service ever adopts core-go auth; today only id/username are used (owner seeded at startup). - webhook_event enum (PROPOSAL_OPENED/MERGED/REJECTED), auth_method enum. - gql_user_wh_sub / gql_user_wh_delivery — column names match core-go's webhooks engine exactly (it selects/inserts by these names). The events check uses cardinality() not array_length() so an empty event list is rejected at the DB (array_length returns NULL there, which a CHECK does not reject — a latent gap in the upstream convention). Webhook tables use bare `timestamp` (not spec's TIMESTAMPTZ) to match core-go's `NOW() at time zone 'utc'` inserts. Verified: schema.sql and the 0003 migration both apply and reverse cleanly; FK/check/cascade all behave.