@drawbridge/drawbridge-agents 0.1.61 → 0.1.62

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.
@@ -42,6 +42,20 @@ skill) so it stops living only in tribal memory.
42
42
  granted keys — that string is data, never rename it without a migration.)
43
43
  - Cascade cleanup of dependent rows belongs in a sync change-stream listener, not inline in the
44
44
  API route that triggered the delete (see `cascade-cleanup.md`).
45
+ - **A product is owned through `product.organizations[]`, never through a connection** (utils ↔
46
+ api ↔ sync). `$addToSet` in utils' Shopify `commerce.product` write and api's campaign/ad
47
+ attach routes; `$pull` in ONE place — sync `stream/organization.js`, on organization delete.
48
+ Disconnect (`queue/connection.js`) pulls `connections[]` only, so a disconnected or hook-less
49
+ provider still lists its products by owner (api `route/organization-product.js`). Orphan
50
+ deletion (`lib/orphan-products.js`) needs ALL of: no organizations, no `campaign.products[]`
51
+ or `advertisement.product` ref, and no `order.lines[].productId` naming it — orders survive the
52
+ org cascade on purpose (dev lost three orders' products before the order lookup existed).
53
+ `order.lines[].product` is the product DOCUMENT, resolved by the utils order hook on
54
+ `provider.id` + `provider.slug` + `source.domain`; the raw `productId` is provider-scoped and
55
+ never a join key on its own. Deploy the api (validator) BEFORE sync/utils consumers write
56
+ either field — `additionalProperties:false` rejects the unknown key — and never backfill on
57
+ dev under a validator not yet on `develop`: the next api deploy reverts it and every
58
+ backfilled doc 121s.
45
59
  - A collection with no `queue/<collection>.js` handler is watched but its events are **silently
46
60
  dropped**. Adding a reactive collection means wiring `stream/index.js`, `lib/queue.js`, and
47
61
  `queue/index.js` together.
@@ -77,8 +91,12 @@ skill) so it stops living only in tribal memory.
77
91
  api registers the number's SmsUrl at twilio connect (`lib/twilio.js`) and `scripts.js
78
92
  registerTwilioWebhooks` backfills — a deployed webhooks route with unregistered numbers hears
79
93
  nothing, silently.
80
- - **`audience` picks the FROM-address; `intent` picks the opt-out machinery** (utils ↔ sync ↔
81
- api). `resolveEmailSender` (sync `queue/notification.js`) resolves verified `identity` row →
94
+ - **`audience` picks the FROM-address; `intent` picks the unsubscribe plumbing — and NOTHING
95
+ passes the suppression gate** (utils ↔ sync ↔ api). Every suppression source blocks every
96
+ lead-facing send, the winner claim included: api's draw route refuses up front ("Disqualify
97
+ this draw and draw again"), sync's `canSend` takes no intent (decided 2026-09-23, reversing
98
+ the 09-02 pass-through; a `$ne : 'unsubscribe'` anywhere on that path is the regression).
99
+ `resolveEmailSender` (sync `queue/notification.js`) resolves verified `identity` row →
82
100
  `subAddress( leadSender, organization.shortId )` for lead-facing mail → `accountSender` for
83
101
  member and platform mail. The tag is `shortId`, never `subdomain` — a rename and a custom-plan
84
102
  downgrade both rewrite subdomain, and the from-address is frozen into delivered mail; api's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.1.61",
3
+ "version": "0.1.62",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {