~bigbes/sr-ht-spec

ref: cc90b4a3bd9fad3864033fc59ea2e440b4288c44 sr-ht-spec/service/merge.go -rw-r--r-- 8.6 KiB
d8a5164f — Eugene Blikh 25 days ago
feat(webhooks): fire on proposal open/merge/reject (Phase 5a)

The firing half — proposal lifecycle events now deliver GraphQL-native
webhooks. Verified end to end against a live daemon: an agent REST
propose delivers a signed POST whose body is the subscription's stored
query executed against the ProposalEvent payload.

- service: an EventSink seam (service/events.go). Propose emits
  PROPOSAL_OPENED for a new proposal, mergeProposal emits PROPOSAL_MERGED
  (the single merge point — both auto-merge and the human approve reach
  it), Reject emits PROPOSAL_REJECTED. Nil-safe; a Service with no sink
  emits nothing.
- graph.NewProposalEvent builds the *model.ProposalEvent payload from a
  service.Proposal (reusing the existing service→graph→model mapping).
- cmd webhookEventSink: proposal events happen in the service layer,
  which has none of core-go's request context, so the sink enqueues a
  dowork task onto the webhook queue. The task runs in the queue's worker
  context (server+database+config, from WithQueues), adds the owner's
  INTERNAL auth, and calls Schedule — which renders each subscriber's
  query and delivers it Ed25519-signed. Fire-and-forget off the write
  path: a webhook never blocks or fails a proposal write.

Phase 5a (webhooks) is complete: DB, the authn→AuthContext bridge, the
GraphQL surface, the core-go server wiring, and firing.
65eac3cf — Eugene Blikh 26 days ago
feat(service): write plane — Propose, Merge, ListProposals (Phase 3)

The service-layer orchestration for spec-zqb: the plane where the merge
model and the proposal state machine first run under real proposals
rather than on paper. Primitives (db proposal CRUD/merge, gitx
branch/commit/merge, authn provenance, core policy matcher) already
existed and were unit-tested; this composes them.

- Propose: the write plane, identical for REST and MCP. Row-first open
  (branch name derives from the serial id), branch cut, and a
  provenance-stamped commit — the agent authors, the owner commits, and
  the X-Agent-Session / X-Agent-Base trailers carry the rest into a
  plain git log. Adds to an existing proposal via ProposalID against its
  fixed base. Frontmatter/schema/id validation at propose time, mirroring
  the update hook the in-process agent write bypasses. Returns
  {proposal, url}.
- Auto-merge policy: a proposal whose every changed path matches the
  space's .spec.yml auto_merge lands immediately with ApprovalPolicy,
  best-effort — a stale or mixed-path proposal falls back to human review
  rather than failing the write.
- Merge / Reject: the state machine. Merge does the already-merged
  ancestry check before gitx's If-Match staleness (design: the two need
  different tests), the owner-signed merge commit, and the atomic row +
  document-registry flip. ErrStale / ErrAlreadyMerged / ErrForbidden map
  the 409/403 boundary.
- ListProposals / GetProposal / ProposalURL: the space-scoped read the
  graph Proposals port declared and left nil, plus the stable
  <origin>/~owner/space/p/<id> link.
- db.ListProposalsBySpace: the per-space, per-state listing.

Tested end to end against Postgres: open, auto-merge, mixed-path
fallback, stale-base 409, human merge, reject, add-to-existing, drifted
base. Surfaces (graph wiring, spec_propose, REST api/) fan out next.