feat(discord): SetSpark record client for the Discord Sage, fixed verbs against setspark-api, connector-verified approvals (#1509)
Row 25, parts 2a and 2b, against the shared-signals contract a5425a2. Model side: eight fixed verbs in the pi extension (record_list, record_get, record_create, record_update, resolve_id, open_approval_request, get_approval_request, create_document), each one HTTP call with arguments checked before any request. Writes carry an idempotency key <principal>:<message id>:<call index> and an audit context. The seat key is read from a 0600 file on every call and never cached, printed or journaled. Connector side: append-only approval ledger, Approve button and exact "approve" reply resolved by the connector against the required approvers, confirmation message posted as button evidence, bind and add_approval through the service under connector keys, retry of unknown entries on start. Evidence: node tests 162 pass, scripts/test-discord.sh 63/63. Review by rev-code-02, round 1 approved (#1509 comment 26467, tree 7872d8c5). Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -2553,3 +2553,56 @@ The binding change and the live check follow the local commit; the
|
||||
binding stays private. Jason's shared-signals clone holds his own
|
||||
uncommitted files; explicit-path staging leaves them alone, and Sage
|
||||
cannot commit while his index holds staged work.
|
||||
|
||||
## 2026-09-20 — Discord SetSpark record client (#1509, QUEUE row 25)
|
||||
|
||||
Before: the Discord Sage's records lived in files under the shared-signals
|
||||
git root, written with `write_file` and committed with `git_commit`; an
|
||||
approval was a word in chat. After: record authority sits in the SetSpark
|
||||
record service (`setspark-api`, NocoDB and Outline behind it, plan v3.2 at
|
||||
shared-signals 55b2515, contract `stack/api/openapi.json` at a5425a2). A
|
||||
binding's tools may carry a `setspark` key (`baseUrl`, `keyFile`,
|
||||
`principal`); the extension then offers eight fixed verbs, each one HTTP
|
||||
call in `packages/discord/src/setspark.mjs`: `record_list`, `record_get`,
|
||||
`record_create`, `record_update`, `resolve_id`, `open_approval_request`,
|
||||
`get_approval_request`, `create_document`. The api key is re-read from a
|
||||
0600 file on every call (one bare line, or the mint's JSON) and exists
|
||||
only in the Authorization header. Every write carries an idempotency key
|
||||
`<principal>:<message id>:<call index>`; a write outside a turn is refused
|
||||
before any request. Arguments are checked in the client; a stale
|
||||
`record_update` renders the current revision and the changed fields.
|
||||
|
||||
Approvals are the connector's, never the model's. A successful
|
||||
`open_approval_request` in a turn makes the connector post the proposal
|
||||
with an Approve button after the reply, journal it to `approvals.jsonl`,
|
||||
and bind the message id to the request at the service under
|
||||
`<principal>:<message id>:bind`. A listed approver's button press or exact
|
||||
`approve` reply is submitted under `<principal>:<event id>:approval` with
|
||||
one Discord message as evidence: the reply itself, or for a press the
|
||||
confirmation line the connector posts first (the service keeps
|
||||
`source_url` unique per approval; the SetSpark coordinator ruled this on
|
||||
2026-09-20). Anyone else gets one fixed line and a drop entry. Start
|
||||
retries intent and unknown binds and approvals with the ledger's stored
|
||||
evidence. The envelope now carries the author id and the message id, read
|
||||
by the extension for the write keys and the audit `context`.
|
||||
|
||||
Contract work: three server operations the plan's prose folded into one
|
||||
(open without a message, bind a message, add an approval with the bound
|
||||
message id) were sent to the coordinator and adopted, with a
|
||||
`request_stale` code and `kind: button|reply` on add_approval.
|
||||
|
||||
Evidence: `scripts/test-discord.sh` 63/63 (`/tmp/suites/test-discord-row25.log`),
|
||||
node tests 162 (`tests/setspark.test.mjs` 11, `tests/approvals.test.mjs`
|
||||
8, `tests/context.test.mjs` extended), the real-pi probe with a setspark
|
||||
key listing the reads and the eight verbs with no key exposure. Review:
|
||||
rev-code-02 APPROVED round 1 on 2026-09-22T17:58:03Z (#1509 comment 26467). Candidate: 20 paths, aggregate 09140edc, tree
|
||||
7872d8c52e60b1c1ad9293d32b2c6664e1f50896.
|
||||
|
||||
Correction recorded: during the doc checks a stray `git stash -q` stashed
|
||||
the whole working tree, other sessions' files included; it was popped
|
||||
within the minute, all 44 modified files returned, and the node tests and
|
||||
the frozen hashes were re-verified afterwards. No file was lost.
|
||||
|
||||
Not done: the private binding change (keyFile at Sage's minted key), the
|
||||
live check in #sage-admin, the push. Outline collections are empty, so
|
||||
`create_document` answers 503 until BizOps creates them.
|
||||
|
||||
Reference in New Issue
Block a user