@drawbridge/drawbridge-agents 0.1.89 → 0.1.91

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.
@@ -9,7 +9,7 @@ One Asana release task is the unit. Its id names everything: the `feature/<id>`
9
9
 
10
10
  The ten lines below are the release, as Darren confirmed them on 2026-10-22. The skill owns lines 1 to 5 and 10 and runs them without anyone present once line 2 has its yes. A developer owns 6 and 9. A QA person owns 7 and 8. The skill never does a developer's or a tester's line for them.
11
11
 
12
- 1. A person gives the skill the release task link. The skill runs the doctor, then asks two things before reading anything: who reviews and who tests, each with the dev organization they sign in to.
12
+ 1. A person gives the skill the release task link. The skill runs the doctor, then asks two things before reading anything: who reviews and who tests.
13
13
  2. The skill reads the task and subtasks, writes the stories, sends ONE numbered list and waits for the yes. The yes starts an unattended run.
14
14
  3. Task → Building: branches, the release document, the band.
15
15
  4. The skill builds on the release branches against the Local database, with the QA agent run locally. Nobody is asked anything; whatever needs a person is parked.
@@ -26,10 +26,12 @@ Read the release task and every subtask through `drawbridge-tests/lib/asana.js`
26
26
 
27
27
  **Then, before the stories, ONE numbered list of two questions**, so the people are known before any work starts and nobody waits for the review to finish (Darren, 2026-10-23: "the first questions asked right away is who is the development reviewer and who is the qa specialist"; "Then any task questions can come after the initial review"):
28
28
 
29
- 1. Who on the development team reviews this release: their GitHub username (for the pull requests), their Asana name (for the task), and the dev organization they sign in to (name or id).
30
- 2. Who tests it: the QA person's Asana name and the dev organization they can open (name or id). Every link in the "Test on dev (QA)" lists is built on THAT organization, never on the e2e organization or one of Darren's, because a tester sent to an organization they cannot open loses the whole session (Jaime, 2026-10-23: a link went to one of Darren's organizations).
29
+ 1. Who on the development team reviews this release: their GitHub username (for the pull requests) and their Asana name (for the task).
30
+ 2. Who tests it: the QA person's Asana name.
31
31
 
32
- Wait for both answers. Never guess either name or organization. The developer still assigns the QA person at line 6; the name asked here is who that will be.
32
+ No organization is asked for, and no test list names one. Nobody can guess which organization a developer or a tester will use, and a link to an organization they cannot open loses the whole session (Jaime, 2026-10-23: a link went to one of Darren's organizations). Each test list instead says what the organization must have (Darren, 2026-10-23: "Open an organization with the following features...").
33
+
34
+ Wait for both answers. Never guess either name. The developer still assigns the QA person at line 6; the name asked here is who that will be.
33
35
 
34
36
  ## Line 2: the one list
35
37
 
@@ -39,9 +41,9 @@ Wait for both answers. Never guess either name or organization. The developer st
39
41
 
40
42
  - every unknown from every story, **each with the fix you recommend**: "I recommend <the option>, because <the reason>." The recommendation comes from having read the code the subtask touches (the files the graph named, opened whole) and from what apps like Drawbridge commonly do for the same thing, and it picks the best experience for the person using the product. That goes for every aspect of the platform, not only screens: a feature, a price or plan rule, a design, a message, a default, a limit. A question without a recommendation reads as a question nobody has thought about (Darren, 2026-10-22: "recommend the fix as well. This way I know you've reviewed the code and considered the best approach. We should always be looking at what is the best user experience as well as what is common for apps like ours"). When the recommendation needs a fact you do not have, say which, and still name the option you would take;
41
43
  - nothing about who reviews or tests: both were answered at line 1;
42
- - **a package publish, if any subtask names `@drawbridge/components` or `@drawbridge/drawbridge-utils`**, as a statement, not a question: this release publishes a package; the developer named above publishes it. The package is built first and gets its own pull request to the package's `main`; the end list (line 5) hands the developer the merge and the publish command; the skill then bumps the pin in every consumer, and the app pull requests open after that, once the publish is done. Line 4 step 2 is the exact sequence.
44
+ - **a package publish, if any subtask names `@drawbridge/components` or `@drawbridge/drawbridge-utils`**, as a statement, not a question: this release publishes a package; the developer named above publishes it. The package is built first and gets its own pull request to the package's `main`; the end list (line 5) hands the developer the merge and the tag to push; the skill then bumps the pin in every consumer, and the app pull requests open after that, once the publish is done. Line 4 step 2 is the exact sequence.
43
45
 
44
- **Publishing is settled; never ask about it.** The developer publishes every package, from the package checkout on `main`, with `git pull && npm run build` (tsup, the tests, `npm publish`, their one-time password). Agents hold no npm token and never will (an agent's `npm publish` answers 401); the automation token (queue task 1218041740718540, in Butterscotch) will move the publish into CI, and until then it is the developer's hand. So the skill never asks who publishes, whether to publish, whether it may publish, or how: it writes the one command into the end list as an instruction to the developer and waits for `npm view` to show the version (Darren, 2026-10-22: "I get a lot of questions in the release about npm publishing etc, lets make sure that it's clear that the developers do the publishing so that these go away").
46
+ **Publishing is settled; never ask about it.** The developer publishes every package by merging its version bump to `main` and pushing the tag `v<version>` (`git checkout main && git pull && git tag v<version> && git push origin v<version>`); GitHub Actions (`.github/workflows/publish.yml`, since Butterscotch 2026-10-23, task 1218041740718540) builds, runs the tests and publishes through npm trusted publishing, so no npm token exists anywhere and the registry shows the publisher as "GitHub Actions" (proven with stripe 0.1.56). The workflow refuses a tag that differs from `package.json` or is not on `main`. The hand path, `git pull && npm run build` on `main` with the developer's one-time password, still works as a fallback. Agents hold no npm token and never will. So the skill never asks who publishes, whether to publish, whether it may publish, or how: it writes the one command into the end list as an instruction to the developer and waits for `npm view` to show the version (Darren, 2026-10-22: "I get a lot of questions in the release about npm publishing etc, lets make sure that it's clear that the developers do the publishing so that these go away").
45
47
 
46
48
  Wait for the yes. Nothing below runs before it. From the yes to the end list, the run is unattended: the next section says what that means.
47
49
 
@@ -74,7 +76,7 @@ All work on the release branches against the **Local** database. Develop and its
74
76
  2. **Package first, and the publish never blocks.** Every subtask that touches `@drawbridge/drawbridge-utils` or `@drawbridge/components` is built before any app subtask, so the package is finished and its pull request ready when the end list goes out, and the build never pauses for it (Darren, 2026-10-22: "I had to wait a while for the publishing code to be written"; "the publish step includes merging each npm package to main, and bumping the pin(s)"). The whole package step, per package, utils before components (components peer-depends on utils):
75
77
  1. Build the package subtasks on `feature/<id>` in that package, tests first, `npm test` and `npm run test:app` (components) green, the two review passes.
76
78
  2. Bump the package's own version in its `package.json` (patch), commit, push, and open its pull request to the package's `main` (`gh pr create --base main …`), assigned to the developer from line 2. Nothing is posted to Asana yet; the link goes into the end list.
77
- 3. Write the publish instruction into the Parked section, addressed to the developer from line 2, as one numbered item with the exact words: merge that pull request, then from the package checkout on `main`: `git pull && npm run build` (tsup, tests, `npm publish` with the one-time password), then confirm with `npm view <package> version`. It is an instruction, not a question, and it goes out at line 5, never now. No question about publishing is ever put to anyone (line 2).
79
+ 3. Write the publish instruction into the Parked section, addressed to the developer from line 1, as one numbered item with the exact words: merge that pull request, then from the package checkout: `git checkout main && git pull && git tag v<version> && git push origin v<version>`; the Publish workflow does the rest, and `npm view <package> version` shows it within a few minutes (by hand if the workflow fails: `npm run build` on `main`). It is an instruction, not a question, and it goes out at line 5, never now. No question about publishing is ever put to anyone (line 2).
78
80
  4. **Keep going** on the app subtasks. For one that needs the new package code, `npm link` the local package checkout into the app worktree so it is written and tested against it now.
79
81
  5. When `npm view <package> version` shows the new version (after the person has published from the end list): bump the pin to that exact version in **every consumer in the family**, not only the release's (Darren, 2026-10-22: "Once a package is published, all consumer pins should also be updated"; pins are version-exact family-wide, `conventions/drawbridge-packages.md`). One command does it: `npx drawbridge-agents-bump <package> <version>` (from any family checkout; `--dry` lists the consumers first) finds every consumer, keeps each one's field, installs the exact version so the lockfile names it, commits "<package> <version>" and pushes each repo's current branch. Before running it, the release's own consumers must have their `feature/<id>` branch checked out (the worktrees), so they take the bump there, while every other consumer takes a pin-only commit on its integration branch. Unlink first where a worktree was linked; run the tests after. A sibling package bumped this way is itself published by the same steps before the apps that depend on it.
80
82
  6. No app pull request opens while a pin still names a version the registry does not have.
@@ -94,7 +96,7 @@ Only when every subtask that can be built has been built: each committed, `npm t
94
96
 
95
97
  1. One pull request per repo: `gh pr create --base develop --head feature/<id> --title "Release <id>: <task name>" --assignee <developer> --reviewer <developer> --body <the Scope table, the per-subtask commits, the QA reports' paths, and the migration's dev command if there is one>`. Components and utils target `main`.
96
98
  2. Every pull request body ends with the lane rule in one line: "Review any time, on the feature branch locally. Merge only when develop is free: one release on develop at a time, so this merges once the release before it is Released." Fill the Branches table in `releases/<id>.md` with the PR links; commit.
97
- 3. **How to test, on every subtask, for both readers.** Append to each subtask's description, under its Story lines, two headings (Darren, 2026-10-22: "steps for a developer to test via code, or locally, and a QA person's steps"). First, "Test locally (developer)": one numbered list with the branch to check out, the exact commands (`npm test` in the repo, the test file that proves the change, `npm run migrate -- <id> --dry --env local` when there is a migration, `npm run walk -- --local <path>` for a screen), the localhost URL and the result each command must print or show. Then, "Test on dev (QA)": one numbered list with the dev app to open first by URL, on the QA person's own organization from line 1 (dashboard `https://dev.dashboard.trydrawbridge.com/organizations/<their organization id>/...`, a share page `https://<slug>.dev.drwbrdg.com/...`, the website `https://dev.trydrawbridge.com/...`, the components app `https://components.trydrawbridge.com/...`), then one action per step, then what the tester must see. Every label in it is quoted from the Local walk and the QA reports of line 4, never from memory (`conventions/walk-the-app.md`), with dev URLs in place of localhost. A migration subtask says what the tester checks after the developer's dev run. The words go through `humanizer`. Both lists are written here, after the build, because only then are the commands and labels known (Darren, 2026-10-22: "include steps on how to test in the respective development apps"). The pull request body and the release doc's QA table point at the subtasks for these steps rather than repeating them.
99
+ 3. **How to test, on every subtask, for both readers.** Append to each subtask's description, under its Story lines, two headings (Darren, 2026-10-22: "steps for a developer to test via code, or locally, and a QA person's steps"). First, "Test locally (developer)": one numbered list with the branch to check out, the exact commands (`npm test` in the repo, the test file that proves the change, `npm run migrate -- <id> --dry --env local` when there is a migration, `npm run walk -- --local <path>` for a screen), and for a screen: "Open an organization with the following features: <what this subtask needs, e.g. a connected Shopify store, a giveaway with a published prize>" on `http://localhost:3000`, then the path inside it by screen name, and the result each command must print or show. Never a fixed organization id. Then, "Test on dev (QA)": one numbered list with the dev app to open first (dashboard `https://dev.dashboard.trydrawbridge.com/organizations`, then "Open an organization with the following features: <what this subtask needs>" and the screens by name from there, never a fixed organization id, a share page `https://<slug>.dev.drwbrdg.com/...`, the website `https://dev.trydrawbridge.com/...`, the components app `https://components.trydrawbridge.com/...`), then one action per step, then what the tester must see. Every label in it is quoted from the Local walk and the QA reports of line 4, never from memory (`conventions/walk-the-app.md`), with dev URLs in place of localhost. A migration subtask says what the tester checks after the developer's dev run. The words go through `humanizer`. Both lists are written here, after the build, because only then are the commands and labels known (Darren, 2026-10-22: "include steps on how to test in the respective development apps"). The pull request body and the release doc's QA table point at the subtasks for these steps rather than repeating them.
98
100
  4. Attach every QA report's screenshots to the release task (`node lib/asana-attach.js <release id> <png>` from drawbridge-tests, one per scenario, named `<subtask id>-<scenario>.png`), because the reports under `results/qa/` are gitignored and exist only on the machine that ran them. Then put the PR links on the release task as ONE numbered list (`drawbridge-asana-comment`): this is the first Asana comment of the release, and the package pull request's link rides in it too. Assign the task to the developer and move it to Review (`POST /sections/1219762451512990/addTask`) only once every pull request is open, parked items included.
99
101
  5. Stop. Report the PR links.
100
102
 
@@ -125,8 +127,9 @@ The skill does none of this. For the record, so the skill can answer "what is th
125
127
  ## What the skill never does
126
128
 
127
129
  - Push `main`, deploy production, write to the production database, publish a package, merge a pull request, assign the QA person or move the task to QA or Approved. Those are a developer's or a tester's lines. On dev the skill writes nothing of its own either (the refresh only READS dev to copy it into Local); the dev migration runs on a developer's explicit instruction, by the developer or by an agent they ask.
128
- - Ask anything about npm publishing: who, whether, how, or for permission. The developer publishes, always; the skill hands them the one command in the end list and waits for the registry.
129
- - Name a reviewer, a tester, their organizations or a release scope that nobody told it; the people are asked at line 1, the scope at line 2. The window is the developer's at line 9; the skill asks for it never.
130
+ - Ask anything about npm publishing: who, whether, how, or for permission. The developer publishes, always, by pushing the version tag; the skill hands them the one command in the end list and waits for the registry.
131
+ - Name a reviewer, a tester or a release scope that nobody told it; the people are asked at line 1, the scope at line 2. The window is the developer's at line 9; the skill asks for it never.
132
+ - Name an organization in a test list. A developer or a tester opens one that has the features the list names; nobody can guess which they will use.
130
133
  - Write a release document, branch or migration under any name but the Asana id.
131
134
  - Build or run the QA agent against dev or develop before the pull requests merge. Local only.
132
135
 
@@ -99,7 +99,15 @@ skill) so it stops living only in tribal memory.
99
99
  `APP_CLIENT_WEBHOOKS_URI`) → `buffer` doc → sync drain → platform-global suppression row;
100
100
  api registers the number's SmsUrl at twilio connect (`lib/twilio.js`) and `scripts.js
101
101
  registerTwilioWebhooks` backfills — a deployed webhooks route with unregistered numbers hears
102
- nothing, silently.
102
+ nothing, silently. **Since Butterscotch (release 1219790090574059; sync 762fdd7, Darren
103
+ 2026-10-23: an opt-out is the person's choice, not one address's) `canSend` gates the
104
+ PERSON, not only the address**: it reads the recipient's contact in the organization (found by
105
+ `canonical.emails`/`canonical.phones` or by `leads`) and blocks when any canonical or raw
106
+ address on that contact is suppressed for the channel. `lead` is optional, so every caller
107
+ that knows the lead passes it, or a returning person under a new mailbox passes until their
108
+ contact is rebuilt with the new address. The api's draw opt-out check
109
+ (`route/organization-campaign-draw.js` `optedOutLeads`) applies the same rule and must stay
110
+ in step with it; before Butterscotch both matched the lead's own canonical address only.
103
111
  - **`audience` picks the FROM-address; `intent` picks the unsubscribe plumbing — and NOTHING
104
112
  passes the suppression gate** (utils ↔ sync ↔ api). Every suppression source blocks every
105
113
  lead-facing send, the winner claim included: api's draw route refuses up front ("Disqualify
@@ -2,8 +2,24 @@
2
2
 
3
3
  Conventions for working on the `@drawbridge/*` npm packages
4
4
  (`drawbridge-utils`, `drawbridge-components`, `drawbridge-mongodb`,
5
- `drawbridge-redis`, `drawbridge-stripe`, `drawbridge-shopify`,
6
- `drawbridge-telemetry`) and the apps that consume them.
5
+ `drawbridge-redis`, `drawbridge-stripe`, `drawbridge-telemetry`, `drawbridge-agents`)
6
+ and the apps that consume them. `drawbridge-shopify` is retired: the repo was archived
7
+ on GitHub and `@drawbridge/shopify` deprecated on npm (2026-09-13, "use
8
+ @drawbridge/drawbridge-utils/shopify/*"); never pin it.
9
+
10
+ ## Publishing
11
+
12
+ **A publish is a tag push (since 2026-10-23).** Merge the version bump to `main`, then from
13
+ the package checkout `git checkout main && git pull && git tag v<version> && git push origin
14
+ v<version>`. Every package's `.github/workflows/publish.yml` runs on `v*` tags: it refuses a
15
+ tag that differs from `package.json` or is not on `main`, runs `npm ci`, `npx tsup` and
16
+ `node --test`, then `npm publish --ignore-scripts` through npm trusted publishing (OIDC,
17
+ `id-token: write`, npm 11). No npm token is stored anywhere; the registry records the
18
+ publisher as "GitHub Actions" (first proven with stripe 0.1.56, run 39975659406). The
19
+ trusted publisher for each package is configured on npmjs.com by Darren. A developer pushes
20
+ the tag; an agent never does.
21
+
22
+ The hand path below still works as a fallback when the workflow fails.
7
23
 
8
24
  ## Build + publish script
9
25
 
@@ -88,8 +88,15 @@ await removeEntrant( seeded ); // when the walk is done
88
88
 
89
89
  `seedEntrant` writes what the api's thin ingest writes — a zero-count lead and one submission
90
90
  flagged pending — and waits for the sync worker to materialize the entries and every counter.
91
- `removeEntrant` deletes the draws that picked the lead and then the lead; sync's cascade does
92
- the rest and each counter nets back to zero. A lead inserted with its subtotals filled in was
91
+ `removeEntrant` deletes the draws that picked the lead and then the lead, and lifts any denial a
92
+ walk made on the entrant, which outlives the lead by design (drawbridge-api `schema/denial.js`
93
+ keys moderation on the person's canonical identifiers, not the lead id, so no product cascade
94
+ removes it; since drawbridge-tests b081ac1, Asana 1219802418571685); sync's cascade does the
95
+ rest and each counter nets back to zero. **Every `walk+<tag>@…` address is ONE person** to
96
+ moderation and to consent, because `toCanonicalEmail` in utils `lib/email.js` strips the plus
97
+ tag before either looks a person up: a denial or an unsubscribe made on `walk+a@` also covers
98
+ `walk+b@`. Concurrent or later walks that must not share a moderation or consent state use
99
+ different base mailboxes, not different tags. A lead inserted with its subtotals filled in was
93
100
  never counted on its campaign, so deleting it decremented Entries and Submissions below zero
94
101
  (Asana 1218811920761541, 2026-09-23). Counters now floor at zero in sync, but the seed still
95
102
  has to go through the worker or the counts on the walked screen are wrong.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.1.89",
3
+ "version": "0.1.91",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {