@drawbridge/drawbridge-agents 0.1.39 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: drawbridge-add-connection
|
|
3
|
+
description: Use when adding a new vendor connection (a new manifest in drawbridge-utils) — runs the auth-type assessment FIRST so the integration is built the way the vendor's own docs recommend, then walks the build against the documented checklist and its guards.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add a connection
|
|
7
|
+
|
|
8
|
+
A connection is one manifest file in `drawbridge-utils/lib/connections/`, and the
|
|
9
|
+
build half is documented — `drawbridge-docs/reference/connection-hooks.md`,
|
|
10
|
+
"Adding a connection". This skill exists for what comes BEFORE the file: deciding
|
|
11
|
+
what kind of connection to build, from the vendor's documentation rather than
|
|
12
|
+
from whichever auth mechanism is easiest to wire.
|
|
13
|
+
|
|
14
|
+
The failure it prevents is real: Attentive was first built as pasted API keys
|
|
15
|
+
because keys worked, and their docs turned out to frame private-app keys as
|
|
16
|
+
"integrate your own account" — the public OAuth app was the documented path for
|
|
17
|
+
platforms like Drawbridge. The rebuild cost an afternoon; the assessment costs
|
|
18
|
+
ten minutes.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
Any new vendor manifest, and any auth-type change to an existing one (keys →
|
|
23
|
+
OAuth is a rebuild of the auth surface, not a tweak).
|
|
24
|
+
|
|
25
|
+
## Steps
|
|
26
|
+
|
|
27
|
+
1. **Fetch the vendor's developer docs and answer these in writing, with the
|
|
28
|
+
URL of the page that answers each.** Never from memory — the Klaviyo consent
|
|
29
|
+
url was once written from memory, pointed at their API host, and stalled
|
|
30
|
+
every merchant on a blank page.
|
|
31
|
+
|
|
32
|
+
- What auth mechanisms exist, and **who does the vendor say each is for**?
|
|
33
|
+
The distinction that matters is *your own account* versus *accounts outside
|
|
34
|
+
your organization*. Drawbridge is always the second: merchants connect
|
|
35
|
+
THEIR account to OUR platform.
|
|
36
|
+
- Is there a partner / public-app / marketplace program? What does
|
|
37
|
+
registration take, and what credentials does it issue?
|
|
38
|
+
- For OAuth: authorize url, token url, how client credentials are sent
|
|
39
|
+
(form fields vs Basic header), whether PKCE is required, token lifetime,
|
|
40
|
+
whether refresh tokens exist, scope names.
|
|
41
|
+
|
|
42
|
+
2. **Pick the `auth.type` from the answers, not from convenience:**
|
|
43
|
+
|
|
44
|
+
| vendor's shape | type |
|
|
45
|
+
|---|---|
|
|
46
|
+
| installed from the vendor's app store, never redirects back | `install` |
|
|
47
|
+
| registered app, merchant consents on the vendor's screen | `oauth` |
|
|
48
|
+
| the vendor offers ONLY per-account credentials | `keys` |
|
|
49
|
+
| no third party behind it at all | `generated` |
|
|
50
|
+
|
|
51
|
+
`keys` when OAuth exists is a decision someone must make out loud — usually
|
|
52
|
+
as an explicitly-flagged interim while a partner registration is pending, and
|
|
53
|
+
the manifest header says so.
|
|
54
|
+
|
|
55
|
+
3. **If the app is not yet registered, build it dormant.** `requires` names the
|
|
56
|
+
client env vars that will exist after registration; until they land on a
|
|
57
|
+
deployment, the connection is offered nowhere. Shipping the manifest complete
|
|
58
|
+
and inert beats waiting — adding the env vars becomes the launch.
|
|
59
|
+
|
|
60
|
+
4. **Record the assessment in the manifest header**: every cited fact, every
|
|
61
|
+
contradiction between the vendor's own pages (cite both and say which one the
|
|
62
|
+
code models), and — for OAuth — the questions only a live registration
|
|
63
|
+
answers: does consent echo `state` back, what is the real token lifetime,
|
|
64
|
+
does one app accept both environments' redirect urls.
|
|
65
|
+
|
|
66
|
+
5. **Build against the checklist** in
|
|
67
|
+
`drawbridge-docs/reference/connection-hooks.md` → "Adding a connection". Its
|
|
68
|
+
guards are the enforcement: the directory-registration test, the
|
|
69
|
+
feature-grant test, `build()` itself. Trust the named failures over your
|
|
70
|
+
memory of the steps.
|
|
71
|
+
|
|
72
|
+
## Notes
|
|
73
|
+
|
|
74
|
+
- The manifest is the only place vendor facts live. If the assessment finds a
|
|
75
|
+
fact that must hold across repos, record it with `drawbridge-record-contract`.
|
|
76
|
+
- The picker/search question is part of the assessment: if the vendor's list
|
|
77
|
+
endpoint filters by name server-side, the field must NOT declare
|
|
78
|
+
`search : false` — Attentive was the first vendor that could, and a test pins
|
|
79
|
+
that it stays searchable.
|
|
@@ -137,6 +137,19 @@ skill) so it stops living only in tribal memory.
|
|
|
137
137
|
- **A subscription must open with the plan item.** It used to open with the metered actions price
|
|
138
138
|
and attach the plan afterwards with `always_invoice`. With no metered item, Stripe rejects an
|
|
139
139
|
itemless subscription, so the plan goes in at creation — changing the first invoice's shape.
|
|
140
|
+
- **Ending a subscription is written twice, from ONE implementation** (stripe ↔ api ↔ sync).
|
|
141
|
+
`subscription.markTerminal` in **drawbridge-stripe** (`lib/subscription.js`, takes the caller's
|
|
142
|
+
db controller + session like `reconcile`) clears the org's `subscription` pointer, demotes to
|
|
143
|
+
`unsubscribed` under `status : { $nin : [ 'suspended', 'unsubscribed' ] }`, `$pull`s the org's
|
|
144
|
+
`type : 'method'` errors and flips the sub doc to `canceled`. Both callers run it: sync's
|
|
145
|
+
`markSubscriptionTerminal` (`lib/buffer.js`, on `customer.subscription.deleted` and terminal
|
|
146
|
+
`subscription.updated`) and drawbridge-api's admin cancel
|
|
147
|
+
(`route/organization-subscription.js` `handlers.delete.cancel`), which mirrors it inline so the
|
|
148
|
+
page isn't stale for a webhook it just caused. **The mirror runs FIRST and consumes the status
|
|
149
|
+
guard**, so the webhook's pass then matches nothing — anything one caller does outside
|
|
150
|
+
`markTerminal` therefore never happens on the admin path. Never re-inline these writes: the
|
|
151
|
+
hand-copied version drifted, and a stale card decline outlived its subscription and permanently
|
|
152
|
+
blocked the org from resubscribing (Asana 1217947409527192).
|
|
140
153
|
- **Invoice line descriptions must keep the word "actions":** `isActionsLine` matches `/actions/i`
|
|
141
154
|
to count billed quantity for the billed-vs-ledger reconcile. A copy edit that drops it silently
|
|
142
155
|
breaks the audit. Detail: drawbridge-docs `reference/billing.md`.
|
|
@@ -8,7 +8,12 @@ drawbridge-docs/superpowers/plans/YYYY-MM-DD-<slug>.md
|
|
|
8
8
|
drawbridge-docs/superpowers/specs/YYYY-MM-DD-<slug>.md
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**THE ONE EXCEPTION IS drawbridge-growth** (Darren, repeatedly, most recently 2026-09-01:
|
|
12
|
+
"Don't add growth docs to docs"): growth is its own system — its plans and specs live in
|
|
13
|
+
`drawbridge-growth/docs/superpowers/{plans,specs}/` and must **never** be written to
|
|
14
|
+
drawbridge-docs. The guard hook exempts any `drawbridge-growth*` path for this reason.
|
|
15
|
+
|
|
16
|
+
For every other repo, this is the **only** allowed home for these working docs. Rules:
|
|
12
17
|
|
|
13
18
|
- Do **not** create or keep superpowers plans/specs (or `docs/superpowers/`, `docs/plans/`)
|
|
14
19
|
inside any other repo. When you finalize a plan or spec, write it under
|
|
@@ -47,6 +47,11 @@ const run = () => {
|
|
|
47
47
|
// drawbridge-docs is the canonical home — anything there is fine.
|
|
48
48
|
if (resolved.split('/').includes('drawbridge-docs')) process.exit(0)
|
|
49
49
|
|
|
50
|
+
// GROWTH IS THE EXCEPTION (Darren, repeatedly, most recently 2026-09-01):
|
|
51
|
+
// growth is its own system and its docs stay in drawbridge-growth — never
|
|
52
|
+
// in drawbridge-docs. Any growth checkout or worktree passes.
|
|
53
|
+
if (/\/drawbridge-growth[^/]*\//.test(resolved + '/')) process.exit(0)
|
|
54
|
+
|
|
50
55
|
const hit = FORBIDDEN.find((seg) => resolved.includes('/' + seg + '/') || resolved.endsWith('/' + seg))
|
|
51
56
|
if (!hit) process.exit(0)
|
|
52
57
|
|
package/package.json
CHANGED