@drawbridge/drawbridge-agents 0.1.58 → 0.1.59

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.
package/claude/CLAUDE.md CHANGED
@@ -8,6 +8,7 @@
8
8
  @../conventions/jsx-fragments.md
9
9
  @../conventions/transactions.md
10
10
  @../conventions/cascade-cleanup.md
11
+ @../conventions/migrations.md
11
12
  @../conventions/dry.md
12
13
  @../conventions/property-shorthand.md
13
14
  @../conventions/drawbridge-packages.md
@@ -478,6 +478,28 @@ failure never does. Sync's step-runner errors the connection on that code during
478
478
  anywhere transient flips healthy connections to error on a blip; renaming it in either repo
479
479
  means dead grants retry into Sentry forever (the Shopify 08-16 incident, ecosystem-wide).
480
480
 
481
+ **A segment's `connections` rows have one writer, and the api ships first (utils ↔ sync ↔ api
482
+ ↔ app-web).** `segment.connections[]` — `{ connection, id, slug, type, url }`, one row per
483
+ connection that publishes the segment — is written ONLY by drawbridge-sync's
484
+ `step.segment.register` (cleared by `step.segment.remove`), through the shared descriptors in
485
+ utils `lib/connections/segment-rows.js`. api and app-web read the rows raw; the `url` is built
486
+ inside the hook, where the settings are decrypted, so nothing downstream may recompute it. Two
487
+ deploy orders, both silent when violated: the api must have **booted** with `schema/segment.js`
488
+ before any sync writes a row (strict validator, `additionalProperties : false` → Mongo 121, row
489
+ lost), and the api's pin must land before sync provisions a workflow, because `enums.step.type`
490
+ derives from the installed utils and the workflow route validates against it ("Document failed
491
+ validation", naming no field). Detail: `drawbridge-docs/reference/connection-hooks.md`.
492
+
493
+ **A system step's workflow is provisioned from the manifest's `trigger` (utils ↔ sync).**
494
+ `ensureSystemWorkflows` in sync walks every system step a manifest declares and creates one
495
+ workflow per step that names a `trigger` — no trigger means nothing fires it, which is how
496
+ Shopify's audit-only token steps stay undispatched. No PUBLISHED manifest carries a trigger yet,
497
+ so sync must not merge ahead of the utils pin bump: on the older package the walk finds no
498
+ trigger, provisions nothing, and Klaviyo connections silently stop getting their daily health
499
+ workflow. Provisioning is idempotent on `(connection, system, title)` with nothing
500
+ unique-indexing it, so a system step's `key` must stay static — a key that varied by account
501
+ would provision a second workflow every time it changed.
502
+
481
503
  **The SMS inbound URL is one string two repos must agree on (api ↔ sync ↔ webhooks).**
482
504
  `APP_CLIENT_WEBHOOKS_URI + '/connection/drawbridge/sms'` is written to each purchased
483
505
  number by api (`route/organization-networking.js`) and re-asserted by sync's daily identity
@@ -0,0 +1,50 @@
1
+ # Migrations live in the api
2
+
3
+ Every one-off migration and backfill goes in `drawbridge-api/scripts.js`.
4
+
5
+ **The only exception is media processing** — work that needs ffmpeg or sharp to
6
+ open an image or a video. Those dependencies exist in `drawbridge-sync` and
7
+ nowhere else, so a migration that re-encodes a file or extracts a video frame
8
+ has to run there. Nothing else qualifies.
9
+
10
+ "It dispatches queue work" is **not** an exception. The api has BullMQ and Redis
11
+ and already enqueues onto the workers' queues, so a migration that needs a worker
12
+ enqueues the job from the api and lets the worker do it. That is the same split
13
+ every live code path already uses: the api decides, the workers execute.
14
+
15
+ ## Why
16
+
17
+ A migration is a thing somebody runs once, under supervision, reading the dry run
18
+ before the real one. Keeping them in one file in one repo means there is one
19
+ place to look on release day, one run-list to work down, and one review surface
20
+ when someone asks what a release will do to production data. Splitting them
21
+ across two repos means the release plan has two homes, and the second one gets
22
+ forgotten — which is exactly what happened to the segment connection rows
23
+ migration.
24
+
25
+ It also means the file that runs migrations cannot break the worker. Sync
26
+ crashed on boot for several days because its `index.js` required a migrations
27
+ file the Dockerfile never copied, and App Platform rolled every deploy back so
28
+ quietly that dev served stale code the whole time.
29
+
30
+ ## How
31
+
32
+ - Add the function to `drawbridge-api/scripts.js`, alongside the others.
33
+ - Add a commented invocation to the numbered run-list at the bottom. Nothing runs
34
+ until somebody uncomments a line and deploys.
35
+ - Take `dryRun` and report what it would do. The first run of a migration is a
36
+ read.
37
+ - Add it to the release document in `drawbridge-docs/releases/` for the launch it
38
+ belongs to, with the ordering constraint stated: what has to be deployed before
39
+ it can run, and what breaks if it runs early.
40
+ - If the work needs a worker, enqueue the job the live path enqueues, with the
41
+ same job id shape, so a migration-queued run and a live run coalesce rather
42
+ than doubling.
43
+
44
+ ## Media migrations, the one exception
45
+
46
+ A media migration still follows everything above, except it lives in
47
+ `drawbridge-sync/scripts/` as a standalone script run by hand with `ENV_FILE`,
48
+ rather than in a `scripts.js` the service requires at boot. **Sync's `index.js`
49
+ must never require a migrations module** — that coupling is what took the worker
50
+ down.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.1.58",
3
+ "version": "0.1.59",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {