@drawbridge/drawbridge-agents 0.1.81 → 0.1.83
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-template/skills/drawbridge-release/SKILL.md +45 -25
- package/bin/bump.js +29 -15
- package/bin/release-doctor.js +11 -1
- package/package.json +1 -1
|
@@ -10,11 +10,11 @@ One Asana release task is the unit. Its id names everything: the `feature/<id>`
|
|
|
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
12
|
1. A person gives the skill the release task link. The skill checks `gh auth status`.
|
|
13
|
-
2. The skill reads the task and subtasks, writes the stories, sends ONE numbered list and waits for the yes.
|
|
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
|
-
4. The skill builds on the release branches against the Local database, with the QA agent run locally.
|
|
16
|
-
5. One
|
|
17
|
-
6. The developer reviews, runs the branch locally, merges; runs any migration on dev; moves the task → QA and assigns the QA person.
|
|
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.
|
|
16
|
+
5. One end list: the parked questions, the access requests, the publish ask, the pull requests that are ready. The person answers, the skill closes out the rest, task → Review.
|
|
17
|
+
6. The developer reviews, runs the branch locally, merges (only when no other release is in QA or Approved); runs any migration on dev; moves the task → QA and assigns the QA person.
|
|
18
18
|
7. The QA person tests on dev.
|
|
19
19
|
8. The QA person moves the task → Approved.
|
|
20
20
|
9. The developer promotes by hand, with the skill as checklist.
|
|
@@ -26,36 +26,52 @@ Read the release task and every subtask through `drawbridge-tests/lib/asana.js`
|
|
|
26
26
|
|
|
27
27
|
## Line 2: the one list
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**The graph first, grep last.** `git -C ../drawbridge-docs pull` brings the committed knowledge graph (`drawbridge-docs/knowledge/graphs/global.json`, rebuilt at the end of every release); it already knows the family, so a release does not rediscover it by searching sixteen repos from scratch (Darren, 2026-10-22: "make sure we are leveraging the knowledge graphs and not always searching each time we start from develop. That is the point of having that feature"). For every subtask, before any grep: `graphify query "<the subtask in words>"`, then `graphify explain <symbol>` and `graphify affected <symbol>` for the handlers, fields or components it names, all with `--graph ../drawbridge-docs/knowledge/graphs/global.json`. The answers give the repos and files the subtask touches and the cross-repo contracts in play; that list is the Scope table at line 3. Grep only inside a file the graph named, to read it, never across repos to find it.
|
|
30
|
+
|
|
31
|
+
**Every subtask has a story; no exceptions.** For every subtask, find the existing story or write one (`drawbridge-feature` step 1), from what the graph showed: a bug fix gets the story whose scenario it restores, a new behaviour gets a new story, an internal change gets the story of the behaviour it must keep working. A subtask that ends line 2 with no story is a stop condition, not a gap to fill later: the QA person tests from the story, so a subtask without one has nothing to test against (Darren, 2026-10-22: "all subtasks in a release create or link to an existing story, so there is context about what needs to be tested"). Then ONE numbered list to the person who started the release, each item three plain sentences (`conventions/rules.md` rule 7), holding:
|
|
30
32
|
|
|
31
33
|
- every unknown from every story;
|
|
32
34
|
- **who on the development team takes the pull requests**: their GitHub username (for the pull requests) and their Asana name (for the task). Asked now so nobody needs to be present between lines 3 and 5. Never guess a reviewer;
|
|
33
|
-
- **a package publish, if any subtask names `@drawbridge/components` or `@drawbridge/drawbridge-utils`**,
|
|
35
|
+
- **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, in the morning. Line 4 step 2 is the exact sequence.
|
|
36
|
+
|
|
37
|
+
**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").
|
|
38
|
+
|
|
39
|
+
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.
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
## From the yes to the end list: nobody is asked anything
|
|
42
|
+
|
|
43
|
+
A release runs at night (Darren, 2026-10-22: "all comments, prompts, access, etc. are asked after all code that can be written has been written. This way we can have a release run at night, without interruption"). So between the yes and line 5:
|
|
44
|
+
|
|
45
|
+
1. **No question, no comment, no request.** The line 3 writes to Asana (section moves, the story on each subtask) happen right after the yes and ask nothing; after them, no question to the person, no Asana comment, no publish ask, no access request: not for a missing credential, not for an MCP server that is not connected, not for a tool a hook or a permission refuses. The rule "ask before writing a line" (`conventions/rules.md` rule 1) was satisfied at line 2; a question born mid-build is parked, not asked.
|
|
46
|
+
2. **Park it, build the rest.** Anything that needs a person is written into the release doc's Parked section (`releases/<id>.md`, committed on the docs branch so it survives a long run): the subtask, what exactly is needed, in the three-sentence shape of rule 7, ready to paste into the end list. Then build every subtask that does not depend on it.
|
|
47
|
+
3. **Be optimistic about access.** When something is missing (a token, a connected service, a tool), write the code as if it will be granted, test what can be tested without it, and park the one step that cannot run (Darren: "lets be optimistic that access can be granted so work isn't held up"). The parked line names what the grant unblocks, so the morning close-out finishes that step and nothing else.
|
|
48
|
+
4. **Permissions never prompt.** The shared `.claude/settings.json` the agents package mirrors into every repo allows every Bash command, file writes, web fetches and every MCP tool; the hooks that refuse a step (read-before-write, drift, QA gate) return an error the skill handles. The release doctor confirms the mirrored settings file is present at line 1, so a machine that would prompt fails before the run, not at 2 am.
|
|
49
|
+
5. **The Local refresh asks nothing.** It runs once, at the start of line 4, and is skipped when the Releases board shows another release in Building, because that release's walk may be mid-flight on the same Local database. Then the build runs on Local as it stands.
|
|
50
|
+
6. **Everything owed to a person comes out once**, as the end list in line 5, after every subtask that can be built has been built.
|
|
36
51
|
|
|
37
52
|
## Line 3: Building
|
|
38
53
|
|
|
39
|
-
1. **
|
|
40
|
-
2. `feature/<id>` in every repo a subtask touches, off `develop` (components and utils off `main`): a git-flow FEATURE branch, never `release/<id>`, because the repos carry git-flow config and a client's "Finish release" merges into `main` with a tag, which would put unapproved code on main before dev QA; "Finish feature" merges into `develop`, which is what this flow does (Darren, 2026-10-22; Almond keeps its older `release/` name). Each in a worktree under `/Users/darrenshea/Projects/.worktrees/<repo>-feature-<id>` so no main checkout changes branch. **drawbridge-docs is the exception**: its `feature/<id>` branch is made in the main docs checkout (docs is `main`-only and single-writer), because `npx drawbridge-agents-check-stories` reads nothing from a worktree path (Asana 1218852240225835). "Repo a subtask touches" includes docs, components and utils, not only the six apps.
|
|
54
|
+
1. **Many releases build at once; one at a time reaches develop.** List the project's tasks (`GET /projects/1219601041167594/tasks?opt_fields=name,memberships.section.name`). Other releases in Building or Review are fine: every release is built on its own branches against the Local database, so they do not touch each other (Darren, 2026-10-22: "We should be able to do multiple releases at once since the code is all being done locally anyways"). Say which ones are in flight and go on. The lane that holds one release is develop: a release in QA or Approved owns dev until it is Released, and the developer merges no other release's pull requests meanwhile (line 6). This step never blocks on it; the merge does. Then move this task to Building (`POST /sections/1219601033297172/addTask { task }`).
|
|
55
|
+
2. `feature/<id>` in every repo a subtask touches, off `develop` (components and utils off `main`): a git-flow FEATURE branch, never `release/<id>`, because the repos carry git-flow config and a client's "Finish release" merges into `main` with a tag, which would put unapproved code on main before dev QA; "Finish feature" merges into `develop`, which is what this flow does (Darren, 2026-10-22; Almond keeps its older `release/` name). Each in a worktree under `/Users/darrenshea/Projects/.worktrees/<repo>-feature-<id>` so no main checkout changes branch. **drawbridge-docs is the exception**: its `feature/<id>` branch is made in the main docs checkout (docs is `main`-only and single-writer), because `npx drawbridge-agents-check-stories` reads nothing from a worktree path (Asana 1218852240225835). **The stories do not wait on that branch.** They are committed and pushed to docs `main` here, before any code, straight from the main docs checkout (`git -C ../drawbridge-docs push origin main`): a story is the spec, text only, nothing deploys from docs, and the feature pipeline writes the story before the first edit anyway. That is what makes the story links in step 5 work the moment they are added (Darren, 2026-10-22: "Story links will need to be added after the docs repo has been pushed on main so the links work"). The docs `feature/<id>` branch then carries only what rides with the code: `releases/<id>.md`, reference pages, a story edited again during the build. "Repo a subtask touches" includes docs, components and utils, not only the six apps.
|
|
41
56
|
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.
|
|
42
|
-
4.
|
|
43
|
-
5.
|
|
44
|
-
6.
|
|
57
|
+
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.
|
|
58
|
+
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.
|
|
59
|
+
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.
|
|
60
|
+
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).
|
|
45
61
|
|
|
46
62
|
## Line 4: build, on Local
|
|
47
63
|
|
|
48
64
|
All work on the release branches against the **Local** database. Develop and its schema are never touched while a release is being built, and one release at a time is promoted to develop.
|
|
49
65
|
|
|
50
|
-
1. **Local up.** `npm run migrate -- refresh` in the api (copies dev into Local; `--dry` first),
|
|
51
|
-
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
|
|
66
|
+
1. **Local up.** `npm run migrate -- refresh` in the api (copies dev into Local; `--dry` first). There is ONE Local database, so when the Releases board shows another release in Building, skip the refresh and build on Local as it stands: a refresh would reset that release's data mid-walk, and nobody is asked. Then `npm run local` in every app the release touches: dashboard 3000, share 3001, website 3002, api 4000, sync 4001, webhooks 4002; the components app (`npm run app` in drawbridge-components) is 3003. The scripts force the ports; nothing sets them.
|
|
67
|
+
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):
|
|
52
68
|
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.
|
|
53
|
-
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.
|
|
54
|
-
3.
|
|
69
|
+
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.
|
|
70
|
+
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).
|
|
55
71
|
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.
|
|
56
|
-
5. When `npm view <package> version` shows the new version (
|
|
72
|
+
5. When `npm view <package> version` shows the new version (in the morning, 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.
|
|
57
73
|
6. No app pull request opens while a pin still names a version the registry does not have.
|
|
58
|
-
3. **Per subtask**, on the release branch: read the story and the reference page,
|
|
74
|
+
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.
|
|
59
75
|
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.
|
|
60
76
|
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.
|
|
61
77
|
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").
|
|
@@ -63,20 +79,23 @@ All work on the release branches against the **Local** database. Develop and its
|
|
|
63
79
|
7. **Review.** `superpowers:code-reviewer` against the stories, then `ponytail-review`, on each repo's diff.
|
|
64
80
|
8. Push the release branches.
|
|
65
81
|
|
|
66
|
-
## Line 5: Review
|
|
82
|
+
## Line 5: the end list, then Review
|
|
83
|
+
|
|
84
|
+
Only when every subtask that can be built has been built: each committed, `npm test` green in each repo, every QA report at `fails : 0`, both review passes done, the branches pushed. A pull request is the request to review, so it exists only once there is something to review.
|
|
67
85
|
|
|
68
|
-
|
|
86
|
+
**First, the end list.** ONE numbered list to the person who started the release, the first words anyone has heard since the yes (Darren, 2026-10-22: "ask finalizing questions / request access to complete then"). In this order: every parked question, every access request with what it unblocks, the publish ask when there is a package, the subtasks not built and why, and the repos whose pull requests are ready to open. Each item three plain sentences (rule 7). Then, in the same message, open the pull requests that depend on nothing parked (steps 1 to 3 below) and stop. When the answers and grants arrive (the morning), finish the parked steps on the release branches, bump the pins once the registry has the version, run the tests and the QA agent on what changed, open the remaining pull requests, and only then move the task to Review. This close-out is the end of the coding segment of a release; nothing in it is asked for a second time.
|
|
69
87
|
|
|
70
88
|
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`.
|
|
71
|
-
2. Fill the Branches table in `releases/<id>.md` with the PR links; commit.
|
|
72
|
-
3.
|
|
73
|
-
4.
|
|
89
|
+
2. Every pull request body ends with the lane rule in one line: "Merge only when no other release is in QA or Approved on the Releases board; dev holds one release at a time." Fill the Branches table in `releases/<id>.md` with the PR links; commit.
|
|
90
|
+
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 (dashboard `https://dev.dashboard.trydrawbridge.com/...`, 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.
|
|
91
|
+
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.
|
|
92
|
+
5. Stop. Report the PR links.
|
|
74
93
|
|
|
75
94
|
## Lines 6 to 8: the developer and the QA person
|
|
76
95
|
|
|
77
96
|
The skill does none of this. For the record, so the skill can answer "what is the next step":
|
|
78
97
|
|
|
79
|
-
- Line 6, the developer: reviews the diff, runs the branch locally, approves and merges. 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.
|
|
98
|
+
- Line 6, the developer: reviews the diff, runs the branch locally, approves and merges, but only when no other release sits in QA or Approved: dev carries one release at a time, and a second merge would put two releases' code in front of the QA person under one band (Darren, 2026-10-22: "only 1 QA release can hit develop at any given time"). 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.
|
|
80
99
|
- Line 7, the QA person: tests on dev. The band already names the release.
|
|
81
100
|
- Line 8, the QA person: moves the task to Approved.
|
|
82
101
|
|
|
@@ -94,11 +113,12 @@ The skill does none of this. For the record, so the skill can answer "what is th
|
|
|
94
113
|
|
|
95
114
|
1. **What's new.** Create the "Marketing: What's new" subtask on the release task now (`POST /tasks/<id>/subtasks`), with the marketing breakdown as its description, in the customer's words, one item per subtask a merchant would notice (`drawbridge-asana-comment` for the shape, `humanizer` for the words), and complete it in the same step (`PUT /tasks/<subtask> { completed : true }`). It exists only finished; a release never carries an open marketing subtask.
|
|
96
115
|
2. Complete every subtask (Application → Complete). Move the release task to Released (`POST /sections/1219601137919036/addTask`).
|
|
97
|
-
3. Remove the worktrees; refresh the knowledge graph (`drawbridge-ship-feature` step 5). Report the promoted commits.
|
|
116
|
+
3. Remove the worktrees; refresh the knowledge graph and commit it in drawbridge-docs (`drawbridge-ship-feature` step 5), so the next release starts from what this one learned instead of searching for it. Report the promoted commits.
|
|
98
117
|
|
|
99
118
|
## What the skill never does
|
|
100
119
|
|
|
101
120
|
- 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.
|
|
121
|
+
- 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.
|
|
102
122
|
- Name a reviewer or a release scope that nobody told it; both are asked in line 2. The tester is the developer's choice at line 6 and the window is the developer's at line 9; the skill asks for neither.
|
|
103
123
|
- Write a release document, branch or migration under any name but the Asana id.
|
|
104
124
|
- Build or run the QA agent against dev or develop before the pull requests merge. Local only.
|
package/bin/bump.js
CHANGED
|
@@ -66,24 +66,38 @@ const main = () => {
|
|
|
66
66
|
process.exit(1)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
let failed = 0
|
|
69
70
|
for (const { repo, field, current } of consumers) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!takesBump(branch)) { console.log(repo + ': skipped, on ' + (branch || 'a detached head') + ' (check out develop, main or feature/<asana id> first)'); continue }
|
|
74
|
-
if (dry) { console.log(repo + ': ' + current + ' -> ' + version + ' (' + field + ', ' + branch + ')'); continue }
|
|
75
|
-
run(dir, 'npm', [ 'install', '--save-exact', saveFlag(field), name + '@' + version, '--no-audit', '--no-fund', '--loglevel=error' ])
|
|
76
|
-
const files = [ 'package.json', 'package-lock.json' ]
|
|
77
|
-
if (name === '@drawbridge/drawbridge-agents') {
|
|
78
|
-
try { run(dir, 'npx', [ 'drawbridge-agents-sync' ]) } catch (error) { /* the mirror is best-effort; the pin still lands */ }
|
|
79
|
-
files.push('.claude')
|
|
71
|
+
try { bumpOne({ projectsDir, repo, field, current, name, version, dry, push }) } catch (error) {
|
|
72
|
+
failed += 1
|
|
73
|
+
console.error(repo + ': FAILED, ' + String(error.stderr || error.message).trim().split('\n').pop() + ' (fix by hand, then re-run; done repos are skipped as already ' + version + ')')
|
|
80
74
|
}
|
|
81
|
-
run(dir, 'git', [ 'add', '-A', ...files.filter((f) => fs.existsSync(path.join(dir, f))) ])
|
|
82
|
-
if (!run(dir, 'git', [ 'status', '--porcelain', '--', ...files ])) { console.log(repo + ': nothing to commit'); continue }
|
|
83
|
-
run(dir, 'git', [ 'commit', '-q', '-m', name.replace('@drawbridge/', '') + ' ' + version ])
|
|
84
|
-
if (push) run(dir, 'git', [ 'push', '-q', 'origin', branch ])
|
|
85
|
-
console.log(repo + ': ' + current + ' -> ' + version + ' on ' + branch + (push ? ', pushed' : ''))
|
|
86
75
|
}
|
|
76
|
+
if (failed) process.exit(1)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// One repo: install the pin, mirror the template when it is the agents package,
|
|
80
|
+
// commit, pull --rebase so a branch that moved meanwhile does not reject the push.
|
|
81
|
+
const bumpOne = ({ projectsDir, repo, field, current, name, version, dry, push }) => {
|
|
82
|
+
const dir = path.join(projectsDir, repo)
|
|
83
|
+
const branch = run(dir, 'git', [ 'branch', '--show-current' ])
|
|
84
|
+
if (current === version) { console.log(repo + ': already ' + version + ' (' + field + ', ' + branch + ')'); return }
|
|
85
|
+
if (!takesBump(branch)) { console.log(repo + ': skipped, on ' + (branch || 'a detached head') + ' (check out develop, main or feature/<asana id> first)'); return }
|
|
86
|
+
if (dry) { console.log(repo + ': ' + current + ' -> ' + version + ' (' + field + ', ' + branch + ')'); return }
|
|
87
|
+
run(dir, 'npm', [ 'install', '--save-exact', saveFlag(field), name + '@' + version, '--no-audit', '--no-fund', '--loglevel=error' ])
|
|
88
|
+
const files = [ 'package.json', 'package-lock.json' ]
|
|
89
|
+
if (name === '@drawbridge/drawbridge-agents') {
|
|
90
|
+
try { run(dir, 'npx', [ 'drawbridge-agents-sync' ]) } catch (error) { /* the mirror is best-effort; the pin still lands */ }
|
|
91
|
+
files.push('.claude')
|
|
92
|
+
}
|
|
93
|
+
run(dir, 'git', [ 'add', '-A', ...files.filter((f) => fs.existsSync(path.join(dir, f))) ])
|
|
94
|
+
if (!run(dir, 'git', [ 'status', '--porcelain', '--', ...files ])) { console.log(repo + ': nothing to commit'); return }
|
|
95
|
+
run(dir, 'git', [ 'commit', '-q', '-m', name.replace('@drawbridge/', '') + ' ' + version ])
|
|
96
|
+
if (push) {
|
|
97
|
+
run(dir, 'git', [ 'pull', '-q', '--rebase', 'origin', branch ])
|
|
98
|
+
run(dir, 'git', [ 'push', '-q', 'origin', branch ])
|
|
99
|
+
}
|
|
100
|
+
console.log(repo + ': ' + current + ' -> ' + version + ' on ' + branch + (push ? ', pushed' : ''))
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
module.exports = { consumersOf, readManifests, saveFlag, takesBump }
|
package/bin/release-doctor.js
CHANGED
|
@@ -38,6 +38,15 @@ const envFileOk = (text) => {
|
|
|
38
38
|
const readClaudeConfig = () => { try { return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.claude.json'), 'utf8')) } catch (error) { return null } }
|
|
39
39
|
const hasDigitalOcean = (config) => Boolean(config && config.mcpServers && config.mcpServers.digitalocean && config.mcpServers.digitalocean.url)
|
|
40
40
|
|
|
41
|
+
// The mirrored settings file is what keeps an unattended run from prompting: the shared
|
|
42
|
+
// allow list covers Bash, file writes, web fetches and every MCP tool.
|
|
43
|
+
const settingsAllowAll = (text) => {
|
|
44
|
+
try {
|
|
45
|
+
const allow = JSON.parse(text).permissions.allow
|
|
46
|
+
return [ 'Bash(*)', 'Write', 'Edit', 'mcp__*' ].every((rule) => allow.includes(rule))
|
|
47
|
+
} catch (error) { return false }
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
// The printed list: one numbered line per failure, each with its fix; nothing when all pass.
|
|
42
51
|
const report = (results) => {
|
|
43
52
|
const failed = results.filter((r) => !r.ok)
|
|
@@ -54,6 +63,7 @@ const checks = (projectsDir) => {
|
|
|
54
63
|
{ name: 'op with --environment (beta channel)', required: true, ok: /--environment/.test(sh('op', [ 'run', '--help' ]) || ''), fix: 'install the 1Password CLI beta: see drawbridge-growth/scripts/op.sh for the curl line' },
|
|
55
64
|
{ name: 'drawbridge-api/.env.local', required: true, ok: envFileOk(read('.env.local')), fix: 'two lines, OP_SERVICE_ACCOUNT_TOKEN and OP_ENVIRONMENT_ID, naming the Local Environment' },
|
|
56
65
|
{ name: 'drawbridge-api/.env.development', required: true, ok: envFileOk(read('.env.development')), fix: 'two lines naming the Development Environment' },
|
|
66
|
+
{ name: 'drawbridge-api/.claude/settings.json allows every tool', required: true, ok: settingsAllowAll(read(path.join('.claude', 'settings.json'))), fix: 'npx drawbridge-agents-sync in drawbridge-api mirrors the shared settings; without the allow list an overnight run stops on a permission prompt' },
|
|
57
67
|
{ name: 'drawbridge-api/scripts/op.sh', required: true, ok: fs.existsSync(path.join(api, 'scripts', 'op.sh')), fix: 'pull develop in drawbridge-api' },
|
|
58
68
|
{ name: 'npm registry', required: true, ok: Boolean(sh('npm', [ 'view', '@drawbridge/drawbridge-agents', 'version' ])), fix: 'check the network or npm login' },
|
|
59
69
|
{ name: 'DigitalOcean MCP server in Claude Code', required: true, ok: hasDigitalOcean(readClaudeConfig()), fix: 'claude mcp add --scope user --transport http digitalocean https://apps.mcp.digitalocean.com/mcp --header "Authorization: Bearer <your DigitalOcean API token>" (the skill reads deployments through it; no doctl)' },
|
|
@@ -72,6 +82,6 @@ const main = async () => {
|
|
|
72
82
|
process.exit(results.some((r) => r.required && !r.ok) ? 1 : 0)
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
module.exports = { checks, envFileOk, hasDigitalOcean, report }
|
|
85
|
+
module.exports = { checks, envFileOk, hasDigitalOcean, report, settingsAllowAll }
|
|
76
86
|
|
|
77
87
|
if (require.main === module) main()
|
package/package.json
CHANGED