You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
chore(spec,cli): enroll webhook in the liveness GOVERNED set (#3462)
Closes the final third of #3462 (umbrella #1878). report/dashboard landed in
#3474; webhook was deferred for two reasons, both handled here.
- Not a registered metadata type: webhook is absent from the metadata-type
registry, so the gate can't resolve it via getMetadataTypeSchema. Registering
it would switch on Studio webhook CRUD + saveMetaItem overlay acceptance +
diagnostics sweeping — wrong while the authoring surface is still disconnected.
Instead the gate walks it via a small SPEC_ONLY_SCHEMAS override in
check-liveness.mts (consulted before the registry). Zero runtime blast radius.
- The whole authoring surface is dead (#3461, confirmed at HEAD): nothing
materializes an authored webhooks: entry (stack/connector) into a sys_webhook
dispatcher row; the runtime reads only admin-authored sys_webhook rows. So
liveness/webhook.json classifies all 16 authorable props dead + authentication
experimental (HMAC-secret-only, its existing marker). Per-prop notes record the
sys_webhook column map as the future materializer's mapping table (#3461 A) and
flag the object->object_name / isActive->active mismatches. Also documents the
objectql engine.ts:1183/1343 dead-end that registers webhooks: as inert
metadata items nothing reads back — the trap that makes this look connected.
- Author-warning wired (@objectstack/cli): added { type: 'webhook', key:
'webhooks' } to TYPE_COLLECTIONS so os compile now advises that webhooks: is a
silent no-op. The required url prop carries the single warning per webhook
(one heads-up per artifact, not one per dead prop); isActive left unmarked
(default(true) boolean). The showcase's TaskChangedWebhook is the live example.
Enrollment only — does not decide #3461's build-the-bridge vs retire-the-surface
question. Gate green (webhook 17 classified: dead 16, experimental 1); lint
contract test red->green; spec 6857 + cli 640 tests pass. No spec shape/behavior
change (ledger + gate/lint config only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| dashboard | 18 | 0 | 2 | – | ADR-0021 dataset widgets (WidgetConfigPanel + DashboardRenderer migrated #3251; DashboardWidgetSchema `.strict()`); dead = aria + performance (perf authorWarn'd); audit-era `globalFilters`/`dateRange` DEAD superseded — LIVE via framework#2501; `title`↔`label` fixed (objectui#2806) |
219
+
| webhook | 0 | 1 | 16 | – |**not a registered metadata type** — governed via the gate's spec-only schema override (`SPEC_ONLY_SCHEMAS`), not `getMetadataTypeSchema` (#3461/#3462). The ENTIRE authoring surface is dead: nothing materializes an authored `webhooks:` entry into a `sys_webhook` dispatcher row (#3461, enforce-or-remove pending). `url` carries the single per-webhook `authorWarn` (one no-op heads-up per artifact, not per-prop); `authentication` experimental (HMAC-`secret`-only); `isActive` unmarked (default(true)). Notes cite the sys_webhook column map as the future materializer's mapping table |
208
220
209
221
The `dead` set across types is the enforce-or-remove worklist (ADR-0049); every
210
222
misleading entry carries `authorWarn` so authors hear about it at compile time.
"_note": "WebhookSchema (outbound webhook — packages/spec/src/automation/webhook.zod.ts:76). Governed via a spec-only schema override in the gate (SPEC_ONLY_SCHEMAS): webhook is NOT a registered metadata type (absent from kernel/metadata-type-schemas.ts), on purpose — registering it would turn on Studio webhook CRUD + saveMetaItem overlay acceptance, which is the wrong move while the authoring surface is still disconnected (below). THE DISCONNECT (#3461, confirmed at HEAD): the entire authored WebhookSchema surface is inert. It's authored on a Stack (stack.zod.ts:261 `webhooks: z.array(WebhookSchema)`) and by connectors (connector.zod.ts:271), but NOTHING materializes an authored webhook into a `sys_webhook` DATA row — and the runtime dispatcher reads ONLY sys_webhook rows (AutoEnqueuer.doRefresh → engine.find('sys_webhook', { where: { active: true } }), plugins/plugin-webhooks/src/auto-enqueuer.ts:175-176). Rows are hand-created by admins via the generic object CRUD UI (sys-webhook.object.ts:45); there is no seeder and zero insert('sys_webhook') anywhere. THE TRAP: objectql DOES 'ingest' `webhooks:` (engine.ts:1183/1343) — registering each as a generic in-memory metadata item under type 'webhook' — but nothing ever reads those items back, and they are NOT the sys_webhook table. It looks like an ingestion path; it dead-ends. So every prop below is classified from the AUTHOR's vantage point (the ledger's stated purpose: an authored prop with no runtime consumer is a silent no-op), which for this surface means the whole thing: all 16 authorable props are DEAD, `authentication` stays experimental via its own marker. The per-prop notes record SALVAGEABILITY — which props have a sys_webhook column/delivery equivalent a future materializer (#3461 option A) could remap (spec `object`→`object_name`, `isActive`→`active`) vs which have no sink anywhere — so this doubles as the mapping table for that work. When the bridge lands, the mapped props flip to live (cite the materializer); if the surface is retired (#3461 option B) the ledger is removed with the schema. objectui has no bespoke consumer of the spec webhook shape. `url` carries the single author-warning per webhook (one no-op heads-up per artifact, not one per prop). Field-level line refs are the runtime sys_webhook object (plugins/plugin-webhooks/src/sys-webhook.object.ts) and its dispatcher (auto-enqueuer.ts).",
4
+
"props": {
5
+
"name": {
6
+
"status": "dead",
7
+
"note": "Authored value never materialized. A sys_webhook.name column exists (sys-webhook.object.ts:97) and the dispatcher reads row.name (auto-enqueuer.ts:266) — but it's fed by admin CRUD, not by authoring. Salvageable 1:1 by a materializer."
8
+
},
9
+
"label": {
10
+
"status": "dead",
11
+
"note": "Authored value never materialized. Runtime col sys-webhook.object.ts:105 (Studio list display only; never sent on the wire). Salvageable 1:1."
12
+
},
13
+
"object": {
14
+
"status": "dead",
15
+
"note": "Authored value never materialized. Runtime subscription keys on col `object_name` (sys-webhook.object.ts:112), read at auto-enqueuer.ts:267 — NAME MISMATCH: spec `object` vs runtime `object_name`. A materializer must remap."
16
+
},
17
+
"triggers": {
18
+
"status": "dead",
19
+
"note": "Authored value never materialized. Runtime col sys-webhook.object.ts:123, read+mapped at auto-enqueuer.ts:212 (create/update/delete; unknown values dropped with a warning, #3196). Salvageable 1:1."
20
+
},
21
+
"url": {
22
+
"status": "dead",
23
+
"authorWarn": true,
24
+
"authorHint": "Stack/connector-authored webhooks are never materialized into the sys_webhook dispatcher — nothing delivers them (#3461). Create sys_webhook rows via the admin UI (or wait for the ingestion bridge) instead of authoring `webhooks:`.",
25
+
"note": "Authored value never materialized. Runtime col sys-webhook.object.ts:137, read at auto-enqueuer.ts:269. Salvageable 1:1. Marked authorWarn as the one-per-webhook carrier: `url` is required so it warns exactly once per authored webhook, flagging the whole artifact as a no-op without spamming a warning per prop."
26
+
},
27
+
"method": {
28
+
"status": "dead",
29
+
"note": "Authored value never materialized. Runtime col sys-webhook.object.ts:145, read at auto-enqueuer.ts:274 (falls back to definition_json then 'POST'). Salvageable 1:1."
30
+
},
31
+
"headers": {
32
+
"status": "dead",
33
+
"note": "Authored value never materialized. No first-class sys_webhook column — the dispatcher reads headers only from the `definition_json` textarea of a hand-created row (auto-enqueuer.ts:275). A materializer would fold this into definition_json."
34
+
},
35
+
"body": {
36
+
"status": "dead",
37
+
"note": "No sink anywhere. The dispatcher always sends its own fixed envelope (object/recordId/action/timestamp + full record, auto-enqueuer.ts:336-342); a custom body is never read. Vestigial even for a future materializer."
38
+
},
39
+
"payloadFields": {
40
+
"status": "dead",
41
+
"note": "No sink anywhere. No field projection — the full record is always sent (auto-enqueuer.ts:341). Never read."
42
+
},
43
+
"includeSession": {
44
+
"status": "dead",
45
+
"note": "No sink anywhere. No session is injected into the enqueue payload. Never read."
46
+
},
47
+
"authentication": {
48
+
"status": "experimental",
49
+
"authorHint": "Only HMAC signing via `secret` is applied to deliveries; bearer/basic/api-key credentials are never attached to outbound requests (liveness audit #1878/#1893).",
50
+
"note": "Self-marked [EXPERIMENTAL — not enforced] on the schema (webhook.zod.ts:102); pinned here so the compile lint warns on it (marker-only classifications aren't read by the lint). At runtime the delivery path applies HMAC via `secret` only (auto-enqueuer.ts:276) — bearer/basic/api-key are inert."
51
+
},
52
+
"retryPolicy": {
53
+
"status": "dead",
54
+
"note": "No sink anywhere. Not present in EnqueueHttpInput (service-messaging/src/http-outbox.ts:87-98); the enqueuer passes no retry fields and the messaging HttpDispatcher owns a fixed retry schedule. Authored maxRetries/backoffStrategy/initialDelayMs/maxDelayMs are all ignored. (The showcase's TaskChangedWebhook authors a retryPolicy that does nothing — the canonical no-op.)"
55
+
},
56
+
"timeoutMs": {
57
+
"status": "dead",
58
+
"note": "Authored value never materialized. Honored only from the `definition_json` textarea of a hand-created row (auto-enqueuer.ts:277 → http-outbox.ts:96), never from this top-level prop."
59
+
},
60
+
"secret": {
61
+
"status": "dead",
62
+
"note": "Authored value never materialized. Honored only from `definition_json` (auto-enqueuer.ts:276 → HMAC signingSecret at :334), never from this top-level prop."
63
+
},
64
+
"isActive": {
65
+
"status": "dead",
66
+
"_authorWarnSkipped": "default(true) boolean — the lint can't distinguish author-set true from the schema default, so warning here would fire on every authored webhook regardless of intent (README rule 2). The `url` carrier already warns once per webhook.",
67
+
"note": "Authored value never materialized. The dispatcher gates on col `active` (sys-webhook.object.ts:162; where: { active: true } at auto-enqueuer.ts:176) — NAME MISMATCH: spec `isActive` vs runtime `active`. A materializer must remap."
68
+
},
69
+
"description": {
70
+
"status": "dead",
71
+
"note": "Authored value never materialized. Runtime col sys-webhook.object.ts:160 (Studio-editable; inert in dispatch). Salvageable 1:1."
72
+
},
73
+
"tags": {
74
+
"status": "dead",
75
+
"note": "No sink anywhere. No sys_webhook column, never read. Vestigial."
0 commit comments