@drawbridge/drawbridge-agents 0.1.94 → 0.1.96

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.
@@ -23,11 +23,11 @@ bulleted section beside it. In order:
23
23
  1. What changed, in plain past tense. Numbered.
24
24
  2. What they need before the first step: a permission to grant, a setting to switch on, a
25
25
  record that has to exist. One item each.
26
- 3. What to do and what to expect. One step per item, one action per step (rules.md rule 9),
27
- each one something they can actually do or check. "Open the campaign and click Draw" is
28
- two steps. Every step starts with the click path from where they already are (Admin,
29
- Connections, then the store; Shopify admin, Apps, then Drawbridge). "Open the app" is not a
30
- path.
26
+ 3. What to do and what to expect. One step per CHECK: what they do, then what they must see,
27
+ in one item, each sentence one action (rules.md rule 9). Setup they already know how to do
28
+ (make a workflow, connect a vendor, open a second tab) is one line, never a click per line.
29
+ Every step starts with the click path from where they already are (Admin, Connections, then
30
+ the store; Shopify admin, Apps, then Drawbridge). "Open the app" is not a path.
31
31
  4. Anything that would otherwise look wrong to them. Numbered too.
32
32
  5. A result already confirmed, an explanation, a billing note: numbered too.
33
33
 
@@ -50,6 +50,14 @@ anything in the result that would look wrong without warning. A second comment c
50
50
  completing the first is the real failure. Structure is what keeps a complete comment readable,
51
51
  so restructure before you cut content.
52
52
 
53
+ **Fifteen steps is the ceiling.** Darren, 2026-10-28, on a 52-step retest (task
54
+ 1219064026230883): "It's insane to expect someone to go through this list." Count the steps
55
+ before the read-back. Over fifteen, restructure: fold setup into one line, drop every click
56
+ that is not the thing being tested, and write a cycle that repeats on another screen
57
+ (disconnect, look, reconnect, look) once per screen, as one step naming what changes there.
58
+ Still over fifteen means the change is more than one handoff: split it by story, one list each.
59
+ Never drop a check to hit the count.
60
+
53
61
  **The stop test:** if it does not fit on a phone screen without scrolling, restructure it. Do
54
62
  not trim facts to hit a length.
55
63
 
@@ -42,8 +42,9 @@ humanizer pass, claim verification and the read-back. Don't restate or work arou
42
42
 
43
43
  On top of those:
44
44
 
45
- - **One numbered list and nothing outside it**, one action per line, so they can reply "3 is
46
- wrong" and you both know what they mean. Item 1 is what changed, plain past tense, with
45
+ - **One numbered list and nothing outside it**, one check per line (what they do, then what
46
+ they must see), fifteen lines at most, so they can reply "3 is wrong" and you both know what
47
+ they mean. Item 1 is what changed, plain past tense, with
47
48
  the cause if they reported a bug. Then prerequisites, then the steps, each with what they
48
49
  should see, then what won't change and what you didn't test, all numbered. No sentence
49
50
  above or below the list.
@@ -65,7 +65,7 @@ A run does not stop while there is code left to write. Whatever the hour, the ag
65
65
  3. Copy `drawbridge-docs/templates/release.md` to `releases/<id>.md`, fill Scope and Branches (reviewer from line 2), commit on the docs branch. The markdown is the one record; there is no json.
66
66
  4. Write this task's link into `drawbridge-api/release.json` and commit it on the api's `feature/<id>` branch ("release <id>: band"), never on `develop`: with several releases building at once, a band written to develop at open would name a release that is not on dev. The band reaches develop with the merge, and dev shows it above the account once the api redeploys, which is exactly when that release is the one on dev. A release that touches no api code still gets an api `feature/<id>` branch carrying this one commit and its own pull request at line 5. Nothing is committed to develop before the pull requests merge. The previous release's cut empties `release.json` on develop (line 8), so when this pull request merges after that, GitHub shows a conflict on `release.json` (changed on both sides): keep the branch's file.
67
67
  5. **Every subtask links its story.** Append to each subtask's description, under its own text, one line per story: "Story: <story id>, <GitHub link>", the link being the story file in the docs repo on GitHub, `https://github.com/trydrawbridge/drawbridge-docs/blob/main/user-stories/<domain folder>/<file>.md` (`PUT /tasks/<subtask> { notes }`; the original description stays above, untouched). Never paste the story into the task: the file in drawbridge-docs is the one copy, and the link is read on GitHub (Darren, 2026-10-22: "link to the GitHub repo for each story in the subtask so it can be read in GitHub in the docs repo"; "Don't rewrite them in the task, just link to them"). The stories were pushed to docs `main` in step 2, so every link resolves the moment it is added; check each with `gh api repos/trydrawbridge/drawbridge-docs/contents/user-stories/<folder>/<file>.md` before writing it. The QA person cross-references the story against the feature before the final test.
68
- 6. Move every subtask from Application → Queue to Application → In progress (`POST /sections/1211315620607633/addTask { task }`). In the Application project, Queue means not scheduled and In progress means in a release (Darren, 2026-10-22); Complete comes at line 10. The release task itself lives in the Releases project only, never in Application or the Backlog, because a release draws its subtasks from both (Darren, 2026-10-22: "releases will include application tasks and backlog items, so their own board makes more sense"). A Backlog subtask keeps its Backlog membership; only Application subtasks get the Queue → In progress move.
68
+ 6. Move every subtask from Application → Queue to Application → In progress (`POST /sections/1211315620607633/addTask { task }`). In the Application project, Queue means not scheduled and In progress means in a release (Darren, 2026-10-22); Complete comes at line 10. A code subtask carries no assignee while it is in a release: the release task is what gets assigned (the developer at line 5, the QA person at line 6), so clear an assignee left on a subtask from before the release (`PUT /tasks/<subtask> { assignee : null }`; Darren, 2026-10-28, on a subtask still assigned to him from September). A setup subtask the release needs a person for (switch a vendor on, buy and verify an SMS number) is the exception: it is assigned to the developer with a due date that leaves its lead time before the release's QA (a toll-free number takes about three business days to verify, so its due date is the Monday before the release's date, not the release's date). The release task itself lives in the Releases project only, never in Application or the Backlog, because a release draws its subtasks from both (Darren, 2026-10-22: "releases will include application tasks and backlog items, so their own board makes more sense"). A Backlog subtask keeps its Backlog membership; only Application subtasks get the Queue → In progress move.
69
69
  7. **No pull request yet.** No "Marketing: What's new" subtask yet either: it is created, written and completed in one go at line 10, so developers and QA never see an open marketing item among the subtasks they are building and testing (Darren, 2026-10-22: "we should not have the marketing task in a release until it's completed, as it's confusing for the developers and qa people"). Nothing is opened, drafted or assigned at this point; the developer hears nothing until line 5, when the branch is finished. A pull request opened early reads to the developer as "review this now" and there is nothing to review (Darren, 2026-10-22, after the first run).
70
70
 
71
71
  ## Line 4: build, on Local
@@ -81,10 +81,11 @@ All work on the release branches against the **Local** database. Develop and its
81
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.
82
82
  6. No app pull request opens while a pin still names a version the registry does not have.
83
83
  3. **Per subtask**, on the release branch: read the story and the reference page, `graphify explain` and `graphify affected` on the symbols the story names (the graph is the map; a grep is only for reading a file the graph pointed at), failing test first where a suite exists, the change, `npm test` in that repo. One commit per subtask, its message starting with the subtask id.
84
- 4. **Migration.** If any subtask moves data, ONE file, `drawbridge-api/scripts/migration-<id>.js`, exporting `({ controller }) => async ({ dryRun, organization, ...args }) => counts`, holding every data step. It uses the controller only; a dry run refuses controller writes but cannot refuse a native `collection.drop()` or `dropIndex()`, so a step that drops anything branches on `dryRun` itself and reports what it would drop. Anything a person must do around the run (export a collection and read it back before a drop, take a snapshot) is written into the release doc's Migrations section as the developer's steps, not into the file. Prove it on Local: `npm run migrate -- <id> --dry --env local`, then `--env local`. The runner prints the counts and writes `runs/<env>-<id>-<time>.json`; nothing copies them anywhere.
84
+ 4. **Migration.** If any subtask moves data, ONE file, `drawbridge-api/scripts/migration-<id>.js`, exporting `({ controller }) => async ({ dryRun, organization, ...args }) => counts`, holding every data step. It uses the controller only; a dry run refuses controller writes but cannot refuse a native `collection.drop()` or `dropIndex()`, so a step that drops anything branches on `dryRun` itself and reports what it would drop. Anything a person must do around the run (export a collection and read it back before a drop) is written into the release doc's Migrations section as the developer's steps, not into the file. Prove it on Local: `npm run migrate -- <id> --dry --env local`, then `--env local`. The runner prints the counts and writes `runs/<env>-<id>-<time>.json`; nothing copies them anywhere.
85
85
  5. **Walk every touched app on Local**: `npm run walk -- --local <path or url>` from drawbridge-tests (dashboard at `localhost:3000`; sync must be running for anything live). Record what was seen; a label quoted later comes from this walk.
86
86
  6. **QA agent on Local.** Dispatch `drawbridge-qa` with `target : local`, once per subtask that has a story (story paths, the SUBTASK id, the organization id: `APP_TEST_ORG_ID` in `drawbridge-tests/.env`, the e2e organization `fixtures/e2e-org.md` describes, present on Local because Local is a copy of dev). A subtask spanning many stories gets one report listing them all. Fix every fail on the release branch, redispatch, until every `drawbridge-tests/results/qa/<subtask id>.json` reads `fails : 0`. The fixes are commits in the same pull request; that is why the agent runs here and not on dev (Darren: "all agent work is done locally so the work and fixes get committed as part of the PR").
87
- **Inbound webhooks are the exception.** Every Local Environment points `APP_CLIENT_WEBHOOKS_URI` at `http://localhost:4002`, which Shopify, Stripe and Twilio cannot reach, so a change to how a webhook is received or handled is marked in the Scope table as "webhooks: tested on dev" and the QA person checks it on dev in line 7.
87
+ **Inbound webhooks are walked against dev, by the agent.** Every Local Environment points `APP_CLIENT_WEBHOOKS_URI` at `http://localhost:4002`, which Shopify, Stripe and Twilio cannot reach, so the agent connects to dev for that one step instead of handing it on (Darren, 2026-10-28: "if you need to test a third party integration you might have to connect to the dev environment, but we must always do this"). The vendor is already pointed at dev's webhooks service (every number and store registers dev's URL), so the agent triggers the event there — a STOP text to dev's number, a test order on the dev store, a Stripe test event — and reads what dev wrote (the `buffer` row, the suppression row, the screen). A change to how the event is HANDLED downstream (sync, api) is then walked on Local by copying that dev `buffer` row into Local's `buffer` collection, where Local's sync drains it through the new code. A change to how it is RECEIVED (drawbridge-webhooks itself) cannot run anywhere but dev until it is deployed there, so the agent walks it on dev right after the developer's merge deploys (line 6), before the task moves to QA, and the developer hears from the agent before the tester does. Either way the walk is recorded in the QA table like any other; "webhooks: tested on dev" by the QA person is no longer a row.
88
+ **Nothing else is the exception.** A blocked scenario is a step nobody has run, and a developer never receives one (Darren, 2026-10-28: "I don't want testing with our developers happening on tasks that you haven't validated locally"). Local reaches more than it looks: it shares dev's Twilio, SendGrid, Spaces, Gemini and BrightData keys, so a text can be sent from a number bought on Local, a real bounce can be made by sending a winner email to an address that does not exist, the SendGrid API key on the sendgrid provider row lists and clears that bounce (`GET`/`DELETE /v3/suppression/bounces`; no dashboard login is needed), and a campaign can be generated from a site with a page video. Each of those is a paid run or a vendor action, so it is parked, asked for in the end list with what it unblocks, and walked on Local once granted, before the task moves to Review (Darren, 2026-10-28, granted all three for Cherry). A Twilio number bought on Local lives in the shared account: once sync marks its row `verified`, a developer copies that one identity row into dev's test organization and dev's sync marks it Active on its next sweep, so dev needs no second purchase and no second three-day verification. A second member of the e2e organization (for the read-only scenarios) needs no vendor: invite one on Local and accept from the connected mailbox. **A fail inside a path the release changed is a bug to find, never a note on the list**: Cherry's winner emails did not arrive on Local and the list told the tester to "check the winner email on dev" instead.
88
89
  7. **Review.** `superpowers:code-reviewer` against the stories, then `ponytail-review`, on each repo's diff.
89
90
  8. Push the release branches.
90
91
 
@@ -96,7 +97,7 @@ Only when every subtask that can be built has been built: each committed, `npm t
96
97
 
97
98
  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`.
98
99
  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 approved and cut to its release/ branch." Fill the Branches table in `releases/<id>.md` with the PR links; commit.
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.
100
+ 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 step per check (what the tester does, then what they must see), fifteen steps at most (`drawbridge-asana-comment`; Darren, 2026-10-28, on a 52-step list: "It's insane to expect someone to go through this list"). **Only the paths the subtask changed.** A story's other scenarios (the Klaviyo sync behind an SMS change, the Shopify reconnect behind a campaign picker, the Google accounts behind a logging change) are not walked again for this release (Darren, 2026-10-28: "we don't need unchanged paths"); that is how a list lands under fifteen without folding steps. **Build and log steps are the developer's.** A Dockerfile, a lockfile, a DigitalOcean build log or deployment status, a Sentry issue, an api log line: these go on the developer's list only, never on the QA list (Darren, 2026-10-28: "all build and log steps should be developer only not QA"). **Every step on both lists was run on Local in line 4**, by the agent, with the access the end list obtained; a step that was not run is not written (line 4 step 6). 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 description is rewritten whole at this point**: the original text, the Story lines, the two lists, nothing else. Build notes, "still to do" lines and parked items live in the release document, and one left in a description reads as unfinished work to the developer (Cherry's sample products subtask carried a "Still to do in Cherry" block after all three items were done). The pull request body and the release doc's QA table point at the subtasks for these steps rather than repeating them.
100
101
  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.
101
102
  5. Stop. Report the PR links.
102
103
 
@@ -104,7 +105,7 @@ Only when every subtask that can be built has been built: each committed, `npm t
104
105
 
105
106
  The skill does none of this. For the record, so the skill can answer "what is the next step":
106
107
 
107
- - Line 6, the developer: reviews the diff, runs the branch locally, approves and merges, but merges only when develop is free. Develop holds one release at a time: the review happens whenever the developer likes, on the feature branch locally, and the approved release waits in Review until the one before it is cut at line 8 (Darren, 2026-10-23: "Develop can only go one at a time"; "The Review slot may queue up once the devs have reviewed a release but that's fine"). Merging means the developer has QA'd the branch locally and it is ready (Darren, 2026-10-23). Then the merge, the dev migration, the move to QA and the QA assignment, in that order. Dev deploys itself. If there is a migration: `npm run migrate -- <id> --dry --env dev` from the api checkout on `develop`, read the printed result, then without `--dry`. The developer may have an agent run those two dev commands on their explicit instruction in their own session (Darren, 2026-10-22); the skill never runs them on its own, and no agent ever runs the production one. Then moves the task to QA and assigns the QA person in Asana.
108
+ - Line 6, the developer: reviews the diff, runs the branch locally, approves and merges, but merges only when develop is free. Develop holds one release at a time: the review happens whenever the developer likes, on the feature branch locally, and the approved release waits in Review until the one before it is cut at line 8 (Darren, 2026-10-23: "Develop can only go one at a time"; "The Review slot may queue up once the devs have reviewed a release but that's fine"). Merging means the developer has QA'd the branch locally and it is ready (Darren, 2026-10-23). Then the merge, the dev migration, the move to QA and the QA assignment, in that order. Dev deploys itself. If there is a migration: `npm run migrate -- <id> --dry --env dev` from the api checkout on `develop`, read the printed result, then without `--dry`. The developer may have an agent run those two dev commands on their explicit instruction in their own session (Darren, 2026-10-22); the skill never runs them on its own. Then moves the task to QA and assigns the QA person in Asana.
108
109
  - Line 7, the QA person: tests on dev. The band already names the release.
109
110
  - Line 8, the QA person: moves the task to Approved. Then the developer cuts the release, below.
110
111
 
@@ -125,12 +126,12 @@ The cut stages the approved release for production and frees develop for the nex
125
126
 
126
127
  ## Line 9: promote, as the developer's checklist
127
128
 
128
- **Production is done by a developer, by hand: every push to `main`, every deploy, the migration** (Darren: "All production deployment and migrations will be done manually, you are only to aid a developer deploying to production"). The skill hands over one step at a time, waits for "done", verifies by reading, records, then hands over the next.
129
+ **Production is done by a developer, by hand: every push to `main`, every deploy, the migration** (Darren: "All production deployment and migrations will be done manually, you are only to aid a developer deploying to production"). The one exception is the migration, which an agent runs when the developer tells it to (step 4). The skill hands over one step at a time, waits for "done", verifies by reading, records, then hands over the next.
129
130
 
130
- 1. Confirm the task sits in Approved and `release/<id>` exists in every app in the Branches table (line 8); refuse otherwise.
131
- 2. Hand over the preparation: the window (avoid 00:00 and 02:00 UTC, sync's scheduled jobs) and the Atlas snapshot. Wait.
131
+ 1. **The Atlas snapshot, first.** Before any "Finish release", push to `main`, deploy or migration, hand over the snapshot of the production cluster, taken at the start of the window (avoid 00:00 and 02:00 UTC, sync's scheduled jobs). Wait for "done". Nothing below is handed over until the snapshot exists, and its time goes in the release doc's Promote section (Darren, 2026-10-26: "the snapshot should be run before any production deployment release is done, so deployments, migrations, etc. It should be the first thing").
132
+ 2. Confirm the task sits in Approved and `release/<id>` exists in every app in the Branches table (line 8); refuse otherwise.
132
133
  3. Per app with a `release/<id>` branch, in this order unless the release doc says otherwise (dashboard, api, sync, share, webhooks, website): hand over the client's "Finish release" with the tag `<id>`, which merges `release/<id>` into `main` and into develop and tags it, then the push of `main`, develop and the tag, then the Deploy click in DigitalOcean (prod apps deploy from `main` with deploy-on-push off). Plain git for the finish: `git checkout main && git pull && git merge --no-ff release/<id>`, `git tag <id>`, `git checkout develop && git pull && git merge --no-ff release/<id>`, `git push origin main develop <id>`. The merge into develop keeps develop's band: develop emptied or replaced `release.json` after the cut and the release branch did not touch it. An app with no `release/<id>` is not promoted. Verify by reading: the deployment ACTIVE through the DigitalOcean MCP server registered in Claude Code (`apps-get-deployment-status`; the release doctor checks it is there, and no machine needs doctl), `/health` reporting `main`'s new commit (the merge commit, whose code is the cut commit plus any fix made on the release branch). Note the commit in the release doc's Promote section.
133
- 4. Migration, after the api is ACTIVE and before sync: from the developer's api checkout on `main` at the promoted commit, with the api's two-line `.env.production` beside `migrate.js`: `npm run migrate -- <id> --env prod --dry --sha <promoted api commit>`, read the result, then without `--dry`. (The deployed api container can run it too, from the api component's Console in the DigitalOcean dashboard.) Then `git rm drawbridge-api/scripts/migration-<id>.js`, committed on develop. `main` takes the removal with the next release's finish; never push develop to `main`, which would ship the next release unapproved.
134
+ 4. Migration, after the api is ACTIVE and before sync: from the developer's api checkout on `main` at the promoted commit, with the api's two-line `.env.production` beside `migrate.js`: `npm run migrate -- <id> --env prod --dry --sha <promoted api commit>`, read the result, then without `--dry`. The production cluster is IP-restricted: before the first local run, the developer adds their own IP range to its Network Access list in Atlas, or the runner cannot connect. When the developer tells it to, the agent runs both commands in the developer's session: it reads the dry run's counts file (`runs/prod-<id>-<time>.json`, never the console), checks the release doc's Migrations steps are done (exports read back, for example), shows the developer what the real run will do, and runs it only on their yes (Darren, 2026-10-26: "The agent does when the developer tells it to"). Only a machine without `.env.production` uses the fallback, the deployed api container's Console in the DigitalOcean dashboard. Then `git rm drawbridge-api/scripts/migration-<id>.js`, committed on develop. `main` takes the removal with the next release's finish; never push develop to `main`, which would ship the next release unapproved.
134
135
  5. Hand over the verification list from the release doc's Promote section; the developer reports what they saw; the skill fills it in.
135
136
 
136
137
  ## Line 10: Released
@@ -141,12 +142,14 @@ The cut stages the approved release for production and frees develop for the nex
141
142
 
142
143
  ## What the skill never does
143
144
 
144
- - Push `main`, cut or finish a `release/` branch, 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.
145
+ - Push `main`, cut or finish a `release/` branch, 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). A migration, on dev or on production, runs on a developer's explicit instruction, by the developer or by an agent they ask; the production one only after the snapshot.
145
146
  - 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.
146
147
  - 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.
147
148
  - 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.
148
149
  - Write a release document, branch or migration under any name but the Asana id.
149
150
  - Build or run the QA agent against dev or develop before the pull requests merge. Local only.
151
+ - Hand a developer or a tester a step the agent did not run on Local. A blocked scenario is parked and asked for, never written into a test list with a note (line 4 step 6).
152
+ - Put a build, lockfile, deployment, log or Sentry step on the QA list. Those are the developer's (line 5 step 3).
150
153
 
151
154
  ## Section ids
152
155
 
package/bin/bump.js CHANGED
@@ -88,7 +88,7 @@ const bumpOne = ({ projectsDir, repo, field, current, name, version, dry, push }
88
88
  const files = [ 'package.json', 'package-lock.json' ]
89
89
  if (name === '@drawbridge/drawbridge-agents') {
90
90
  try { run(dir, 'npx', [ 'drawbridge-agents-sync' ]) } catch (error) { /* the mirror is best-effort; the pin still lands */ }
91
- files.push('.claude')
91
+ files.push('.claude', '.nvmrc')
92
92
  }
93
93
  run(dir, 'git', [ 'add', '-A', ...files.filter((f) => fs.existsSync(path.join(dir, f))) ])
94
94
  if (!run(dir, 'git', [ 'status', '--porcelain', '--', ...files ])) { console.log(repo + ': nothing to commit'); return }
@@ -61,16 +61,22 @@ quietly that dev served stale code the whole time.
61
61
  only place that needs this, and locally it's there"):
62
62
  `npm run migrate -- <release asana id> --env prod --dry --sha <promoted commit>`,
63
63
  then the same without `--dry`. The runner refuses production off `main`, at
64
- the wrong commit, or with `scripts/`, `lib/` or `migrate.js` modified. The
65
- deployed api container can run it too (the api component's Console in the
66
- DigitalOcean dashboard, then the `op run --environment "$OP_ENVIRONMENT_ID" --
67
- node migrate.js …` form; nobody installs doctl for this; the image carries `migrate.js` and `scripts/`, there is no
68
- git, so `--sha` is proven against `APP_COMMIT_HASH`). An agent never runs it:
69
- it hands the developer the command and verifies by reading afterwards. The
70
- dev run is different: a developer may ask an agent in their own session to run
71
- the two `--env dev` commands and show the result, since the guard admits only
72
- the dev cluster whoever types it (Darren, 2026-10-22). Never unasked, and
73
- never production.
64
+ the wrong commit, or with `scripts/`, `lib/` or `migrate.js` modified.
65
+ **The production cluster is IP-restricted**: before the first local run,
66
+ each developer adds their own IP range to the production cluster's Network
67
+ Access list in Atlas, or the runner cannot connect. Only a machine without
68
+ `.env.production` uses the fallback, the deployed api container (the api
69
+ component's Console in the DigitalOcean dashboard, then the
70
+ `op run --environment "$OP_ENVIRONMENT_ID" -- node migrate.js …` form; nobody
71
+ installs doctl for this; the image carries `migrate.js` and `scripts/`, there
72
+ is no git, so `--sha` is proven against `APP_COMMIT_HASH`).
73
+ An agent runs a migration only when the developer tells it to, in the
74
+ developer's own session: the two `--env dev` commands (Darren, 2026-10-22),
75
+ and the production ones from the developer's checkout once the Atlas snapshot
76
+ is done (Darren, 2026-10-26: "The agent does when the developer tells it
77
+ to"). It reads the dry run's counts file and shows the developer what the
78
+ real run will do, and it waits for the developer's yes before the real run.
79
+ Never unasked.
74
80
  - **The runner records nothing but its counts file**, and nothing else records
75
81
  a run either: a developer runs the migration on dev after the pull request
76
82
  merges and reads the printed result, and runs it on production from their
@@ -37,6 +37,6 @@
37
37
 
38
38
  Why: a confident sentence with nothing behind it reads exactly like a verified one, and the reader builds on it. It has sent a tester hunting for a badge that did not exist, described a repo as missing code that was in another package, and promised an edit that had not happened. Every one of those was cheaper to ask about than to correct afterwards.
39
39
 
40
- 9. Write in the spirit of ASD-STE100. One idea per sentence, with an active verb, in about 20 words. A procedural step is one action: "Open the campaign. Click Draw." never "Open the campaign and click Draw". A condition or warning comes before the step it applies to, never after. One word has one meaning for the whole reply: do not swap in synonyms for variety, and write "must" for a requirement and "we recommend" for advice, never "should". The STE dictionary is not in force; use the words the team uses (rule 7).
40
+ 9. Write in the spirit of ASD-STE100. One idea per sentence, with an active verb, in about 20 words. A sentence carries one action: "Open the campaign. Click Draw." never "Open the campaign and click Draw". A retest step is one check, the sentences for what they do and one for what they must see, and a retest list has at most fifteen steps (the `drawbridge-asana-comment` skill). A condition or warning comes before the step it applies to, never after. One word has one meaning for the whole reply: do not swap in synonyms for variety, and write "must" for a requirement and "we recommend" for advice, never "should". The STE dictionary is not in force; use the words the team uses (rule 7).
41
41
 
42
42
  Why: STE exists so that a reader who skims under pressure cannot misread a step. That is Jaime reading a retest list and Darren reading a question. Darren, 2026-10-21, asked for the spirit of STE and not the letter.
@@ -122,6 +122,22 @@ Claude can walk it as you. Vendor reference: https://code.claude.com/docs/en/chr
122
122
  This path is not available to a headless or SDK session, which is why the Playwright walk
123
123
  above is the one every handoff relies on.
124
124
 
125
+ ## A vendor step Local cannot reach is walked against dev, not parked
126
+
127
+ Every update built on Local is walked before it is reported, with no exception for the
128
+ step a vendor has to take part in (Darren, 2026-10-28: "make sure you have walked all
129
+ updates when working locally. If you need to test a third party integration you might
130
+ have to connect to the dev environment, but we must always do this"). Local shares dev's
131
+ vendor keys, so most vendor steps run from Local directly: a text from a number bought on
132
+ Local, a winner email to an address that does not exist for a real bounce, a campaign
133
+ generated from a live site. The step Local cannot run is the one the vendor delivers
134
+ inbound (a Twilio STOP, a Shopify order, a Stripe event), because every Local Environment
135
+ points the webhooks URL at `localhost`. For that step the agent connects to dev: trigger
136
+ the event against dev's number, store or account, read what dev wrote, and replay the
137
+ captured `buffer` row into Local so the local sync handles it through the new code. The
138
+ walk is recorded like any other. "Tested on dev by QA" or "blocked" is never the record
139
+ for a step the agent could have reached on dev.
140
+
125
141
  ## What is enforced
126
142
 
127
143
  - The `drawbridge-asana-ship-handoff` skill requires the walk before a handoff comment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawbridge/drawbridge-agents",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Shared agent-instruction content (rules, code style, conventions) for the drawbridge-* monorepo.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {