@edgehero/pi-dispatch-admin 1.3.0 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgehero/pi-dispatch-admin",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Operator console (a pi extension) + skill for pi-dispatch: run the pi coding agent as a self-hosted service. A /dispatch TUI for the queue, spend caps, run history, editable GitHub, GitLab, Forgejo and Azure DevOps triggers (cron/label/comment/PR/MR/work item), and scheduled pause windows — plus AI-operable, human-confirmed controls. Runs against a live pi-dispatch deployment.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -159,11 +159,14 @@ about the trigger is the same — the `on.type`, the `{any, all, none}` label pr
159
159
  Three things are NOT the same, and all of them refuse at load rather than misbehaving quietly:
160
160
 
161
161
  - **`pull_request` actions are the forge's own words.** GitHub takes
162
- `labeled | opened | synchronize | reopened | review_submitted`; GitLab takes
163
- `open | update | reopen | approved`; Forgejo takes
164
- `label_updated | opened | synchronized | reopened`; Azure takes `created | updated`. A word
162
+ `labeled | opened | synchronize | reopened | review_submitted | closed`; GitLab takes
163
+ `open | update | reopen | approved | close`; Forgejo takes
164
+ `label_updated | opened | synchronized | reopened | closed`; Azure takes `created | updated` and has
165
+ no close word (a close trigger on azure is refused at load — not yet covered, not declined). A word
165
166
  from the wrong forge is refused when the file is written. It would not break anything otherwise — it
166
167
  would simply never match an event, and the trigger would look configured while doing nothing.
168
+ The close word rides **alone**: a rule mixing it with other actions is refused, because a close is
169
+ gated on the actor who closed the item where every other action gates on the author or a label.
167
170
  `review_submitted` is GitHub's `pull_request_review` event: it fires on **every** submitted review, so
168
171
  add `reviewState` (`approved | changes_requested | commented`) to narrow which verdicts are worth paying
169
172
  for. That field is github-only and legal only beside `review_submitted`; anywhere else it refuses at
@@ -176,6 +179,32 @@ Three things are NOT the same, and all of them refuse at load rather than misbeh
176
179
  requests, so `any`/`all`/`none` could never match and a rule that loads clean and never fires reads as a
177
180
  broken harness.
178
181
 
182
+ ## Close triggers and one-shots — `on.once`, and the re-arm
183
+
184
+ Two shapes fire on a close: the `issue` trigger type (an issue closing, `on.action` in the forge's close
185
+ word), and a `pull_request` rule whose only action is the close word (on GitHub and Forgejo a merged PR
186
+ counts as closed; on GitLab only an explicit close fires it). Both
187
+ may carry `on.number` to pin one specific item, and `once: true` (which requires `number`) makes the rule
188
+ a **one-shot**: after one run the worker marks the entry spent by adding
189
+ `on.disarmed: { at, jobId }` to it in `triggers.json`. The entry is never deleted — run history
190
+ attributes by array position — so `dispatch_triggers` and the panel keep showing it, marked spent,
191
+ matching nothing.
192
+
193
+ Four things to say when an operator asks:
194
+
195
+ - **A failed run still spends it.** "Fired" means "produced a run record", so a one-shot whose job failed
196
+ is spent too. The fix is the re-arm below, after they have read why it failed.
197
+ - **Re-arming is deleting `on.disarmed` from the entry** in `triggers.json`, nothing else. It is an
198
+ operator file edit: no tool and no panel key writes or removes that mark, so say so plainly rather than
199
+ reaching for `dispatch_trigger_edit` (which changes the flow only).
200
+ - **Authoring one**: `dispatch_trigger_add` takes `kind: issue` (plus `number` and `once`), behind the
201
+ same confirm dialog as every trigger write, and a close-only `pull_request` rule accepts the same two
202
+ fields. The shared validator refuses anything malformed, and `once` requires `number`.
203
+ - **Both services must read the same file.** The disarm is a worker write, so a receiver pointed at a
204
+ different `triggers.json` stays armed; `PI_TRIGGERS_FILE` set to one absolute path is the fix, and
205
+ `pi-dispatch doctor` warns about it. In the compose topology the receiver's read-only mount lags a
206
+ disarm until restart, and the worker's own pre-spend check is what prevents a second run meanwhile.
207
+
179
208
  ## Racing two agents on one event — `run.replicas`
180
209
 
181
210
  `"replicas": 2` on a `label`, `comment` or `pull_request` trigger turns one delivery into **two independent
@@ -190,9 +219,10 @@ Three things to say when an operator asks about it:
190
219
  - **You cannot set it from here.** There is no `replicas` parameter on `dispatch_trigger_add` or
191
220
  `_edit`, and no panel key. It is a reviewed edit to `triggers.json`, deliberately: a spend multiplier is
192
221
  plainly a capability a model should not gain. Say so rather than looking for a way around it.
193
- - **It refuses on cron, and beside `resume`.** A local job's `/workspace` IS the operator's folder, so two
194
- replicas would edit one working tree with no gate and no undo. And a resumed run continues one lineage
195
- where replicas exist to fork it.
222
+ - **It refuses on cron, beside `resume`, and beside `once: true`.** A local job's `/workspace` IS the
223
+ operator's folder, so two replicas would edit one working tree with no gate and no undo. A resumed run
224
+ continues one lineage where replicas exist to fork it. And a one-shot promises exactly one run, which N
225
+ racing sandboxes contradict.
196
226
 
197
227
  `dispatch_trigger_add` takes an optional `forge` parameter, defaulting to `github`. Unlike `image`, this
198
228
  one IS offered to the model — a model that can already add a GitHub trigger can already arm a paid run,