Summary
Re-running bgagent linear setup <slug> on an already-installed workspace silently overwrites that workspace's per-workspace webhook signing secret with the stack-wide fallback secret — which belongs to whichever workspace installed first. In a multi-workspace deployment this breaks HMAC signature verification for every workspace after the first: the receiver rejects Linear's deliveries with HTTP 401 {"error":"Invalid signature"}.
Live-hit on the demo-abca workspace: after re-authing (which runs setup), the stored webhook_signing_secret flipped from the workspace's real value to the stack-wide one (lin_wh_e9m… from a different install), and every webhook delivery started 401-ing. Recovery required bgagent linear update-webhook-secret demo-abca to paste the real secret back.
Root cause
cli/src/commands/linear.ts, setup action (~line 687). The block is guarded only on stackWideAlreadyConfigured:
if (stackWideAlreadyConfigured) {
// "Either way the stored value is this workspace's signing secret — lift it
// into the per-workspace bundle without prompting" ← WRONG for workspace #2
webhookSigningSecret = <stack-wide secret>;
}
The inline comment's assumption ("the stored value is this workspace's signing secret") holds only for the first workspace. For an additional workspace, the stack-wide secret is a different workspace's secret, so the mirror corrupts the (correct) per-workspace secret. Also, setup rebuilds the OAuth bundle fresh from the exchange and upserts it before this block, so the pre-existing webhook_signing_secret is already dropped by the time the block runs.
Fix (implemented)
- New pure helper
resolveWebhookSecretAction(existingPerWorkspaceSecret, stackWideConfigured) in cli/src/linear-oauth.ts → preserve | mirror-stackwide | prompt. An existing valid per-workspace secret always wins (rotation is update-webhook-secret's job); else mirror stack-wide (with a warning to verify for additional workspaces); else prompt.
setup now reads the existing per-workspace webhook_signing_secret before the OAuth overwrite and preserves it.
- 5 unit tests (incl. the multi-workspace re-run case).
Acceptance criteria
Summary
Re-running
bgagent linear setup <slug>on an already-installed workspace silently overwrites that workspace's per-workspace webhook signing secret with the stack-wide fallback secret — which belongs to whichever workspace installed first. In a multi-workspace deployment this breaks HMAC signature verification for every workspace after the first: the receiver rejects Linear's deliveries with HTTP 401{"error":"Invalid signature"}.Live-hit on the
demo-abcaworkspace: after re-authing (which runssetup), the storedwebhook_signing_secretflipped from the workspace's real value to the stack-wide one (lin_wh_e9m…from a different install), and every webhook delivery started 401-ing. Recovery requiredbgagent linear update-webhook-secret demo-abcato paste the real secret back.Root cause
cli/src/commands/linear.ts,setupaction (~line 687). The block is guarded only onstackWideAlreadyConfigured:The inline comment's assumption ("the stored value is this workspace's signing secret") holds only for the first workspace. For an additional workspace, the stack-wide secret is a different workspace's secret, so the mirror corrupts the (correct) per-workspace secret. Also,
setuprebuilds the OAuth bundle fresh from the exchange andupserts it before this block, so the pre-existingwebhook_signing_secretis already dropped by the time the block runs.Fix (implemented)
resolveWebhookSecretAction(existingPerWorkspaceSecret, stackWideConfigured)incli/src/linear-oauth.ts→preserve | mirror-stackwide | prompt. An existing valid per-workspace secret always wins (rotation isupdate-webhook-secret's job); else mirror stack-wide (with a warning to verify for additional workspaces); else prompt.setupnow reads the existing per-workspacewebhook_signing_secretbefore the OAuth overwrite and preserves it.Acceptance criteria
setupon an installed workspace does NOT change itswebhook_signing_secret.