@bluewombat/manager-github 0.8.0 → 0.10.0

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.
Files changed (96) hide show
  1. package/README.md +188 -33
  2. package/SPECS.md +60 -24
  3. package/dist/adapter/normalize/intent.d.ts +3 -3
  4. package/dist/adapter/normalize/intent.d.ts.map +1 -1
  5. package/dist/adapter/normalize/intent.js +4 -4
  6. package/dist/adapter/normalize/intent.js.map +1 -1
  7. package/dist/adapter/normalize/normalize.d.ts +5 -1
  8. package/dist/adapter/normalize/normalize.d.ts.map +1 -1
  9. package/dist/adapter/normalize/normalize.js +5 -1
  10. package/dist/adapter/normalize/normalize.js.map +1 -1
  11. package/dist/adapter/run/run-adapter.d.ts +1 -1
  12. package/dist/adapter/run/run-adapter.d.ts.map +1 -1
  13. package/dist/adapter/run/run-adapter.js +29 -2
  14. package/dist/adapter/run/run-adapter.js.map +1 -1
  15. package/dist/adapter/types.d.ts +13 -3
  16. package/dist/adapter/types.d.ts.map +1 -1
  17. package/dist/adapter/types.js +1 -1
  18. package/dist/adapter/types.js.map +1 -1
  19. package/dist/emitter/event/field-matrix.d.ts +1 -1
  20. package/dist/emitter/event/field-matrix.d.ts.map +1 -1
  21. package/dist/emitter/event/field-matrix.js +5 -9
  22. package/dist/emitter/event/field-matrix.js.map +1 -1
  23. package/dist/emitter/github/labels.d.ts +3 -4
  24. package/dist/emitter/github/labels.d.ts.map +1 -1
  25. package/dist/emitter/github/labels.js +9 -9
  26. package/dist/emitter/github/labels.js.map +1 -1
  27. package/dist/emitter/render/render-event.d.ts.map +1 -1
  28. package/dist/emitter/render/render-event.js +13 -42
  29. package/dist/emitter/render/render-event.js.map +1 -1
  30. package/dist/emitter/run/run-emitter.d.ts +1 -1
  31. package/dist/emitter/run/run-emitter.js +3 -3
  32. package/dist/emitter/thread/marker.d.ts +1 -1
  33. package/dist/emitter/thread/marker.d.ts.map +1 -1
  34. package/dist/emitter/thread/marker.js +1 -1
  35. package/dist/emitter/thread/marker.js.map +1 -1
  36. package/dist/emitter/types.d.ts +8 -10
  37. package/dist/emitter/types.d.ts.map +1 -1
  38. package/dist/emitter/types.js +1 -1
  39. package/dist/emitter/types.js.map +1 -1
  40. package/dist/github/blockers.d.ts +24 -0
  41. package/dist/github/blockers.d.ts.map +1 -0
  42. package/dist/github/blockers.js +50 -0
  43. package/dist/github/blockers.js.map +1 -0
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +164 -45
  47. package/dist/index.js.map +1 -1
  48. package/dist/label-colors.d.ts +26 -0
  49. package/dist/label-colors.d.ts.map +1 -0
  50. package/dist/label-colors.js +51 -0
  51. package/dist/label-colors.js.map +1 -0
  52. package/dist/listener/delivery/process-tree.d.ts +1 -1
  53. package/dist/listener/delivery/process-tree.js +1 -1
  54. package/dist/listener/run/run-listener.d.ts +1 -1
  55. package/dist/listener/run/run-listener.js +1 -1
  56. package/dist/listener/source/select-events.d.ts +6 -1
  57. package/dist/listener/source/select-events.d.ts.map +1 -1
  58. package/dist/listener/source/select-events.js +9 -2
  59. package/dist/listener/source/select-events.js.map +1 -1
  60. package/dist/listener/types.d.ts +1 -1
  61. package/dist/listener/types.d.ts.map +1 -1
  62. package/dist/listener/types.js +1 -1
  63. package/dist/listener/types.js.map +1 -1
  64. package/dist/manager.d.ts +28 -8
  65. package/dist/manager.d.ts.map +1 -1
  66. package/dist/manager.js +152 -95
  67. package/dist/manager.js.map +1 -1
  68. package/dist/settled.d.ts +5 -4
  69. package/dist/settled.d.ts.map +1 -1
  70. package/dist/settled.js +7 -7
  71. package/dist/settled.js.map +1 -1
  72. package/dist/setup.d.ts +33 -8
  73. package/dist/setup.d.ts.map +1 -1
  74. package/dist/setup.js +117 -19
  75. package/dist/setup.js.map +1 -1
  76. package/dist/state-labels.d.ts +23 -0
  77. package/dist/state-labels.d.ts.map +1 -0
  78. package/dist/state-labels.js +55 -0
  79. package/dist/state-labels.js.map +1 -0
  80. package/dist/submission/lines.d.ts +19 -0
  81. package/dist/submission/lines.d.ts.map +1 -0
  82. package/dist/submission/lines.js +91 -0
  83. package/dist/submission/lines.js.map +1 -0
  84. package/dist/submission/submission.d.ts +16 -6
  85. package/dist/submission/submission.d.ts.map +1 -1
  86. package/dist/submission/submission.js +53 -7
  87. package/dist/submission/submission.js.map +1 -1
  88. package/dist/tickets/list-tickets.d.ts +29 -0
  89. package/dist/tickets/list-tickets.d.ts.map +1 -0
  90. package/dist/tickets/list-tickets.js +98 -0
  91. package/dist/tickets/list-tickets.js.map +1 -0
  92. package/dist/written-fingerprint.d.ts +2 -1
  93. package/dist/written-fingerprint.d.ts.map +1 -1
  94. package/dist/written-fingerprint.js +3 -3
  95. package/dist/written-fingerprint.js.map +1 -1
  96. package/package.json +3 -3
package/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # Manager: GitHub Issues
2
2
 
3
- The FeatureManager triplet for GitHub Issues: issues in, issue comments (and
4
- optional state labels) out, over the REST API with a token.
3
+ The FeatureTracker and TicketTracker for GitHub Issues: a Feature is an issue
4
+ carrying `feature-ready`, its Tickets are its sub-issues carrying `task-ready`;
5
+ comments (and optional state labels) go out, over the REST API with a token.
6
+ The same repository is the Authority: it cuts the lines and holds the pull
7
+ requests.
5
8
  Contract: [SPECS.md](./SPECS.md).
6
9
 
7
10
  ```bash
@@ -10,27 +13,78 @@ npx mason init --manager @bluewombat/manager-github --manager-option repo=owner/
10
13
  export GITHUB_TOKEN=…
11
14
  ```
12
15
 
13
- ```json
14
- {
15
- "manager": "@bluewombat/manager-github",
16
- "managerOptions": {
17
- "repo": "owner/name",
18
- "labels": ["mason"],
19
- "readyLabel": "ready",
20
- "defaultProject": "app",
21
- "stateLabelPrefix": "mason:"
22
- }
23
- }
16
+ ```yaml
17
+ featureTracker:
18
+ package: "@bluewombat/manager-github"
19
+ options:
20
+ repo: owner/name
21
+ featureReadyLabel: feature-ready
22
+ mergeWhenReadyLabel: merge-when-ready
23
+ resumeLabel: to-resume
24
+ defaultProject: app
25
+ stateLabels: &stateLabels
26
+ invalid: "mason:invalid"
27
+ accepted: "mason:accepted"
28
+ progress: "mason:progress"
29
+ escalated: "mason:escalated"
30
+ escalation_reminder: "mason:escalation_reminder"
31
+ resumed: "mason:resumed"
32
+ submitted: "mason:submitted"
33
+ done: "mason:done"
34
+ cancelled: "mason:cancelled"
35
+ reject_late: "mason:reject_late"
36
+ ticketTracker:
37
+ package: "@bluewombat/manager-github"
38
+ options:
39
+ repo: owner/name
40
+ ticketReadyLabel: task-ready
41
+ resumeLabel: to-resume
42
+ defaultProject: app
43
+ stateLabels: *stateLabels
24
44
  ```
25
45
 
46
+ Each label option is written under the side that uses it:
47
+ `featureReadyLabel` and `mergeWhenReadyLabel` under `featureTracker`,
48
+ `ticketReadyLabel`, `noTestsLabel` and `e2eLabel` under `ticketTracker`; `skipTestE2eLabel` under `featureTracker`. The package is told which side it
49
+ serves, so `mason setup` creates only that side's labels and `mason doctor`
50
+ names only those; an option under the wrong side is refused by name. The
51
+ options both sides read (`repo`, `tokenEnv`, `resumeLabel`, `defaultProject`,
52
+ `stateLabels`, …) are written under both.
53
+
54
+ A config that gives both sides the same options (a YAML anchor, say) builds one
55
+ manager serving both: every option is then accepted, and the label options may
56
+ share one name.
57
+
58
+ The same package answers both sides here; a Feature tracker of another tool
59
+ takes the first block alone (Features), and this package the second (Tickets,
60
+ lines).
61
+
26
62
  The token is read by this package from the environment; it never appears in the
27
63
  config file and Host never sees it.
28
64
 
65
+ ## Features and Tickets
66
+
67
+ | On GitHub | Means |
68
+ | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
69
+ | A top-level issue with `feature-ready` | A Feature: its content is final, its Tickets may be written. Only these are listed. A sub-issue is never a Feature |
70
+ | Its sub-issues | Its Tickets, in the order GitHub gives them |
71
+ | `task-ready` on a sub-issue | The Ticket's specification is final. A Feature starts only when every one of its Tickets carries it |
72
+ | "Blocked by" (issue dependencies) | On a Feature: the Features that must be done first. On a Ticket: the Tickets that must be done first |
73
+ | `skip-test-e2e` on a Feature | The Feature is not put through the e2e acceptance check: its e2e Ticket is cancelled and the Gate that runs the suite is not run. Put on or taken off, it is read again until the Feature is submitted |
74
+ | `e2e` on a sub-issue | The Ticket holds the e2e acceptance suite of its Feature: it is made by the e2e pass, with no tests written first |
75
+ | `merge-when-ready` on a Feature | Its line may be folded without a human. Without it, a human merges the pull request and the system waits |
76
+ | `to-resume` on an escalated Feature or Ticket | Resume |
77
+ | `priority:<n>`, `project:<name>` labels | On a Feature and on a Ticket |
78
+
79
+ The Feature and Ticket labels may share one name (`featureReadyLabel` and
80
+ `ticketReadyLabel` both `ready-for-agent`): a sub-issue is a Ticket whatever it
81
+ carries, and `mason setup` creates the label once.
82
+
29
83
  ## What an issue says
30
84
 
31
85
  The body is the intention, word for word, less what GitHub itself put there:
32
86
  the comments an issue template leaves behind, and the boxes of a task list —
33
- `- [ ] a CSV downloads` reaches the Planner as `- a CSV downloads`. Nothing is
87
+ `- [ ] a CSV downloads` reaches the Builder as `- a CSV downloads`. Nothing is
34
88
  read out of it: no checklist is required, no criteria are extracted (what
35
89
  "done" means is the layer above mason's, not mason's). A body that was only a
36
90
  template's comments falls back to the title.
@@ -38,7 +92,7 @@ template's comments falls back to the title.
38
92
  That text, with the title, the project and the priority, is the issue's
39
93
  fingerprint — what tells an **edit** from an **echo**. Comments, state labels
40
94
  and a ticked box all move `updated_at` and deliver the issue again; none of
41
- them changes the fingerprint. Rewording the body does, and on a Feature in
95
+ them changes the fingerprint. Rewording the body does, and on a Ticket in
42
96
  flight that is recorded and freezes it once the current unit lands, for a
43
97
  human to decide. So: tick the boxes as you please; rewrite the words only when
44
98
  you mean it.
@@ -51,30 +105,40 @@ you mean it.
51
105
  | `tokenEnv` | no | `GITHUB_TOKEN` | Environment variable holding the token |
52
106
  | `token` | no | — | The token itself. For embedding and tests; prefer `tokenEnv` |
53
107
  | `apiBase` | no | `https://api.github.com` | REST base, for GitHub Enterprise |
54
- | `labels` | no | none | Every listed label must be on the issue for it to be admitted |
55
- | `readyLabel` | no | `ready` | On an escalated Feature, this label means resume; the escalation comment names it |
56
- | `defaultProject` | no | `unknown` | Project when no `project:` label says otherwise |
108
+ | `featureReadyLabel` (featureTracker) | no | `feature-ready` | The label that makes an issue a Feature the system may take |
109
+ | `ticketReadyLabel` (ticketTracker) | no | `task-ready` | The label a sub-issue carries once its specification is final |
110
+ | `noTestsLabel` (ticketTracker) | no | `no-tests` | The label that says no tests are written before this Ticket's code. It is read on the sub-issue, beside its text: the Ticket is made without the agent that writes tests first (see No tests first) |
111
+ | `e2eLabel` (ticketTracker) | no | `e2e` | The label that says this sub-issue holds the Feature's e2e acceptance suite (see The e2e labels) |
112
+ | `skipTestE2eLabel` (featureTracker) | no | `skip-test-e2e` | On a Feature: it is not put through the e2e acceptance check |
113
+ | `mergeWhenReadyLabel` (featureTracker) | no | `merge-when-ready` | On a Feature: its line is folded without a human |
114
+ | `resumeLabel` | no | `to-resume` | On an escalated Feature or Ticket, this label means resume; the escalation comment names it |
115
+ | `defaultProject` | no | none | The Project of a Feature that has no `project:` label. Without it, such a Feature is refused as invalid (`missing-project`). `mason setup` creates the label `project:<defaultProject>` |
57
116
  | `defaultPriority` | no | `50` | Priority when no `priority:` label says otherwise (0…100) |
58
- | `stateLabelPrefix` | no | none | When set, state labels (`mason:done`, …) are moved too |
117
+ | `stateLabels` | no | none | A mapping that names, for every Event, the label it lands as (see State labels). Absent: the Events leave no label |
118
+ | `labelColors` | no | none | A colour for each kind of label the setup creates, by what it is for, not by its name (see Label colours) |
59
119
  | `branch` | no | `main` | The work line pull requests are opened onto. `mason setup` checks it exists and names the repository's default branch when it does not |
60
120
  | `remote` | no | `https://github.com/<repo>.git` | Git URL of the repository, for Host's copy of the work line. Over HTTPS, git authenticates with the token in `tokenEnv` — read when git asks, never written to disk — so pushes are the token's account. Set it for GitHub Enterprise, a mirror, or SSH (then this machine's keys are used, and `doctor` says so) |
61
- | `keepBranch` | no | `false` | Leave the feature branch on the remote after the fold. By default it is deleted once the squash is on the work line |
121
+ | `keepBranch` | no | `false` | Leave the ticket branch on the remote after the fold. By default it is deleted once the squash is on the work line |
62
122
  | `commitAuthor` | no | none | `Name <email>`: who the system is on the commits it makes. Without it, commits carry this machine's git identity, and `doctor` warns. Use the token's account (its noreply address works) so GitHub credits the system, not a person |
63
123
 
64
124
  ## Submissions
65
125
 
66
- This manager is an Authority: it offers an assembled feature to the repository
67
- as a pull request, and takes an accepted one in.
126
+ This manager is an Authority: it cuts the line of a Feature, offers an
127
+ assembled Ticket (or a Feature's line) to the repository as a pull request, and
128
+ takes an accepted one in.
68
129
 
69
130
  | Method | On GitHub |
70
131
  | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
71
- | `submit` | Opens a pull request from the published branch onto `workLine.branch`. An open one on the same branch is reused, never duplicated |
72
- | `verdict` | Reads the checks on the pull request's head commit |
132
+ | `openLine`| Creates `feature/<n>` from `branch` (`git/refs`). A line that exists is a success |
133
+ | `dropLine`| Deletes a line nobody will fold |
134
+ | `submit` | Opens a pull request from the published branch onto the line it is offered to (a Feature's line, or `branch`). An open one on the same branch is reused, never duplicated |
135
+ | `settled` | Whether the pull request is still open, was merged (by anyone), or was closed unmerged. A human's merge is read here |
136
+ | `behind` | Whether the line holds commits the pull request does not (`compare` of the line's tip against the pull request's head, `behind_by`). Asked right before a `fold`. A failed read is `unavailable`, never `current` |
73
137
  | `fold` | Squash-merges it: the commit's title is the pull request's; its body is what the Project's Describer said (carried on the pull request), else `Closes #N` alone. Then deletes the branch unless `keepBranch` |
74
138
 
75
139
  The pull request is titled after the issue (trailing full stop dropped, `(#N)`
76
- appended) and its body is the issue's intention, the Plan's steps, and
77
- `Closes #N` — so GitHub closes the issue when the pull request is merged.
140
+ appended) and its body is the issue's intention and
141
+ `Closes #N` — so GitHub closes the issue when the pull request is merged. GitHub honours `Closes` only for a merge into the default branch, so a Ticket folded into a Feature's line stays open until a reservation says otherwise (`docs/RESERVATIONS.md`).
78
142
  Nothing in it is generated.
79
143
 
80
144
  `verdict` counts the grace for a commit with no check from the **commit**, not
@@ -82,7 +146,7 @@ from the pull request: one that was sent back and published again is old while
82
146
  its commit is seconds old, and reading "no check yet" as "no checks exist" folds
83
147
  it unjudged.
84
148
 
85
- `clearReady` takes the `readyLabel` off an issue once a resume has been acted
149
+ `clearResume` takes the `resumeLabel` off an issue once a resume has been acted
86
150
  on, so an escalation freezes instead of resuming on every pass.
87
151
 
88
152
  `probe` asks whether an issue Host already holds is still there. A poll cannot
@@ -119,7 +183,7 @@ back whole, as the full read would have been. A 304 sent with an
119
183
  Authorization header does not count against the token's hourly budget
120
184
  (GitHub's [REST best practices](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api);
121
185
  measured: three 304s in a row left `x-ratelimit-used` unchanged). So a tick
122
- where nothing moved on the watched issues — the listing, a probe of a Feature
186
+ where nothing moved on the watched issues — the listing, a probe of a Ticket
123
187
  in flight — costs a round trip and no budget. Handing the answer back rather
124
188
  than reading a 304 as "nothing" matters: when Host did not commit its Cursor,
125
189
  the same listing is asked again and the issues not consumed yet must come back.
@@ -130,11 +194,100 @@ per judgement, with nothing to remember an `etag` in. Anything this manager
130
194
  writes on an issue (a comment, a label) changes the listing, so the next read
131
195
  after a report is a full one.
132
196
 
197
+ ## State labels
198
+
199
+ The Events are `invalid`, `accepted`, `progress`, `escalated`, `escalation_reminder`,
200
+ `resumed`, `submitted`, `done`, `cancelled` and `reject_late`. With `stateLabels` set,
201
+ the issue carries the label of its latest Event and the previous one is removed;
202
+ every other label (`project:`, `priority:`, yours) is never touched. `done` and
203
+ `cancelled` also keep a fresh Mason home from rebuilding or taking up an issue that
204
+ already ended, unless the resume label is on it.
205
+
206
+ `stateLabels` names every Event, with any label you like, and gives the same name to
207
+ no two Events:
208
+
209
+ ```yaml
210
+ options:
211
+ stateLabels:
212
+ invalid: "mason:invalid"
213
+ accepted: "mason:accepted"
214
+ progress: "mason:progress"
215
+ escalated: "mason:escalated"
216
+ escalation_reminder: "mason:escalation_reminder"
217
+ resumed: "mason:resumed"
218
+ submitted: "mason:submitted"
219
+ done: "termine"
220
+ cancelled: "abandonne"
221
+ reject_late: "mason:reject_late"
222
+ ```
223
+
224
+ Absent, nothing is labelled and the guard on `done` and `cancelled` is off. Naming
225
+ an Event outside the list, leaving one out, or giving a label twice is refused.
226
+
227
+ ## No tests first
228
+
229
+ When the host writes a Ticket's tests before its code, a sub-issue carrying the `no-tests`
230
+ label (the `noTestsLabel` option renames it) is made without that step: no agent writes
231
+ tests for it, and its producer writes its own with its code. The decision is the label,
232
+ never a line of the issue's text, so it can be set or taken off without editing the
233
+ specification. Put it on a Ticket before it starts: a Ticket already under way keeps what
234
+ it began with. `mason setup` creates the label, and `labelColors` colours it with the
235
+ `no-tests` key.
236
+
237
+ ## The e2e labels
238
+
239
+ A Project may prove each Feature by an acceptance suite run once on the whole Feature (see the
240
+ runtime README, "The e2e acceptance check"). On the tracker it is two labels. `e2e`
241
+ (`e2eLabel`, under `ticketTracker`) marks the last sub-issue of the Feature, whose body is the
242
+ scenarios agreed with the operator, `E1`, `E2`…, copied verbatim; it must be blocked by every
243
+ other sub-issue. `skip-test-e2e` (`skipTestE2eLabel`, under `featureTracker`) on the Feature
244
+ leaves the check out: the `e2e` sub-issue is cancelled, and the Gate that runs the suite is not
245
+ run. Both are compared without case, and read again on each delivery until the Feature is
246
+ submitted. `mason setup` creates them, and `labelColors` colours them with the keys `e2e` and
247
+ `skip-test-e2e`.
248
+
249
+ ## Label colours
250
+
251
+ `mason setup` gives each label a colour so a human can tell them apart. `labelColors`
252
+ chooses them, one label at a time. A key is the identifier of a label, never its name, so
253
+ renaming a label (`stateLabels`, `resumeLabel`, ...) does not lose its colour. A colour is
254
+ six hex digits, with or without a `#`:
255
+
256
+ ```yaml
257
+ options:
258
+ labelColors:
259
+ feature-ready: "1d76db"
260
+ ticket-ready: "1d76db"
261
+ merge-when-ready: "1d76db"
262
+ no-tests: "fbca04"
263
+ e2e: "0e8a16"
264
+ skip-test-e2e: "fbca04"
265
+ resume: "0e8a16"
266
+ project: "c5def5" # project:<defaultProject>
267
+ state-invalid: "5319e7"
268
+ state-accepted: "5319e7"
269
+ state-progress: "5319e7"
270
+ state-escalated: "d93f0b"
271
+ state-escalation_reminder: "d93f0b"
272
+ state-resumed: "5319e7"
273
+ state-submitted: "5319e7"
274
+ state-done: "0e8a16"
275
+ state-cancelled: "6a737d"
276
+ state-reject_late: "6a737d"
277
+ ```
278
+
279
+ Every key is optional. When the Feature and the Ticket ready labels are one label (the
280
+ same name), `feature-ready` colours it. A colour the config chose is applied to a label
281
+ that already exists too: `mason setup` says `recolour label …` and `--apply` does it. A
282
+ label the config chose no colour for keeps the default colour when it is created and is
283
+ never touched when it exists, so a colour set by hand on GitHub stays. A key that is not in
284
+ the list above, or a colour that is not six hex digits, is refused by name.
285
+
133
286
  ## Prepare the repository
134
287
 
135
288
  `mason setup` reads the config and says what the repository still needs;
136
289
  `--apply` creates it. It is idempotent, so it is safe to re-run after changing
137
- `labels` or turning `stateLabelPrefix` on.
290
+ the label options or setting `stateLabels`.
138
291
 
139
292
  ```bash
140
293
  mason setup --apply
@@ -146,10 +299,10 @@ mason setup --apply
146
299
  | Issues are enabled | `has_issues` on that same response |
147
300
  | The token can write | `permissions.push` on that same response, rather than guessing at scopes |
148
301
  | The branch exists | `GET /repos/{owner}/{repo}/branches/{branch}` for `branch` (default `main`); when it is missing and the config named none, the step says the repository's default branch and what to write |
149
- | Labels | `labels`, `readyLabel`, `project:<defaultProject>`, and one per Event when `stateLabelPrefix` is set |
302
+ | Labels | `featureReadyLabel`, `ticketReadyLabel`, `noTestsLabel`, `e2eLabel`, `mergeWhenReadyLabel`, `skipTestE2eLabel`, `resumeLabel`, `project:<defaultProject>`, and one per Event when `stateLabels` is set |
150
303
 
151
- Creating a label is the only write; nothing existing is renamed, recoloured, or
152
- deleted.
304
+ Creating a label, and recolouring one the config chose a colour for (see Label
305
+ colours), are the only writes; nothing existing is renamed or deleted.
153
306
 
154
307
  ## Stack
155
308
 
@@ -181,10 +334,12 @@ SPECS.md the contract
181
334
  README.md how to build and configure it
182
335
  src/
183
336
  index.ts createManager, checkManager, scaffoldManager — the plugin face
184
- manager.ts the ManagerPort (listen / adapt / report / probe live in listener/, adapter/, emitter/, probe/)
337
+ manager.ts the Manager: `features` and `tickets` (listen / adapt / report / probe live in listener/, adapter/, emitter/, probe/)
185
338
  github/ Octokit channel shared by listen, adapt, report, and probe
186
339
  probe/ whether a minted key's issue is still on the tracker
187
340
  repo.ts owner/name
341
+ tickets/ a Feature's sub-issues, their blockers and signals
342
+ submission/ lines, pull requests, settled
188
343
  issue-number.ts the issue a key or a payload points at
189
344
  labels.ts whether a payload carries a label
190
345
  ```
package/SPECS.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Manager: GitHub Issues
2
2
 
3
- A plugin, not a Transformer. GitHub Issues in, issue comments (and optional
4
- state labels) out, as one `ManagerPort`. Listen / adapt / report live in this
5
- package. GitHub-the-git-host is out of scope — only the tracker.
3
+ A plugin, not a Transformer. GitHub Issues in (Features and their sub-issues),
4
+ issue comments (and optional state labels) out, and the lines and pull requests
5
+ of the same repository, as one `Manager` (`features` and `tickets`). Listen /
6
+ adapt / report live in this package.
6
7
 
7
8
  ## 1. Job
8
9
 
@@ -10,15 +11,18 @@ package. GitHub-the-git-host is out of scope — only the tracker.
10
11
  Host manager-github
11
12
  ---- --------------
12
13
  managerOptions read repo / labels / token
13
- a Cursor poll the issue list
14
+ a Cursor poll the issues labelled feature-ready
15
+ a Feature list its sub-issues and blockers
14
16
  a key probe: is the issue still there
15
17
  one semantic Event comment, and maybe label
18
+ a Feature's line openLine: cut feature/<n>; dropLine
16
19
  a published branch submit: open (or reuse) the pull request
17
- a Submission reference fold: squash-merge it, delete the branch unless keepBranch
18
- ← ManagerPort
20
+ a Submission reference fold: squash-merge it, delete the branch unless keepBranch;
21
+ settled: open / folded / closed
22
+ ← Manager
19
23
  ```
20
24
 
21
- **In:** a `ManagerContext`. **Out:** a `ManagerPort`, or a reason.
25
+ **In:** a `ManagerContext`. **Out:** a `Manager`, or a reason.
22
26
 
23
27
  ## 2. The token
24
28
 
@@ -29,7 +33,7 @@ or written to a Thread.
29
33
 
30
34
  ## 3. Keys
31
35
 
32
- `github:<owner>/<name>#<issue>`, or `github:unconvertible` when the payload
36
+ Features and Tickets share one spelling: `github:<owner>/<name>#<issue>`, or `github:unconvertible` when the payload
33
37
  carries no issue number. `report` recovers the issue from the key: every key
34
38
  this package mints ends in `#<issue>`, so no issue number crosses the Port.
35
39
 
@@ -42,18 +46,28 @@ normalised, trailing blanks and runs of blank lines collapsed. Nothing is read:
42
46
  no criteria, no headings, no meaning. A body that was only that falls back to
43
47
  the title, like no body at all.
44
48
 
45
- The FeatureStandard's `fingerprint` hashes that intention with the title, the
49
+ The FeatureStandard's and TicketStandard's `fingerprint` hashes that intention with the title, the
46
50
  project and the priority. It is what tells an **edit** from an **echo** — a
47
51
  comment or a state label moves `updated_at` and delivers the issue again, and
48
52
  so does a human ticking a box. None of those is a change of mind, so none of
49
- them changes the fingerprint; a different fingerprint on a Feature in flight
53
+ them changes the fingerprint; a different fingerprint on a Ticket in flight
50
54
  is recorded as pending and freezes it once the current unit lands
51
55
  (PRODUCT.md, "An intention edited while in flight"). Rewording the body is an
52
56
  edit. Ticking a box is not.
53
57
 
54
- ## 4. Ready
58
+ ## 3c. Features and Tickets
55
59
 
56
- `signalsReady` is true when the payload carries `readyLabel`, ignoring case, in
60
+ A Feature is listed when the issue has no parent (`parent_issue_url` is null) and carries `featureReadyLabel`
61
+ (`labels=<featureReadyLabel>` on the list call). Its `blocked_by` are read from
62
+ `GET /issues/{n}/dependencies/blocked_by`, its `merge_when_ready` from
63
+ `mergeWhenReadyLabel` and its `skip_e2e` from `skipTestE2eLabel`. `tickets(feature)` reads `GET /issues/{n}/sub_issues`
64
+ and, per sub-issue, its blockers; `task_ready` is `ticketReadyLabel`, `resume` is
65
+ `resumeLabel`, `open` is the issue state. Every failed read is `unavailable`,
66
+ never an empty list.
67
+
68
+ ## 4. Resume
69
+
70
+ `signalsResume` is true when the payload carries `resumeLabel`, ignoring case, in
57
71
  either label spelling GitHub uses (a string, or an object with `name`). A poll
58
72
  returns a snapshot with no `labeled` edge, so the label on the snapshot is the
59
73
  only signal there is.
@@ -65,10 +79,12 @@ run. It reads the repository once — that single response answers reachable,
65
79
  issues enabled, and whether the token may write — then lists the labels the
66
80
  configuration names against the labels that exist.
67
81
 
68
- Without `apply` it reports a plan and sends no `POST`. With `apply` it creates
69
- the missing labels and nothing else: an existing label is never renamed,
70
- recoloured, or deleted, and a repeated name is created once. A second `apply`
71
- reports everything `satisfied`.
82
+ Without `apply` it reports a plan and sends no write. With `apply` it creates the
83
+ missing labels, and recolours an existing label whose colour differs from the one the
84
+ config chose for it (`labelColors`, keyed by the label's identifier: `feature-ready`,
85
+ `ticket-ready`, `merge-when-ready`, `no-tests`, `e2e`, `skip-test-e2e`, `resume`, `project`, `state-<event>`): an existing label is never renamed or deleted, a label with no
86
+ chosen colour keeps whatever colour it has, and a repeated name is created once. A
87
+ second `apply` reports everything `satisfied`.
72
88
 
73
89
  A 404 on the repository is reported as "not found, or this token cannot see
74
90
  it", because from here the two are the same answer.
@@ -76,13 +92,12 @@ it", because from here the two are the same answer.
76
92
  ## 6. Doctor
77
93
 
78
94
  `checkManager` reports the repository, whether the token variable is set
79
- (`fail` when it is not), whether labels narrow the Source (`warn` when they do
80
- not — every open issue would be admitted), and reminds which labels the
81
- repository needs. It never calls the API: the answer must be the same offline.
95
+ (`fail` when it is not), and names the labels the repository needs (`featureReadyLabel`,
96
+ `ticketReadyLabel`, `mergeWhenReadyLabel`, `resumeLabel`). It never calls the API: the answer must be the same offline.
82
97
 
83
98
  ## 6. Init
84
99
 
85
- `scaffoldManager` fills `repo`, `tokenEnv`, `labels`, `readyLabel` and
100
+ `scaffoldManager` fills `repo`, `tokenEnv`, `resumeLabel` and
86
101
  `defaultProject`. It does not create labels on the repository: that needs the
87
102
  network, and the operator still owns the tracker. A `repo` already in
88
103
  `managerOptions` is kept.
@@ -99,19 +114,33 @@ with no issue number is `unavailable`, not `gone`.
99
114
  not answer says nothing about the intention being converted. Probe is the
100
115
  path that may read a 404 as absence.
101
116
 
117
+ ## 7b. Lines
118
+
119
+ `openLine({line, from})` reads `git/ref/heads/<from>` and creates `refs/heads/<line>`
120
+ at that commit; a 422 (the ref exists) is a success. `dropLine` deletes the ref
121
+ and ignores a refusal. `settled({reference})` reads the pull request: merged is
122
+ `folded`, closed unmerged is `closed`, otherwise `open`.
123
+
124
+ ## 7c. Behind
125
+
126
+ `behind({reference})` reads the pull request, the tip of the branch it is offered
127
+ to (`git/ref/heads/<base>`), and compares the two by commit, `behind_by` of
128
+ `<tip>...<head>`. `0` is `current`; more is `behind` with that count; any failed
129
+ read, or an answer with no count, is `unavailable`. It writes nothing.
130
+
102
131
  ## 8. Acceptance
103
132
 
104
133
  1. A token in the variable named by `tokenEnv` is enough to build the Port.
105
134
  2. No token → a reason naming the variable it looked in.
106
135
  3. A missing `repo`, a `repo` that is not `owner/name`, and an unknown option
107
136
  key each return a reason.
108
- 4. `checkManager` fails on a missing token and warns when no label is set.
137
+ 4. `checkManager` fails on a missing token and names the four labels.
109
138
  5. `setup` without `apply` writes nothing; with `apply` it creates only the
110
139
  missing labels, and a second `apply` writes nothing at all.
111
140
  6. Disabled issues, a read-only token, and an unreachable repository each block
112
141
  before any label is created.
113
142
  7. `scaffoldManager` keeps a `repo` it was given and names `GITHUB_TOKEN`.
114
- 8. `invalid`, `accepted`, `planned`, `escalated`, `escalation_reminder` and
143
+ 8. `invalid`, `accepted`, `escalated`, `escalation_reminder` and
115
144
  `resumed` comments are written as string literals, not a field dump. An
116
145
  `invalid` comment is the heading and the reason as a paragraph, with no
117
146
  `- reason:` bullet.
@@ -124,7 +153,7 @@ path that may read a 404 as absence.
124
153
  11. `setup` blocks on a `branch` the repository does not have, and names the
125
154
  repository's default branch when the config named none.
126
155
  12. An `escalated` comment says what a human can do from the issue, in this
127
- tracker's gestures: add `readyLabel` to resume, close the issue to
156
+ tracker's gestures: add `resumeLabel` to resume, close the issue to
128
157
  abandon — and, for an escalation born in `merging`, only the first, since
129
158
  a close there is refused. An `escalation_reminder` is the same comment
130
159
  under "Still escalated": same diagnosis, same answers.
@@ -132,4 +161,11 @@ path that may read a 404 as absence.
132
161
  heading, one line saying it is the stopping step's own words written for
133
162
  the agent that repairs it, then the text fenced and folded at 80 columns.
134
163
 
135
- How to build and configure: [README.md](./README.md).
164
+ 14. A Feature is listed only with `featureReadyLabel`; its Tickets are its sub-issues;
165
+ a blocker read that fails is `unavailable`, never an empty list.
166
+ 15. `openLine` of an existing line succeeds; `settled` distinguishes a merge
167
+ from a close.
168
+ 16. `behind` is `current` at `behind_by` 0, `behind` with the count above it, and
169
+ `unavailable` when GitHub does not answer or the reference is not a pull request.
170
+
171
+ How to build and configure: [README.md](./README.md).
@@ -1,11 +1,11 @@
1
1
  import type { Intent, Invalid } from "../types.js";
2
2
  /** What a repository can say about an issue, in this Block's five words. */
3
- export type Kind = "create" | "update" | "ready" | "cancel" | "delete";
3
+ export type Kind = "create" | "update" | "resume" | "cancel" | "delete";
4
4
  export type IntentInput = {
5
5
  action: string | undefined;
6
6
  state: string | undefined;
7
7
  labels: string[];
8
- readyLabel: string;
8
+ resumeLabel: string;
9
9
  };
10
10
  export type IntentResult = {
11
11
  ok: true;
@@ -19,7 +19,7 @@ export type IntentResult = {
19
19
  *
20
20
  * With an action, the repository said what changed. Without one — a snapshot a
21
21
  * subscription listed — only the state is known: open means the intention
22
- * itself, closed means drop it. `ready` needs the edge, not the state: it is the
22
+ * itself, closed means drop it. `resume` needs the edge, not the state: it is the
23
23
  * moment the label arrived, and a snapshot cannot tell that from a label that
24
24
  * has been there for a week.
25
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../../src/adapter/normalize/intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGnD,4EAA4E;AAC5E,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AA+BvE,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1F;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAmB3D"}
1
+ {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../../src/adapter/normalize/intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGnD,4EAA4E;AAC5E,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AA+BxE,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1F;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAmB3D"}
@@ -22,7 +22,7 @@ const ACTION_TO_KIND = {
22
22
  const KIND_TO_INTENT = {
23
23
  create: "upsert",
24
24
  update: "upsert",
25
- ready: "ready",
25
+ resume: "resume",
26
26
  cancel: "cancel",
27
27
  delete: "cancel",
28
28
  };
@@ -31,7 +31,7 @@ const KIND_TO_INTENT = {
31
31
  *
32
32
  * With an action, the repository said what changed. Without one — a snapshot a
33
33
  * subscription listed — only the state is known: open means the intention
34
- * itself, closed means drop it. `ready` needs the edge, not the state: it is the
34
+ * itself, closed means drop it. `resume` needs the edge, not the state: it is the
35
35
  * moment the label arrived, and a snapshot cannot tell that from a label that
36
36
  * has been there for a week.
37
37
  */
@@ -49,8 +49,8 @@ export function readIntent(input) {
49
49
  },
50
50
  };
51
51
  }
52
- if (input.action === "labeled" && hasLabel(input.labels, input.readyLabel)) {
53
- return { ok: true, intent: KIND_TO_INTENT.ready };
52
+ if (input.action === "labeled" && hasLabel(input.labels, input.resumeLabel)) {
53
+ return { ok: true, intent: KIND_TO_INTENT.resume };
54
54
  }
55
55
  return { ok: true, intent: KIND_TO_INTENT[kind] };
56
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"intent.js","sourceRoot":"","sources":["../../../src/adapter/normalize/intent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKvC,MAAM,cAAc,GAAyB;IAC3C,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,QAAQ;IACpB,UAAU,EAAE,QAAQ;IACpB,YAAY,EAAE,QAAQ;IACtB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF,MAAM,cAAc,GAAyB;IAC3C,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAWF;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,gBAAgB;gBACtB,MAAM,EAAE,WAAW,KAAK,CAAC,MAAM,gCAAgC;aAChE;SACF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../../../src/adapter/normalize/intent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKvC,MAAM,cAAc,GAAyB;IAC3C,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,QAAQ;IACnB,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,QAAQ;IACpB,UAAU,EAAE,QAAQ;IACpB,YAAY,EAAE,QAAQ;IACtB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF,MAAM,cAAc,GAAyB;IAC3C,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAWF;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,gBAAgB;gBACtB,MAAM,EAAE,WAAW,KAAK,CAAC,MAAM,gCAAgC;aAChE;SACF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;AACpD,CAAC"}
@@ -7,7 +7,11 @@ export type NormalizeOptions = {
7
7
  defaultPriority: number;
8
8
  projectLabelPrefix: string;
9
9
  priorityLabelPrefix: string;
10
- readyLabel: string;
10
+ resumeLabel: string;
11
+ mergeWhenReadyLabel: string;
12
+ skipTestE2eLabel: string;
13
+ /** Keys of the issues that block this one, read beside it: the issue itself does not carry them. */
14
+ blockedBy?: string[];
11
15
  normalizedAt: string;
12
16
  };
13
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapter/normalize/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAA4B,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAgGF;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAmIpF"}
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapter/normalize/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAA4B,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oGAAoG;IACpG,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAgGF;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,GAAG,eAAe,CA0IpF"}
@@ -138,7 +138,7 @@ export function normalize(raw, options) {
138
138
  action: raw.action,
139
139
  state: state.value,
140
140
  labels: labels.names,
141
- readyLabel: options.readyLabel,
141
+ resumeLabel: options.resumeLabel,
142
142
  });
143
143
  if (!intent.ok) {
144
144
  return { ok: false, invalid: intent.invalid };
@@ -206,6 +206,10 @@ export function normalize(raw, options) {
206
206
  ...(title.value !== undefined ? { title: title.value } : {}),
207
207
  ...(intention !== undefined ? { intention } : {}),
208
208
  priority: priority.value ?? options.defaultPriority,
209
+ blocked_by: options.blockedBy ?? [],
210
+ merge_when_ready: labels.names.some((name) => name.toLowerCase() === options.mergeWhenReadyLabel.toLowerCase()),
211
+ skip_e2e: options.skipTestE2eLabel.length > 0 &&
212
+ labels.names.some((name) => name.toLowerCase() === options.skipTestE2eLabel.toLowerCase()),
209
213
  ...(Object.keys(source).length > 0 ? { source } : {}),
210
214
  fingerprint: "",
211
215
  normalized_at: options.normalizedAt,