@a9n-shoji/rvw 0.2.0 → 0.2.2
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/CHANGELOG.md +32 -0
- package/README.md +28 -7
- package/dist/cli.mjs +492 -307
- package/dist/cli.mjs.map +3 -3
- package/dist/web/assets/{DocumentViewer-CNgHpCuL.js → DocumentViewer-ADzspLC4.js} +32 -32
- package/dist/web/assets/{markdown-source-map-_dS1q5Dg.js → MarkdownTable-DVJ0_MIC.js} +2 -2
- package/dist/web/assets/WalkthroughViewer-yQibj2tK.js +1 -0
- package/dist/web/assets/{abnfDiagram-N423BO3Z-0v-ndtqQ.js → abnfDiagram-N423BO3Z-6dQzEuPV.js} +1 -1
- package/dist/web/assets/architecture-TIHT7OUA-DEoBuEnc.js +1 -0
- package/dist/web/assets/{architectureDiagram-T3A2C74G-WSNJ-tZE.js → architectureDiagram-T3A2C74G-DP7hHV7T.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-lmMbEUwI.js → blockDiagram-VBNYF7ZC-ZbwzVKWP.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-BrN5YVV8.js → c4Diagram-5PPSVZJV-HVD9_uV4.js} +1 -1
- package/dist/web/assets/channel-DjgdV-zW.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-gt2JPyrH.js → chunk-2GRJ4B5K-Bkxs2d4J.js} +1 -1
- package/dist/web/assets/{chunk-4I5QYGJK-Cg5GZ_-7.js → chunk-4I5QYGJK-BJHE6zC_.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-BdhRGvxW.js → chunk-5RXB4S5H-B-52dfCQ.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-B1DYWNVJ.js → chunk-6Q2QTUOP-BpD0vQ66.js} +1 -1
- package/dist/web/assets/{chunk-7Z6QIM7H-D2YImamZ.js → chunk-7Z6QIM7H-BTI1qgAO.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-CWtwL_CM.js → chunk-GF5L2VYU-B8BxoCCx.js} +1 -1
- package/dist/web/assets/{chunk-I66GZJ75-DdoGlxMt.js → chunk-I66GZJ75-zwi-Xf01.js} +1 -1
- package/dist/web/assets/{chunk-JQJVKLGR-CVWAp3XP.js → chunk-JQJVKLGR-D9mKBWUz.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-BBacwg3J.js → chunk-KBJHAD2P-Qmtir3qc.js} +1 -1
- package/dist/web/assets/{chunk-NSK5VX7P-1AsXiBV3.js → chunk-NSK5VX7P-SP2vZdAo.js} +1 -1
- package/dist/web/assets/{chunk-QR6OTTB3-DL2s5pNp.js → chunk-QR6OTTB3-uVDCHurs.js} +1 -1
- package/dist/web/assets/{chunk-UBXNYLIW-mu3fkNQI.js → chunk-UBXNYLIW-CtNzBWOU.js} +1 -1
- package/dist/web/assets/{chunk-W5SLKNZC-BAwhMBBb.js → chunk-W5SLKNZC-BPfDqbmK.js} +1 -1
- package/dist/web/assets/{chunk-WRU74C26-C7IeXZaP.js → chunk-WRU74C26-BZkEOexq.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-B6Qjam7Y.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-B6Qjam7Y.js +1 -0
- package/dist/web/assets/{cynefin-VYW2F7L2-DNisGvNv.js → cynefin-VYW2F7L2-BRisCyqC.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-BA27gOTz.js → cynefinDiagram-MW4NZA55-ClIrmCkB.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-vo_1YAuM.js → dagre-VZM6K2ZE-X31uC_JB.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-DLLdUj0_.js → diagram-7IWD3JNH-DBQFjl0i.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-BV0WyNA-.js → diagram-B4RE2ZJO-BZ7934Wg.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-DXxKSx2U.js → diagram-LBJQPF4R-CcJqH9aY.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-DsQ2GzIK.js → diagram-Q27KOJAE-DzM7eiil.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-DfjElFP1.js → diagram-UB23O5K3-CVCV-d3m.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-cYu0XV1a.js → ebnfDiagram-BXEA7PRR-D8xb9FBp.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-DnsLBHaV.js → erDiagram-JOGREHBK-1l-G_PMG.js} +1 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-ndNeUBOZ.js +1 -0
- package/dist/web/assets/flowDiagram-UKHOOZJN-BVxK_6Tu.js +1 -0
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-CVD5gq3E.js → ganttDiagram-PKOTCBZU-CDJ9a1tp.js} +1 -1
- package/dist/web/assets/{gitGraph-TEB2WS4Q-C_j0n-Kw.js → gitGraph-TEB2WS4Q-DOeTh5wB.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-qACevJmW.js → gitGraphDiagram-DS77QQ5N-DYLxc8Nz.js} +1 -1
- package/dist/web/assets/index-9cOaPesh.js +384 -0
- package/dist/web/assets/index-kK3hngnz.css +1 -0
- package/dist/web/assets/{info-DKCQHKI2-Cmkbz3Yi.js → info-DKCQHKI2-B5u97QuQ.js} +1 -1
- package/dist/web/assets/{infoDiagram-6WML65LV-CDrAem7M.js → infoDiagram-6WML65LV-zosKW2OD.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-DXgCnFqW.js → ishikawaDiagram-WSZJBQD7-VYcc_au8.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-1KW-I7DV.js → journeyDiagram-NVQOT4AX-C_nU8kP4.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-CCt75OuF.js → kanban-definition-27J2QSJJ-DybKaWfx.js} +1 -1
- package/dist/web/assets/{line-DQfkDi_r.js → line-DWsGplGj.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-B7TMMtY0.js → mermaid-parser.core-D_T_3oIQ.js} +3 -3
- package/dist/web/assets/{mermaid.core-B93x-7Mk.js → mermaid.core-BoLGzxB5.js} +4 -4
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-Dyt28V6S.js → mindmap-definition-FAOFIHXS-CsdpqPIa.js} +1 -1
- package/dist/web/assets/{packet-7NZHBO7P-zQ0u35s3.js → packet-7NZHBO7P-CReAEF1h.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-et0KHGIE.js → pegDiagram-VL7TDLO6-CE9-NPMu.js} +1 -1
- package/dist/web/assets/{pie-RZYD4A2V-uur86LkQ.js → pie-RZYD4A2V-GYixXUka.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-c027WPTB.js → pieDiagram-7S7Q4E2Y-CqaAKNg2.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-BtSV4E-0.js → quadrantDiagram-CIZ2JOQS-eRMHJqfj.js} +1 -1
- package/dist/web/assets/{radar-I7S5WNFK-B_g0VZMF.js → radar-I7S5WNFK-BwHxP0iK.js} +1 -1
- package/dist/web/assets/{railroad-3IZDKUUU-VFLsM_Em.js → railroad-3IZDKUUU-AHc3O4Oz.js} +1 -1
- package/dist/web/assets/railroad-abnf-AHOZXSZD-ZKY5olmV.js +1 -0
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-CrG1UCMo.js +1 -0
- package/dist/web/assets/railroad-peg-LSFZ7HO6-CoY6MIbj.js +1 -0
- package/dist/web/assets/{railroadDiagram-AXF67PYL-uOtnxRbQ.js → railroadDiagram-AXF67PYL-BhAxCokG.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-DGSZAsj4.js → requirementDiagram-LRYGKXZP-BRW-kYhg.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-CCDk8Vxt.js → sankeyDiagram-W5VNT64P-DfnYLbDJ.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-Ce-Jzdk5.js → sequenceDiagram-SI44F4Z6-WEkIPSg8.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-BYpYzyEa.js → stateDiagram-OKZ733FA-CdN7veIN.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-0xn0gbpn.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-BL-CUc6d.js → swimlanes-SLNWSIFB-g9wWLf5Y.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-YJ3yh_d3.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-p9pkof_K.js → timeline-definition-Z64GVDOM-DjkKZIPE.js} +1 -1
- package/dist/web/assets/{treeView-QDETBFTQ-dRhXTU9p.js → treeView-QDETBFTQ-WARa9r09.js} +1 -1
- package/dist/web/assets/{treemap-6X3UGDF4-CZHe3dIh.js → treemap-6X3UGDF4-CTFjdb6k.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-ipODJegx.js → vennDiagram-T6HMQDX7-CI9QrBwu.js} +1 -1
- package/dist/web/assets/{wardley-OPB4EBWU-CxHlVjkF.js → wardley-OPB4EBWU-Cfo_Nnla.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-YarkIncH.js → wardleyDiagram-T6FBY63Y-CzGJ4fzZ.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-Cw1M-7a4.js → xychartDiagram-ELKLHX3M-BXCiD7gJ.js} +1 -1
- package/dist/web/index.html +2 -2
- package/migrations/010_comment_post_references.sql +17 -0
- package/package.json +2 -1
- package/skills/rvw/SKILL.md +79 -6
- package/skills/rvw-walkthrough/SKILL.md +1 -1
- package/skills/rvw-watch-comments/SKILL.md +217 -67
- package/skills/rvw-watch-comments/scripts/auto-ack.mjs +211 -0
- package/skills/rvw-watch-comments/scripts/preflight.mjs +108 -0
- package/skills/rvw-watch-comments/scripts/rvw-command.mjs +80 -0
- package/skills/rvw-watch-comments/scripts/watch-driver.mjs +272 -0
- package/skills/rvw-watch-comments/scripts/watch-state.mjs +223 -70
- package/dist/web/assets/WalkthroughViewer-BVB3ETHG.js +0 -1
- package/dist/web/assets/architecture-TIHT7OUA-BjGWtjPU.js +0 -1
- package/dist/web/assets/channel-DkH22XO0.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-C6Z4rmZR.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-C6Z4rmZR.js +0 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-BUAcQ5NU.js +0 -1
- package/dist/web/assets/flowDiagram-UKHOOZJN-hAe9ojjP.js +0 -1
- package/dist/web/assets/index-Coye1BY5.js +0 -384
- package/dist/web/assets/index-DFdYNMZE.css +0 -1
- package/dist/web/assets/railroad-abnf-AHOZXSZD-TVRi9_2L.js +0 -1
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-B5B-JrS4.js +0 -1
- package/dist/web/assets/railroad-peg-LSFZ7HO6-CfIpa6ba.js +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BJFG9cb2.js +0 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-BS8L8fb1.js +0 -8
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rvw-watch-comments
|
|
3
|
-
description: Continuously watch all Pull Requests saved in the local rvw database for new root comments and replies, durably queue them, delegate
|
|
3
|
+
description: Continuously watch all Pull Requests saved in the local rvw database for new root comments and replies, durably queue them, acknowledge them immediately, investigate or delegate bounded batches, and replace the acknowledgement with a final rvw reply. Use when a user asks an Agent task to monitor, watch, poll, or continuously address new rvw review comments, optionally allowing fixes and pushes only for Pull Requests authored by the authenticated GitHub user.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Watch rvw comments
|
|
7
7
|
|
|
8
|
-
Run one long-lived parent task as the intake and durable-state owner.
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Run one long-lived parent task as the intake and durable-state owner. The bundled driver owns the
|
|
9
|
+
watch process, cursor resume, RFC 7464 parsing, ingestion, and optional immediate acknowledgement;
|
|
10
|
+
do not recreate that plumbing. rvw never launches or manages an Agent. Use the `rvw` Skill for
|
|
11
|
+
exact-source inspection, final edits, authorized fixes, and synchronization.
|
|
11
12
|
|
|
12
13
|
## Fix policy
|
|
13
14
|
|
|
@@ -27,9 +28,9 @@ rvw replies remain allowed. Never resolve unless the user separately changes tha
|
|
|
27
28
|
|
|
28
29
|
## Durable task state
|
|
29
30
|
|
|
30
|
-
Use
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Use Node 24 and one task-private absolute SQLite path outside every reviewed repository. The state
|
|
32
|
+
tool stores identifiers, cursors, leases, retries, and generated post IDs, but never comment bodies or
|
|
33
|
+
source. Separate watch tasks use separate state databases.
|
|
33
34
|
|
|
34
35
|
Initialize once after running `gh api user --jq .login`:
|
|
35
36
|
|
|
@@ -40,84 +41,174 @@ node '<SKILL_DIR>/scripts/watch-state.mjs' init \
|
|
|
40
41
|
--own-mode 'investigate-and-reply'
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
Omit `--expected-login` and force `investigate-and-reply` when identity is unavailable.
|
|
44
|
-
|
|
44
|
+
Omit `--expected-login` and force `investigate-and-reply` when identity is unavailable.
|
|
45
|
+
Initialization rejects a policy change for an existing task.
|
|
46
|
+
|
|
47
|
+
On restart, run `recover`, then `status`. Both expose `quarantinedBatches`; `status` also exposes
|
|
48
|
+
recoverable `inFlightBatches` with lease IDs and status posts.
|
|
45
49
|
|
|
46
50
|
```bash
|
|
47
51
|
node '<SKILL_DIR>/scripts/watch-state.mjs' recover --state '<TASK_STATE_DB>'
|
|
48
52
|
node '<SKILL_DIR>/scripts/watch-state.mjs' status --state '<TASK_STATE_DB>'
|
|
49
53
|
```
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
Before resuming intake, edit every extant `statusPostId` in quarantined batches to
|
|
56
|
+
`⚠️ 対応を継続できませんでした` with the recorded error. Repeating that exact edit is safe and
|
|
57
|
+
prevents an interrupted third attempt from leaving `確認中` indefinitely.
|
|
54
58
|
|
|
55
59
|
## Start or resume intake
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
Run the single preflight command. It concurrently detects `rvw` and verifies Node `>=24.15.0`.
|
|
62
|
+
Require `protocolVersion` 3 and `agent.transport`, `comment.watch`, `comment.read`, `comment.reply`,
|
|
63
|
+
`comment.edit`, `comment.codeReferences`, and `pullRequest.sync`, and report agent status and ping in
|
|
64
|
+
one JSON value. Stop when `ok` is false. A disconnected ping is diagnostic when status safely selects
|
|
65
|
+
direct-database transport; an unavailable selected transport is fatal.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
node '<SKILL_DIR>/scripts/preflight.mjs'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Start the bundled driver with the state path. `--auto-ack` is the normal mode: it claims an eligible
|
|
72
|
+
PR batch, re-reads every thread, creates `🔎 確認中です…` (or restores it when retrying that batch),
|
|
73
|
+
records suppression, and emits
|
|
74
|
+
one `batch-acknowledged` JSON line containing the lease and operations. The first `watch-ready` line
|
|
75
|
+
means monitoring is established. The driver chooses cursorless start only when state has no cursor;
|
|
76
|
+
that intentionally skips all existing comments. Otherwise it resumes from the exact durable cursor.
|
|
77
|
+
Before each initial connection or reconnect, it auto-acknowledges any eligible event that was durably
|
|
78
|
+
ingested before an earlier driver interruption.
|
|
62
79
|
|
|
63
80
|
```bash
|
|
64
|
-
node '<SKILL_DIR>/scripts/watch-
|
|
81
|
+
node '<SKILL_DIR>/scripts/watch-driver.mjs' '<TASK_STATE_DB>' --auto-ack
|
|
65
82
|
```
|
|
66
83
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
The driver polls rvw once per second. After an unexpected EOF or process exit it re-reads the durable
|
|
85
|
+
cursor and reconnects after 1, 2, 4, 8, then 16 seconds, capped at 30 seconds. Five short-lived
|
|
86
|
+
reconnect failures are terminal; a run lasting at least 30 seconds resets that budget. Protocol error
|
|
87
|
+
frames are terminal because retrying an invalid cursor or incompatible contract cannot recover.
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
Driver exit codes are stable:
|
|
72
90
|
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
| Exit | Meaning |
|
|
92
|
+
| ---- | ------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `0` | Graceful `SIGINT` / `SIGTERM` stop after forwarding termination to rvw. |
|
|
94
|
+
| `20` | rvw process, startup, protocol error frame, or reconnect budget failure. |
|
|
95
|
+
| `21` | Malformed, non-RFC-7464, or truncated watch output. |
|
|
96
|
+
| `22` | Durable state status or ingest failure. |
|
|
97
|
+
| `23` | Automatic acknowledgement failed; its claimed lease has already been returned to retry or quarantine when possible. |
|
|
98
|
+
|
|
99
|
+
Without `--auto-ack`, the driver emits `pending` lines and leaves the batch unclaimed. An external
|
|
100
|
+
monitor can also wait independently without hand-written polling:
|
|
75
101
|
|
|
76
102
|
```bash
|
|
77
|
-
node '<SKILL_DIR>/scripts/watch-state.mjs'
|
|
103
|
+
node '<SKILL_DIR>/scripts/watch-state.mjs' wait --state '<TASK_STATE_DB>'
|
|
78
104
|
```
|
|
79
105
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
106
|
+
`wait` immediately returns existing eligible work or waits for the pending set to change from empty
|
|
107
|
+
to non-empty, then prints one JSON line with `pullRequests` and `pending`. Add `--follow` to emit every
|
|
108
|
+
later empty-to-non-empty transition. The driver and `ingest` commit each event and cursor atomically;
|
|
109
|
+
a crash before that commit causes rvw to replay it. Never construct or edit cursors.
|
|
110
|
+
|
|
111
|
+
## Acknowledge and process a batch
|
|
112
|
+
|
|
113
|
+
With the normal auto-ack driver, consume its `batch-acknowledged` object directly. Each operation has
|
|
114
|
+
`commentRef`, the batch-operation-stable `idempotencyKey`, `statusPostId`, `acknowledgement`, `status`, and the
|
|
115
|
+
fresh `comment get` result as `thread`. A disappeared thread has `status: "gone"` and is not
|
|
116
|
+
acknowledged. If intake runs without auto-ack, invoke the same complete fast path once for the PR:
|
|
83
117
|
|
|
84
118
|
```bash
|
|
85
|
-
node '<SKILL_DIR>/scripts/
|
|
119
|
+
node '<SKILL_DIR>/scripts/auto-ack.mjs' \
|
|
86
120
|
--state '<TASK_STATE_DB>' \
|
|
87
121
|
--pull-request '<PR_URL>'
|
|
88
122
|
```
|
|
89
123
|
|
|
90
|
-
|
|
91
|
-
|
|
124
|
+
For a null `statusPostId`, auto-ack sends exactly `{ "body": "🔎 確認中です…",
|
|
125
|
+
"idempotencyKey": "<BATCH_OPERATION_KEY>" }` to `rvw comment reply`, then records the returned post. It omits
|
|
126
|
+
`authorLabel` and `relatedCommitOid`, so an uncertain retry has the identical payload. For an existing
|
|
127
|
+
status post in the same retried batch it sends
|
|
128
|
+
`{ "body": "🔎 確認中です…", "relatedCommitOid": null }` to `comment edit`. A later batch for the
|
|
129
|
+
same thread has a new key and null `statusPostId`, so it creates another acknowledgement and never
|
|
130
|
+
rewrites the previous final answer.
|
|
131
|
+
The acknowledgement's watch event is suppressed even when intake queued it before the post ID was
|
|
132
|
+
recorded.
|
|
92
133
|
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
134
|
+
The auto-ack claim initially has no repository write reservation. This lets acknowledgement remain
|
|
135
|
+
fast without a live GitHub round trip. If the batch later passes every fix-and-push check, reserve its
|
|
136
|
+
verified head repository immediately before the first code write:
|
|
96
137
|
|
|
97
138
|
```bash
|
|
98
|
-
node '<SKILL_DIR>/scripts/watch-state.mjs'
|
|
99
|
-
--state '<TASK_STATE_DB>'
|
|
139
|
+
node '<SKILL_DIR>/scripts/watch-state.mjs' reserve-write \
|
|
140
|
+
--state '<TASK_STATE_DB>' \
|
|
141
|
+
--lease '<LEASE_ID>' \
|
|
142
|
+
--write-key '<HEAD_OWNER>/<HEAD_REPOSITORY>'
|
|
100
143
|
```
|
|
101
144
|
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
The unique reservation prevents two leases from writing the same repository. A manually invoked
|
|
146
|
+
`auto-ack` may instead receive `--write-key` when that identity was already verified.
|
|
147
|
+
|
|
148
|
+
## Investigate directly or delegate
|
|
149
|
+
|
|
150
|
+
For an `investigate-and-reply` batch containing only one or two comments with a focused source scope,
|
|
151
|
+
the parent may investigate directly when doing so will not materially delay intake handling. Do not
|
|
152
|
+
pay worker startup and result-relay cost for those small batches. Delegate broader investigation,
|
|
153
|
+
multiple unrelated comments, or any authorized fix-and-push batch to one fresh worker per PR. The
|
|
154
|
+
driver continues intake independently while the parent or worker investigates.
|
|
155
|
+
|
|
156
|
+
Workers never access the task state or post rvw replies. Give a worker the raw comment URIs, policy,
|
|
157
|
+
expected login, repository location, live head identity when relevant, lease ID, and one absolute
|
|
158
|
+
result path outside the reviewed repository. Require an atomic write (temporary sibling followed by
|
|
159
|
+
rename) of exactly this final JSON shape:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"leaseId": "<LEASE_ID>",
|
|
164
|
+
"pullRequest": "https://github.com/owner/repository/pull/123",
|
|
165
|
+
"outcomes": [
|
|
166
|
+
{
|
|
167
|
+
"commentRef": "rvw://comment/uuid",
|
|
168
|
+
"body": "📝 調査結果\n\nThe failure is handled by [the retry guard](rvw-ref:retry-guard).",
|
|
169
|
+
"relatedCommitOid": "0123456789abcdef0123456789abcdef01234567",
|
|
170
|
+
"references": [
|
|
171
|
+
{
|
|
172
|
+
"id": "retry-guard",
|
|
173
|
+
"label": "Retry guard",
|
|
174
|
+
"path": "src/request-handler.ts",
|
|
175
|
+
"startLine": 18,
|
|
176
|
+
"endLine": 24
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"pushStatus": "not-needed"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
104
184
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
185
|
+
`pushStatus` is `not-attempted`, `not-needed`, or `pushed`. `relatedCommitOid` is the exact available PR
|
|
186
|
+
commit containing every referenced path and may identify investigation evidence even when no change
|
|
187
|
+
was made. Set it to null only when `references` is empty. `references` is always the complete array for
|
|
188
|
+
that outcome. The worker's completion notification only signals that the file is ready. The parent
|
|
189
|
+
reads and validates the file after that notification and never depends on relayed message text for the
|
|
190
|
+
result. Accept no progress, plans, or partial findings as the final result.
|
|
108
191
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
192
|
+
For every concrete claim about code behavior, an implemented change, or relevant test coverage, use
|
|
193
|
+
typed references by default so the reviewer can open the exact evidence. Select the smallest useful
|
|
194
|
+
committed range, include a signature plus the relevant body for multi-line behavior, link every
|
|
195
|
+
declaration from `body` as `rvw-ref:<referenceId>`, and keep IDs unique within the post. A repository
|
|
196
|
+
comment target already opens its exact source; do not duplicate it unless a separately labeled range
|
|
197
|
+
adds navigation value. Omit references only for outcomes without useful code evidence, uncommitted
|
|
198
|
+
evidence, terminal errors, or target-only evidence where another link would not help navigation.
|
|
113
199
|
|
|
114
|
-
Re-read each
|
|
115
|
-
with exactly one final outcome:
|
|
200
|
+
Re-read each extant thread immediately before applying a direct or file result. Replace its recorded
|
|
201
|
+
status post with exactly one final outcome:
|
|
116
202
|
|
|
117
203
|
- `✅ 対応しました` followed by the change, commit, and test result.
|
|
118
204
|
- `📝 調査結果` followed by the conclusion when no code change was made.
|
|
119
205
|
- `⚠️ 対応を継続できませんでした` followed by the terminal reason.
|
|
120
206
|
|
|
207
|
+
Validate the outcome's body, `relatedCommitOid`, and complete `references` array against the freshly
|
|
208
|
+
read thread, then pass all three fields to `rvw comment edit`. A result without references must send
|
|
209
|
+
`references: []`; set `relatedCommitOid` to null unless the post needs that commit for repository links
|
|
210
|
+
or images. Never leave references from the acknowledgement or a previous retry on the status post.
|
|
211
|
+
|
|
121
212
|
Finish the lease only after every required final edit succeeds:
|
|
122
213
|
|
|
123
214
|
```bash
|
|
@@ -125,28 +216,32 @@ node '<SKILL_DIR>/scripts/watch-state.mjs' complete \
|
|
|
125
216
|
--state '<TASK_STATE_DB>' --lease '<LEASE_ID>'
|
|
126
217
|
```
|
|
127
218
|
|
|
128
|
-
Pass `{ "postIds": [] }` over closed stdin. The field remains available to suppress
|
|
219
|
+
Pass `{ "postIds": [] }` over closed stdin. The field remains available to suppress exceptional
|
|
129
220
|
additional task-created posts. If a thread or its recorded status post disappeared during work,
|
|
130
|
-
complete it without creating a replacement and report it as gone.
|
|
131
|
-
|
|
221
|
+
complete it without creating a replacement and report it as gone. Comment and reply bodies are UTF-8
|
|
222
|
+
GFM Markdown up to 64 KiB, not 4 KiB; a 4093-byte result is within the contract.
|
|
132
223
|
|
|
133
224
|
## Choose the worker mode
|
|
134
225
|
|
|
135
|
-
Use `fix-and-push` only when all checks succeed immediately before the first
|
|
226
|
+
Use `fix-and-push` only when all checks succeed immediately before reserving and making the first
|
|
227
|
+
write:
|
|
136
228
|
|
|
137
229
|
1. The immutable task policy allows it.
|
|
138
230
|
2. `gh api user --jq .login` still equals the expected login, case-insensitively.
|
|
139
231
|
3. `rvw comment get <URI> --live --json` reports the same PR author login.
|
|
140
232
|
4. Live `headRepository.owner`, `headRepository.name`, branch, and head OID are all present.
|
|
141
233
|
5. The intended push URL, branch, and current remote head exactly match those live values.
|
|
234
|
+
6. `reserve-write` succeeds for the live head repository.
|
|
142
235
|
|
|
143
|
-
Otherwise
|
|
144
|
-
|
|
236
|
+
Otherwise use `investigate-and-reply`. Never infer ownership or a push target from the base repository,
|
|
237
|
+
branch name alone, local Git author, remote name, or rvw `authorLabel`.
|
|
145
238
|
|
|
146
239
|
### Investigate and reply
|
|
147
240
|
|
|
148
|
-
Inspect exact and surrounding source read-only and
|
|
149
|
-
comment.
|
|
241
|
+
Inspect exact and surrounding source read-only and produce one concise final outcome per affected
|
|
242
|
+
comment. Use the exact commit that supports the conclusion as `relatedCommitOid` and follow the code
|
|
243
|
+
evidence defaults above even though no commit was pushed. The parent edits the recorded status post;
|
|
244
|
+
do not add another final reply.
|
|
150
245
|
|
|
151
246
|
### Fix and push an owned PR
|
|
152
247
|
|
|
@@ -158,18 +253,73 @@ retrying; never repeat the implementation blindly.
|
|
|
158
253
|
|
|
159
254
|
After GitHub exposes the pushed head, run `rvw pr sync --repository '<WORKTREE>' --stdin --json`
|
|
160
255
|
without comment updates. Then edit each status post with its final body and the synchronized head as
|
|
161
|
-
`relatedCommitOid`.
|
|
256
|
+
`relatedCommitOid`. Follow the code evidence defaults above: link the implemented behavior and relevant
|
|
257
|
+
test ranges from the final body and include the post's complete typed `references` array at that exact
|
|
258
|
+
head. If the outcome has no useful code evidence, send `references: []` explicitly rather than
|
|
259
|
+
retaining stale declarations.
|
|
162
260
|
|
|
163
261
|
## Failure and stop
|
|
164
262
|
|
|
165
|
-
Report a failed lease through `fail` with `{ "error": "...", "retryable": true }` over stdin.
|
|
166
|
-
state tool retains the same batch, status posts, and idempotency keys, retries after about 10
|
|
167
|
-
and then 1 minute, and quarantines it after the third failed attempt. Leave
|
|
168
|
-
for a scheduled retry. Before a non-retryable failure or the third failed
|
|
169
|
-
status post to the terminal warning form, then call `fail`. On a recovered
|
|
170
|
-
the acknowledgement before work.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
263
|
+
Report a failed lease through `fail` with `{ "error": "...", "retryable": true }` over closed stdin.
|
|
264
|
+
The state tool retains the same batch, status posts, and idempotency keys, retries after about 10
|
|
265
|
+
seconds and then 1 minute, and quarantines it after the third failed attempt. Leave
|
|
266
|
+
`🔎 確認中です…` unchanged for a scheduled retry. Before a non-retryable failure or the third failed
|
|
267
|
+
attempt, edit every extant status post to the terminal warning form, then call `fail`. On a recovered
|
|
268
|
+
retry, auto-ack restores the acknowledgement before work. Continue unrelated PRs.
|
|
269
|
+
|
|
270
|
+
On graceful stop, stop dispatching, let active writes reach a safe boundary, terminate the driver,
|
|
271
|
+
and report `status`. Resume with the stored cursor and `recover`; never start the same task twice with
|
|
272
|
+
one state database.
|
|
273
|
+
|
|
274
|
+
## Bundled CLI contract reference
|
|
275
|
+
|
|
276
|
+
Successful commands write exactly one newline-terminated JSON object to stdout, except `wait --follow`
|
|
277
|
+
and the long-lived driver, which write one object per transition. State-command errors and driver
|
|
278
|
+
fatal errors write JSON to stderr with a nonzero exit; auto-ack returns its structured failure on
|
|
279
|
+
stdout with a nonzero exit. Commands marked with stdin read one complete JSON object through EOF.
|
|
280
|
+
|
|
281
|
+
| Command | Arguments | stdin JSON | Success JSON |
|
|
282
|
+
| --------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
283
|
+
| `init` | `--state PATH [--expected-login LOGIN] [--own-mode investigate-and-reply\|fix-and-push]` | none | `{ok,state,taskId,databaseId,cursor,expectedGitHubLogin,ownPullRequests,batches,inFlightBatches,quarantinedBatches}` |
|
|
284
|
+
| `ingest` | `--state PATH` | `ready`, `comment-posted`, or `stopped` frame from rvw | `{ok,status,cursor[,sequence]}`; event and cursor commit atomically |
|
|
285
|
+
| `list` | `--state PATH` | none | `{ok,pending:[{pullRequest,batchId,eventCount,firstSequence,commentRefs}]}` |
|
|
286
|
+
| `wait` | `--state PATH [--interval-ms N] [--follow]` | none | `{ok,type:"pending",pullRequests,pending}` on empty-to-non-empty |
|
|
287
|
+
| `claim` | `--state PATH --pull-request URL [--write-key owner/repo]` | none | `{ok,leaseId,batchId,pullRequest,attempts,writeKey,events,operations}` |
|
|
288
|
+
| `reserve-write` | `--state PATH --lease ID --write-key owner/repo` | none | `{ok,leaseId,batchId,pullRequest,writeKey,status}` |
|
|
289
|
+
| `ack` | `--state PATH --lease ID` | `{commentRef,postId}` | `{ok,batchId,commentRef,statusPostId,status}` |
|
|
290
|
+
| `complete` | `--state PATH --lease ID` | `{postIds:string[]}` | `{ok,batchId,status:"completed",suppressedPostIds}` |
|
|
291
|
+
| `fail` | `--state PATH --lease ID` | `{error:string,retryable:boolean}` | `{ok,batchId,status,attempts,nextAttemptAt[,operations]}` |
|
|
292
|
+
| `recover` | `--state PATH` | none | `{ok,recovered,pending,quarantined,quarantinedBatches}` |
|
|
293
|
+
| `status` | `--state PATH` | none | task policy, cursor, batch counts, `inFlightBatches`, and `quarantinedBatches` |
|
|
294
|
+
|
|
295
|
+
Frame schemas accepted by `ingest`:
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{ "type": "ready", "databaseId": "32 lowercase hex", "cursor": "opaque", "anchoredAtCurrent": true }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"type": "comment-posted",
|
|
304
|
+
"cursor": "opaque",
|
|
305
|
+
"event": {
|
|
306
|
+
"sequence": 1,
|
|
307
|
+
"postId": "id",
|
|
308
|
+
"commentRef": "rvw://comment/uuid",
|
|
309
|
+
"pullRequestUrl": "https://github.com/owner/repo/pull/123",
|
|
310
|
+
"createdAt": "ISO-8601",
|
|
311
|
+
"deleted": false
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Claim `operations` are `{commentRef,idempotencyKey,statusPostId}`. Claim `events` are
|
|
317
|
+
`{sequence,postId,commentRef,pullRequestUrl}`. State schema additions are created with
|
|
318
|
+
idempotent local migrations; existing state databases remain readable and retain their cursors,
|
|
319
|
+
leases, and unfinished batch keys and status posts.
|
|
320
|
+
|
|
321
|
+
| Script | Invocation | Output |
|
|
322
|
+
| --------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
323
|
+
| Preflight | `node scripts/preflight.mjs` | One aggregate `{ok,node,rvw,agent,checks,errors}` object. |
|
|
324
|
+
| Driver | `node scripts/watch-driver.mjs STATE [--auto-ack]` | `watch-ready`, `pending`, `batch-acknowledged`, and reconnect JSON lines. |
|
|
325
|
+
| Auto-ack | `node scripts/auto-ack.mjs --state STATE --pull-request URL [--write-key owner/repo]` | Claimed lease plus `{events,operations}`; each operation includes the fresh thread or `gone`. |
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { runRvw, successfulJson } from "./rvw-command.mjs";
|
|
6
|
+
|
|
7
|
+
const ACKNOWLEDGEMENT_BODY = "🔎 確認中です…";
|
|
8
|
+
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const stateScript = path.join(scriptDirectory, "watch-state.mjs");
|
|
10
|
+
|
|
11
|
+
function fail(message, details) {
|
|
12
|
+
const error = new Error(message);
|
|
13
|
+
error.details = details;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseOptions(values) {
|
|
18
|
+
const options = {};
|
|
19
|
+
for (let index = 0; index < values.length; index += 2) {
|
|
20
|
+
const key = values[index];
|
|
21
|
+
const value = values[index + 1];
|
|
22
|
+
if (!key?.startsWith("--") || value === undefined || value.startsWith("--")) {
|
|
23
|
+
fail(`Expected --name value, received ${key ?? ""}`);
|
|
24
|
+
}
|
|
25
|
+
options[key.slice(2)] = value;
|
|
26
|
+
}
|
|
27
|
+
return options;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function required(options, key) {
|
|
31
|
+
const value = options[key];
|
|
32
|
+
if (typeof value !== "string" || value.length === 0) fail(`--${key} is required`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function runState(state, command, args = [], input) {
|
|
37
|
+
const { spawn } = await import("node:child_process");
|
|
38
|
+
const child = spawn(process.execPath, [stateScript, command, "--state", state, ...args], {
|
|
39
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
40
|
+
});
|
|
41
|
+
const stdout = [];
|
|
42
|
+
const stderr = [];
|
|
43
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
44
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
45
|
+
child.stdin.end(input === undefined ? undefined : JSON.stringify(input));
|
|
46
|
+
const code = await new Promise((resolve, reject) => {
|
|
47
|
+
child.once("error", reject);
|
|
48
|
+
child.once("close", resolve);
|
|
49
|
+
});
|
|
50
|
+
const stdoutText = Buffer.concat(stdout).toString("utf8");
|
|
51
|
+
const stderrText = Buffer.concat(stderr).toString("utf8");
|
|
52
|
+
let json = null;
|
|
53
|
+
try {
|
|
54
|
+
json = JSON.parse(stdoutText);
|
|
55
|
+
} catch {
|
|
56
|
+
// Report the original output below.
|
|
57
|
+
}
|
|
58
|
+
if (code !== 0 || !json?.ok) {
|
|
59
|
+
fail(`watch-state ${command} failed`, { code, stdout: stdoutText, stderr: stderrText });
|
|
60
|
+
}
|
|
61
|
+
return json;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function rvwFailure(command, result) {
|
|
65
|
+
return {
|
|
66
|
+
command,
|
|
67
|
+
exitCode: result.code,
|
|
68
|
+
signal: result.signal,
|
|
69
|
+
output: result.json,
|
|
70
|
+
stderr: result.stderr.trim() || null,
|
|
71
|
+
stdout: result.json ? null : result.stdout.trim() || null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isGone(result) {
|
|
76
|
+
return (
|
|
77
|
+
result.json?.ok === false &&
|
|
78
|
+
["COMMENT_NOT_FOUND", "NOT_FOUND"].includes(result.json?.error?.code)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function acknowledgeOperation(state, leaseId, operation, threadResult) {
|
|
83
|
+
if (isGone(threadResult)) {
|
|
84
|
+
return {
|
|
85
|
+
...operation,
|
|
86
|
+
status: "gone",
|
|
87
|
+
acknowledgement: "skipped",
|
|
88
|
+
thread: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (!successfulJson(threadResult)) {
|
|
92
|
+
fail(`rvw comment get failed for ${operation.commentRef}`, {
|
|
93
|
+
failure: rvwFailure("comment get", threadResult),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (operation.statusPostId === null) {
|
|
97
|
+
const reply = await runRvw(["comment", "reply", operation.commentRef, "--stdin", "--json"], {
|
|
98
|
+
input: {
|
|
99
|
+
body: ACKNOWLEDGEMENT_BODY,
|
|
100
|
+
idempotencyKey: operation.idempotencyKey,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
if (!successfulJson(reply) || typeof reply.json?.post?.id !== "string") {
|
|
104
|
+
fail(`rvw comment reply failed for ${operation.commentRef}`, {
|
|
105
|
+
failure: rvwFailure("comment reply", reply),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
await runState(state, "ack", ["--lease", leaseId], {
|
|
109
|
+
commentRef: operation.commentRef,
|
|
110
|
+
postId: reply.json.post.id,
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
...operation,
|
|
114
|
+
statusPostId: reply.json.post.id,
|
|
115
|
+
status: "acknowledged",
|
|
116
|
+
acknowledgement: "created",
|
|
117
|
+
thread: threadResult.json,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const edit = await runRvw(
|
|
121
|
+
[
|
|
122
|
+
"comment",
|
|
123
|
+
"edit",
|
|
124
|
+
operation.commentRef,
|
|
125
|
+
"--post",
|
|
126
|
+
operation.statusPostId,
|
|
127
|
+
"--stdin",
|
|
128
|
+
"--json",
|
|
129
|
+
],
|
|
130
|
+
{ input: { body: ACKNOWLEDGEMENT_BODY, relatedCommitOid: null } },
|
|
131
|
+
);
|
|
132
|
+
if (!successfulJson(edit)) {
|
|
133
|
+
fail(`rvw comment edit failed for ${operation.commentRef}`, {
|
|
134
|
+
failure: rvwFailure("comment edit", edit),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
await runState(state, "ack", ["--lease", leaseId], {
|
|
138
|
+
commentRef: operation.commentRef,
|
|
139
|
+
postId: operation.statusPostId,
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
...operation,
|
|
143
|
+
status: "acknowledged",
|
|
144
|
+
acknowledgement: "restored",
|
|
145
|
+
thread: threadResult.json,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function main() {
|
|
150
|
+
const options = parseOptions(process.argv.slice(2));
|
|
151
|
+
const state = path.resolve(required(options, "state"));
|
|
152
|
+
const pullRequest = required(options, "pull-request");
|
|
153
|
+
let claimed = null;
|
|
154
|
+
try {
|
|
155
|
+
const claimArgs = ["--pull-request", pullRequest];
|
|
156
|
+
if (options["write-key"]) claimArgs.push("--write-key", options["write-key"]);
|
|
157
|
+
claimed = await runState(state, "claim", claimArgs);
|
|
158
|
+
const threadResults = await Promise.all(
|
|
159
|
+
claimed.operations.map((operation) =>
|
|
160
|
+
runRvw(["comment", "get", operation.commentRef, "--json"]),
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
const operations = [];
|
|
164
|
+
for (let index = 0; index < claimed.operations.length; index += 1) {
|
|
165
|
+
operations.push(
|
|
166
|
+
await acknowledgeOperation(
|
|
167
|
+
state,
|
|
168
|
+
claimed.leaseId,
|
|
169
|
+
claimed.operations[index],
|
|
170
|
+
threadResults[index],
|
|
171
|
+
),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
process.stdout.write(
|
|
175
|
+
`${JSON.stringify({
|
|
176
|
+
ok: true,
|
|
177
|
+
type: "acknowledged",
|
|
178
|
+
leaseId: claimed.leaseId,
|
|
179
|
+
batchId: claimed.batchId,
|
|
180
|
+
pullRequest: claimed.pullRequest,
|
|
181
|
+
attempts: claimed.attempts,
|
|
182
|
+
writeKey: claimed.writeKey,
|
|
183
|
+
events: claimed.events,
|
|
184
|
+
operations,
|
|
185
|
+
})}\n`,
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
let leaseFailure = null;
|
|
189
|
+
if (claimed?.leaseId) {
|
|
190
|
+
try {
|
|
191
|
+
leaseFailure = await runState(state, "fail", ["--lease", claimed.leaseId], {
|
|
192
|
+
error: error instanceof Error ? error.message : String(error),
|
|
193
|
+
retryable: true,
|
|
194
|
+
});
|
|
195
|
+
} catch (failError) {
|
|
196
|
+
leaseFailure = { ok: false, error: String(failError) };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
process.stdout.write(
|
|
200
|
+
`${JSON.stringify({
|
|
201
|
+
ok: false,
|
|
202
|
+
error: error instanceof Error ? error.message : String(error),
|
|
203
|
+
details: error?.details ?? null,
|
|
204
|
+
leaseFailure,
|
|
205
|
+
})}\n`,
|
|
206
|
+
);
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await main();
|