@deftai/directive-core 0.84.0 → 0.86.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/dist/cache/fetch.d.ts +17 -0
- package/dist/cache/fetch.js +50 -0
- package/dist/doctor/checks.js +16 -1
- package/dist/doctor/constants.d.ts +4 -2
- package/dist/doctor/constants.js +10 -6
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.d.ts +2 -1
- package/dist/doctor/main.js +83 -22
- package/dist/doctor/npm-registry.d.ts +23 -0
- package/dist/doctor/npm-registry.js +128 -0
- package/dist/doctor/npm-view.d.ts +10 -0
- package/dist/doctor/npm-view.js +31 -0
- package/dist/doctor/payload-staleness.js +2 -9
- package/dist/doctor/signpost-checks.d.ts +2 -1
- package/dist/doctor/signpost-checks.js +2 -0
- package/dist/doctor/types.d.ts +12 -0
- package/dist/eval-health-relocation/evaluate.js +15 -1
- package/dist/fs/projection-containment.js +1 -1
- package/dist/hooks/cursor-hooks.d.ts +6 -20
- package/dist/hooks/cursor-hooks.js +7 -92
- package/dist/init-deposit/agent-hooks.d.ts +2 -2
- package/dist/init-deposit/agent-hooks.js +24 -38
- package/dist/init-deposit/gitignore.js +5 -0
- package/dist/init-deposit/hygiene.d.ts +22 -0
- package/dist/init-deposit/hygiene.js +136 -4
- package/dist/init-deposit/legacy-detect.d.ts +25 -1
- package/dist/init-deposit/legacy-detect.js +143 -11
- package/dist/init-deposit/refresh.d.ts +2 -0
- package/dist/init-deposit/refresh.js +18 -5
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +2 -0
- package/dist/policy/org-force-on-migration.d.ts +44 -0
- package/dist/policy/org-force-on-migration.js +242 -0
- package/dist/policy/product-signal.d.ts +3 -1
- package/dist/policy/product-signal.js +15 -4
- package/dist/policy/value-feedback.d.ts +18 -1
- package/dist/policy/value-feedback.js +78 -5
- package/dist/preflight-cache/evaluate.js +12 -1
- package/dist/product-signal/consent-prompt.d.ts +13 -0
- package/dist/product-signal/consent-prompt.js +30 -0
- package/dist/product-signal/submit.js +22 -1
- package/dist/render/framework-commands.d.ts +1 -1
- package/dist/render/framework-commands.js +6 -6
- package/dist/render/index.d.ts +1 -0
- package/dist/render/index.js +1 -0
- package/dist/render/roadmap-render.d.ts +5 -1
- package/dist/render/roadmap-render.js +20 -2
- package/dist/render/rule-map-template.d.ts +2 -0
- package/dist/render/rule-map-template.js +407 -0
- package/dist/render/rule-map.d.ts +2 -0
- package/dist/render/rule-map.js +708 -0
- package/dist/review-monitor/constants.d.ts +7 -1
- package/dist/review-monitor/constants.js +40 -14
- package/dist/review-monitor/github-lease.d.ts +45 -0
- package/dist/review-monitor/github-lease.js +106 -0
- package/dist/review-monitor/index.d.ts +2 -0
- package/dist/review-monitor/index.js +2 -0
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +152 -0
- package/dist/review-monitor/record.d.ts +61 -13
- package/dist/review-monitor/record.js +379 -132
- package/dist/review-monitor/verify.d.ts +2 -0
- package/dist/review-monitor/verify.js +32 -13
- package/dist/scm/gh-rest.d.ts +7 -1
- package/dist/scm/gh-rest.js +35 -0
- package/dist/session/session-start.js +5 -0
- package/dist/task-surface/index.js +53 -13
- package/dist/verify-env/agent-hooks-live-probe.d.ts +32 -0
- package/dist/verify-env/agent-hooks-live-probe.js +216 -0
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/dist/xbrief-migrate/constants.d.ts +6 -0
- package/dist/xbrief-migrate/constants.js +6 -0
- package/dist/xbrief-migrate/index.d.ts +2 -2
- package/dist/xbrief-migrate/index.js +2 -2
- package/dist/xbrief-migrate/migrate-project.d.ts +7 -0
- package/dist/xbrief-migrate/migrate-project.js +44 -18
- package/package.json +3 -3
|
@@ -1,163 +1,384 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { resolveRepo } from "../triage/queue/repo.js";
|
|
3
|
+
import { DEFAULT_STALE_MINUTES, EXIT_CONFIG_ERROR, EXIT_CONFLICT, EXIT_READY, SCHEMA_VERSION, } from "./constants.js";
|
|
4
|
+
import { createReviewOwnerComment, deleteReviewOwnerComment, listReviewOwnerComments, resolveGitHubLogin, updateReviewOwnerComment, } from "./github-lease.js";
|
|
5
|
+
import { computeExpiresAt, findActiveLeaseComment, isLeaseExpired, parseIso8601Utc, parseReviewOwnerLease, renderReleasedReviewOwnerComment, renderReviewOwnerComment, selectWinningReviewOwnerComment, } from "./lease-comment.js";
|
|
6
|
+
export { parseIso8601Utc };
|
|
7
|
+
function leaseToRecord(pr, repo, lease, commentId, worktreePath, parentSessionId) {
|
|
8
|
+
return {
|
|
9
|
+
pr,
|
|
10
|
+
repo,
|
|
11
|
+
head_sha: lease.head_sha,
|
|
12
|
+
platform_primitive: lease.platform_primitive,
|
|
13
|
+
monitor_agent_id: lease.monitor_agent_id,
|
|
14
|
+
owner: lease.owner,
|
|
15
|
+
started_at: lease.started_at,
|
|
16
|
+
expires_at: lease.expires_at,
|
|
17
|
+
worktree_path: worktreePath,
|
|
18
|
+
parent_session_id: parentSessionId,
|
|
19
|
+
ended_at: lease.ended_at,
|
|
20
|
+
comment_id: commentId,
|
|
21
|
+
};
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
return
|
|
23
|
+
function buildLease(input) {
|
|
24
|
+
const startedAtIso = input.startedAt.toISOString();
|
|
25
|
+
return {
|
|
26
|
+
owner: input.owner,
|
|
27
|
+
monitor_agent_id: input.monitorAgentId.trim(),
|
|
28
|
+
head_sha: input.headSha,
|
|
29
|
+
started_at: startedAtIso,
|
|
30
|
+
expires_at: computeExpiresAt(input.startedAt, input.staleMinutes),
|
|
31
|
+
platform_primitive: input.platformPrimitive,
|
|
32
|
+
ended_at: null,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function resolveRegisterRepo(input) {
|
|
36
|
+
return resolveRepo(input.repo ?? null, resolve(input.projectRoot));
|
|
26
37
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
function sameClaimHolder(lease, owner, monitorAgentId) {
|
|
39
|
+
return lease.monitor_agent_id === monitorAgentId.trim() && lease.owner === owner;
|
|
40
|
+
}
|
|
41
|
+
function conflictMessage(holder, pr) {
|
|
42
|
+
return (`review_monitor_register: PR #${pr} review-owner lease held by ${holder.owner} ` +
|
|
43
|
+
`(monitor_agent_id=${holder.monitor_agent_id}, expires_at=${holder.expires_at}).`);
|
|
44
|
+
}
|
|
45
|
+
function resolveCreateRace(repo, pr, createdId, seams) {
|
|
46
|
+
const listed = listReviewOwnerComments(repo, pr, seams);
|
|
47
|
+
if (!Array.isArray(listed)) {
|
|
48
|
+
return {
|
|
49
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
50
|
+
message: `review_monitor_register: ${listed.error}`,
|
|
51
|
+
record: null,
|
|
52
|
+
commentId: null,
|
|
53
|
+
priorOwner: null,
|
|
54
|
+
};
|
|
35
55
|
}
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
const winner = selectWinningReviewOwnerComment(listed);
|
|
57
|
+
if (winner === null || winner.id === createdId) {
|
|
58
|
+
return null;
|
|
38
59
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
if (winner.id !== createdId) {
|
|
61
|
+
const deleteResult = deleteReviewOwnerComment(repo, createdId, seams);
|
|
62
|
+
if ("error" in deleteResult) {
|
|
63
|
+
return {
|
|
64
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
65
|
+
message: `review_monitor_register: create-race loser could not delete duplicate: ${deleteResult.error}`,
|
|
66
|
+
record: null,
|
|
67
|
+
commentId: null,
|
|
68
|
+
priorOwner: winner?.lease?.owner ?? null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const holder = winner?.lease ?? null;
|
|
72
|
+
return {
|
|
73
|
+
exitCode: EXIT_CONFLICT,
|
|
74
|
+
message: holder !== null
|
|
75
|
+
? conflictMessage(holder, pr)
|
|
76
|
+
: `review_monitor_register: PR #${pr} create-race lost to an older claim comment.`,
|
|
77
|
+
record: null,
|
|
78
|
+
commentId: winner?.id ?? null,
|
|
79
|
+
priorOwner: holder?.owner ?? null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
43
83
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
function verifyExclusiveActiveClaim(repo, pr, commentId, owner, monitorAgentId, seams, options) {
|
|
85
|
+
const relisted = listReviewOwnerComments(repo, pr, seams);
|
|
86
|
+
if (!Array.isArray(relisted)) {
|
|
87
|
+
return {
|
|
88
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
89
|
+
message: `review_monitor_register: ${relisted.error}`,
|
|
90
|
+
record: null,
|
|
91
|
+
commentId: null,
|
|
92
|
+
priorOwner: null,
|
|
93
|
+
};
|
|
47
94
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
95
|
+
const active = findActiveLeaseComment(relisted, options);
|
|
96
|
+
if (active === null || active.id !== commentId) {
|
|
97
|
+
const holder = active?.lease ?? null;
|
|
98
|
+
return {
|
|
99
|
+
exitCode: EXIT_CONFLICT,
|
|
100
|
+
message: holder !== null
|
|
101
|
+
? conflictMessage(holder, pr)
|
|
102
|
+
: `review_monitor_register: PR #${pr} post-write verify failed; active lease not on comment ${commentId}.`,
|
|
103
|
+
record: null,
|
|
104
|
+
commentId: active?.id ?? commentId,
|
|
105
|
+
priorOwner: holder?.owner ?? null,
|
|
106
|
+
};
|
|
51
107
|
}
|
|
52
|
-
|
|
53
|
-
|
|
108
|
+
if (active.lease === null || !sameClaimHolder(active.lease, owner, monitorAgentId)) {
|
|
109
|
+
const holder = active.lease ?? {
|
|
110
|
+
owner: "unknown",
|
|
111
|
+
monitor_agent_id: "unknown",
|
|
112
|
+
expires_at: "",
|
|
113
|
+
head_sha: null,
|
|
114
|
+
started_at: "",
|
|
115
|
+
platform_primitive: "cursor-task",
|
|
116
|
+
ended_at: null,
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
exitCode: EXIT_CONFLICT,
|
|
120
|
+
message: conflictMessage(holder, pr),
|
|
121
|
+
record: null,
|
|
122
|
+
commentId: active.id,
|
|
123
|
+
priorOwner: holder.owner !== owner ? holder.owner : null,
|
|
124
|
+
};
|
|
54
125
|
}
|
|
55
|
-
|
|
56
|
-
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
export function registerReviewMonitor(input) {
|
|
129
|
+
const repo = resolveRegisterRepo(input);
|
|
130
|
+
if (repo === null) {
|
|
131
|
+
return {
|
|
132
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
133
|
+
message: "review_monitor_register: could not resolve owner/repo — pass --repo OWNER/REPO or run inside a git repo with origin",
|
|
134
|
+
record: null,
|
|
135
|
+
commentId: null,
|
|
136
|
+
priorOwner: null,
|
|
137
|
+
};
|
|
57
138
|
}
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
139
|
+
const seams = input.seams ?? {};
|
|
140
|
+
const owner = input.owner ?? resolveGitHubLogin(seams);
|
|
141
|
+
if (owner === null || owner.length === 0) {
|
|
142
|
+
return {
|
|
143
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
144
|
+
message: "review_monitor_register: could not resolve GitHub login — pass --owner or authenticate gh",
|
|
145
|
+
record: null,
|
|
146
|
+
commentId: null,
|
|
147
|
+
priorOwner: null,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const startedAt = input.startedAt ?? new Date();
|
|
151
|
+
const staleMinutes = input.staleMinutes ?? DEFAULT_STALE_MINUTES;
|
|
152
|
+
const monitorAgentId = input.monitorAgentId.trim();
|
|
153
|
+
const worktreePath = resolve(input.projectRoot);
|
|
154
|
+
const listed = listReviewOwnerComments(repo, input.pr, seams);
|
|
155
|
+
if (!Array.isArray(listed)) {
|
|
156
|
+
return {
|
|
157
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
158
|
+
message: `review_monitor_register: ${listed.error}`,
|
|
159
|
+
record: null,
|
|
160
|
+
commentId: null,
|
|
161
|
+
priorOwner: null,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const anchor = selectWinningReviewOwnerComment(listed);
|
|
165
|
+
const active = findActiveLeaseComment(listed, { now: startedAt, headSha: input.headSha ?? null });
|
|
166
|
+
if (active?.lease !== null && active?.lease !== undefined) {
|
|
167
|
+
const holder = active.lease;
|
|
168
|
+
if (sameClaimHolder(holder, owner, monitorAgentId)) {
|
|
169
|
+
const renewed = buildLease({
|
|
170
|
+
owner,
|
|
171
|
+
monitorAgentId,
|
|
172
|
+
headSha: input.headSha ?? holder.head_sha,
|
|
173
|
+
platformPrimitive: input.platformPrimitive,
|
|
174
|
+
startedAt,
|
|
175
|
+
staleMinutes,
|
|
92
176
|
});
|
|
177
|
+
const body = renderReviewOwnerComment(renewed);
|
|
178
|
+
const updated = updateReviewOwnerComment(repo, active.id, body, seams);
|
|
179
|
+
if ("error" in updated) {
|
|
180
|
+
return {
|
|
181
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
182
|
+
message: `review_monitor_register: ${updated.error}`,
|
|
183
|
+
record: null,
|
|
184
|
+
commentId: active.id,
|
|
185
|
+
priorOwner: null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
exitCode: EXIT_READY,
|
|
190
|
+
message: `review_monitor_register: renewed PR #${input.pr} review-owner lease for ${monitorAgentId} (comment ${active.id}).`,
|
|
191
|
+
record: leaseToRecord(input.pr, repo, renewed, active.id, worktreePath, input.parentSessionId ?? null),
|
|
192
|
+
commentId: active.id,
|
|
193
|
+
priorOwner: null,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (!isLeaseExpired(holder, startedAt) && input.force !== true) {
|
|
197
|
+
return {
|
|
198
|
+
exitCode: EXIT_CONFLICT,
|
|
199
|
+
message: conflictMessage(holder, input.pr),
|
|
200
|
+
record: leaseToRecord(input.pr, repo, holder, active.id, null, null),
|
|
201
|
+
commentId: active.id,
|
|
202
|
+
priorOwner: holder.owner,
|
|
203
|
+
};
|
|
93
204
|
}
|
|
205
|
+
const priorOwner = holder.owner;
|
|
206
|
+
const takeover = buildLease({
|
|
207
|
+
owner,
|
|
208
|
+
monitorAgentId,
|
|
209
|
+
headSha: input.headSha ?? null,
|
|
210
|
+
platformPrimitive: input.platformPrimitive,
|
|
211
|
+
startedAt,
|
|
212
|
+
staleMinutes,
|
|
213
|
+
});
|
|
214
|
+
const body = renderReviewOwnerComment(takeover);
|
|
215
|
+
const updated = updateReviewOwnerComment(repo, active.id, body, seams);
|
|
216
|
+
if ("error" in updated) {
|
|
217
|
+
return {
|
|
218
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
219
|
+
message: `review_monitor_register: ${updated.error}`,
|
|
220
|
+
record: null,
|
|
221
|
+
commentId: active.id,
|
|
222
|
+
priorOwner,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const forceNote = input.force === true && !isLeaseExpired(holder, startedAt)
|
|
226
|
+
? ` (forced takeover from ${priorOwner})`
|
|
227
|
+
: isLeaseExpired(holder, startedAt)
|
|
228
|
+
? " (expired lease takeover)"
|
|
229
|
+
: "";
|
|
230
|
+
return {
|
|
231
|
+
exitCode: EXIT_READY,
|
|
232
|
+
message: `review_monitor_register: claimed PR #${input.pr} review-owner lease for ${monitorAgentId}` +
|
|
233
|
+
`${forceNote} (comment ${active.id}).`,
|
|
234
|
+
record: leaseToRecord(input.pr, repo, takeover, active.id, worktreePath, input.parentSessionId ?? null),
|
|
235
|
+
commentId: active.id,
|
|
236
|
+
priorOwner: priorOwner !== owner ? priorOwner : null,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const lease = buildLease({
|
|
240
|
+
owner,
|
|
241
|
+
monitorAgentId,
|
|
242
|
+
headSha: input.headSha ?? null,
|
|
243
|
+
platformPrimitive: input.platformPrimitive,
|
|
244
|
+
startedAt,
|
|
245
|
+
staleMinutes,
|
|
246
|
+
});
|
|
247
|
+
const body = renderReviewOwnerComment(lease);
|
|
248
|
+
if (anchor !== null) {
|
|
249
|
+
const updated = updateReviewOwnerComment(repo, anchor.id, body, seams);
|
|
250
|
+
if ("error" in updated) {
|
|
251
|
+
return {
|
|
252
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
253
|
+
message: `review_monitor_register: ${updated.error}`,
|
|
254
|
+
record: null,
|
|
255
|
+
commentId: anchor.id,
|
|
256
|
+
priorOwner: null,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const verified = verifyExclusiveActiveClaim(repo, input.pr, anchor.id, owner, monitorAgentId, seams, { now: startedAt, headSha: input.headSha ?? null });
|
|
260
|
+
if (verified !== null) {
|
|
261
|
+
return verified;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
exitCode: EXIT_READY,
|
|
265
|
+
message: `review_monitor_register: claimed PR #${input.pr} review-owner lease for ${monitorAgentId} (comment ${anchor.id}).`,
|
|
266
|
+
record: leaseToRecord(input.pr, repo, lease, anchor.id, worktreePath, input.parentSessionId ?? null),
|
|
267
|
+
commentId: anchor.id,
|
|
268
|
+
priorOwner: null,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const created = createReviewOwnerComment(repo, input.pr, body, seams);
|
|
272
|
+
if ("error" in created) {
|
|
273
|
+
return {
|
|
274
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
275
|
+
message: `review_monitor_register: ${created.error}`,
|
|
276
|
+
record: null,
|
|
277
|
+
commentId: null,
|
|
278
|
+
priorOwner: null,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const race = resolveCreateRace(repo, input.pr, created.id, seams);
|
|
282
|
+
if (race !== null) {
|
|
283
|
+
return race;
|
|
94
284
|
}
|
|
95
285
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
export function writeReviewMonitorFile(path, data) {
|
|
104
|
-
atomicWriteJson(path, data);
|
|
105
|
-
}
|
|
106
|
-
export function registerReviewMonitor(input) {
|
|
107
|
-
const path = reviewMonitorPath(input.projectRoot);
|
|
108
|
-
const { data, error } = readReviewMonitorFile(path);
|
|
109
|
-
if (data === null) {
|
|
110
|
-
throw new Error(error ?? "could not read review-monitor file");
|
|
111
|
-
}
|
|
112
|
-
const startedAt = (input.startedAt ?? new Date()).toISOString();
|
|
113
|
-
const record = {
|
|
114
|
-
pr: input.pr,
|
|
115
|
-
repo: input.repo ?? null,
|
|
116
|
-
head_sha: input.headSha ?? null,
|
|
117
|
-
platform_primitive: input.platformPrimitive,
|
|
118
|
-
monitor_agent_id: input.monitorAgentId.trim(),
|
|
119
|
-
started_at: startedAt,
|
|
120
|
-
worktree_path: resolve(input.projectRoot),
|
|
121
|
-
parent_session_id: input.parentSessionId ?? null,
|
|
122
|
-
ended_at: null,
|
|
286
|
+
exitCode: EXIT_READY,
|
|
287
|
+
message: `review_monitor_register: claimed PR #${input.pr} review-owner lease for ${monitorAgentId} (comment ${created.id}).`,
|
|
288
|
+
record: leaseToRecord(input.pr, repo, lease, created.id, worktreePath, input.parentSessionId ?? null),
|
|
289
|
+
commentId: created.id,
|
|
290
|
+
priorOwner: null,
|
|
123
291
|
};
|
|
124
|
-
const active = data.records.filter((r) => r.ended_at === null && r.pr !== input.pr);
|
|
125
|
-
writeReviewMonitorFile(path, {
|
|
126
|
-
schema_version: SCHEMA_VERSION,
|
|
127
|
-
records: [...active, record],
|
|
128
|
-
});
|
|
129
|
-
return { path, record };
|
|
130
292
|
}
|
|
131
|
-
export function
|
|
132
|
-
|
|
133
|
-
|
|
293
|
+
export function releaseReviewMonitor(input) {
|
|
294
|
+
const repo = resolveRepo(input.repo ?? null, resolve(input.projectRoot));
|
|
295
|
+
if (repo === null) {
|
|
296
|
+
return {
|
|
297
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
298
|
+
message: "review_monitor_release: could not resolve owner/repo — pass --repo OWNER/REPO or run inside a git repo with origin",
|
|
299
|
+
};
|
|
134
300
|
}
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
301
|
+
const seams = input.seams ?? {};
|
|
302
|
+
const listed = listReviewOwnerComments(repo, input.pr, seams);
|
|
303
|
+
if (!Array.isArray(listed)) {
|
|
304
|
+
return {
|
|
305
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
306
|
+
message: `review_monitor_release: ${listed.error}`,
|
|
307
|
+
};
|
|
139
308
|
}
|
|
140
|
-
|
|
141
|
-
|
|
309
|
+
const anchor = selectWinningReviewOwnerComment(listed);
|
|
310
|
+
if (anchor === null) {
|
|
311
|
+
return {
|
|
312
|
+
exitCode: EXIT_READY,
|
|
313
|
+
message: `review_monitor_release: no review-owner comment on PR #${input.pr}; nothing to release.`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const active = findActiveLeaseComment(listed, { now: input.endedAt ?? new Date() });
|
|
317
|
+
const owner = input.owner ?? resolveGitHubLogin(seams);
|
|
318
|
+
if (active?.lease !== null && active?.lease !== undefined) {
|
|
319
|
+
const holder = active.lease;
|
|
320
|
+
if (owner === null || owner.length === 0) {
|
|
321
|
+
return {
|
|
322
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
323
|
+
message: "review_monitor_release: could not resolve GitHub login — pass --owner or authenticate gh",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const monitorAgentId = input.monitorAgentId?.trim() ?? null;
|
|
327
|
+
const ownerMatches = holder.owner === owner;
|
|
328
|
+
const authorized = monitorAgentId !== null && monitorAgentId.length > 0
|
|
329
|
+
? ownerMatches && holder.monitor_agent_id === monitorAgentId
|
|
330
|
+
: ownerMatches;
|
|
331
|
+
if (!authorized) {
|
|
332
|
+
return {
|
|
333
|
+
exitCode: EXIT_CONFLICT,
|
|
334
|
+
message: conflictMessage(holder, input.pr),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
142
337
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
338
|
+
const releaseTarget = active ?? anchor;
|
|
339
|
+
const endedAt = (input.endedAt ?? new Date()).toISOString();
|
|
340
|
+
const body = renderReleasedReviewOwnerComment(endedAt);
|
|
341
|
+
const updated = updateReviewOwnerComment(repo, releaseTarget.id, body, seams);
|
|
342
|
+
if ("error" in updated) {
|
|
343
|
+
return {
|
|
344
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
345
|
+
message: `review_monitor_release: ${updated.error}`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
exitCode: EXIT_READY,
|
|
350
|
+
message: `review_monitor_release: released PR #${input.pr} review-owner lease (comment ${releaseTarget.id}).`,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
export function findActiveMonitorForPrFromComments(comments, pr, options) {
|
|
354
|
+
const mapped = comments
|
|
355
|
+
.map((entry) => ({
|
|
356
|
+
id: entry.id,
|
|
357
|
+
body: entry.body,
|
|
358
|
+
createdAt: "",
|
|
359
|
+
lease: parseReviewOwnerLease(entry.body),
|
|
360
|
+
}))
|
|
361
|
+
.filter((entry) => entry.lease !== null);
|
|
362
|
+
const active = findActiveLeaseComment(mapped, {
|
|
363
|
+
now: options.now,
|
|
364
|
+
headSha: options.headSha ?? null,
|
|
365
|
+
});
|
|
366
|
+
if (active?.lease === null || active?.lease === undefined) {
|
|
145
367
|
return null;
|
|
146
368
|
}
|
|
147
|
-
return
|
|
369
|
+
return leaseToRecord(pr, options.repo ?? null, active.lease, active.id, null, null);
|
|
148
370
|
}
|
|
149
371
|
export function isRecordActive(record, options) {
|
|
150
372
|
if (record.ended_at !== null) {
|
|
151
373
|
return false;
|
|
152
374
|
}
|
|
153
375
|
const started = parseIso8601Utc(record.started_at);
|
|
154
|
-
|
|
376
|
+
const expires = parseIso8601Utc(record.expires_at);
|
|
377
|
+
if (started === null || expires === null) {
|
|
155
378
|
return false;
|
|
156
379
|
}
|
|
157
380
|
const now = options.now ?? new Date();
|
|
158
|
-
|
|
159
|
-
const ageMs = now.getTime() - started.getTime();
|
|
160
|
-
if (ageMs > staleMinutes * 60 * 1000) {
|
|
381
|
+
if (now.getTime() > expires.getTime()) {
|
|
161
382
|
return false;
|
|
162
383
|
}
|
|
163
384
|
if (options.headSha !== undefined && options.headSha !== null && record.head_sha !== null) {
|
|
@@ -172,7 +393,33 @@ export function findActiveMonitorForPr(file, pr, options) {
|
|
|
172
393
|
}
|
|
173
394
|
return matches.sort((a, b) => b.started_at.localeCompare(a.started_at))[0] ?? null;
|
|
174
395
|
}
|
|
396
|
+
export function emptyReviewMonitorFile() {
|
|
397
|
+
return { schema_version: SCHEMA_VERSION, records: [] };
|
|
398
|
+
}
|
|
399
|
+
/** @deprecated Local `.deft/review-monitor.json` is obsolete (#2814); always empty. */
|
|
400
|
+
export function readReviewMonitorFile(_path) {
|
|
401
|
+
return { data: emptyReviewMonitorFile(), error: null };
|
|
402
|
+
}
|
|
403
|
+
/** @deprecated Local `.deft/review-monitor.json` is obsolete (#2814); no-op. */
|
|
404
|
+
export function writeReviewMonitorFile(_path, _data) {
|
|
405
|
+
// intentionally no-op
|
|
406
|
+
}
|
|
407
|
+
/** @deprecated Local `.deft/review-monitor.json` is obsolete (#2814). */
|
|
408
|
+
export function reviewMonitorPath(projectRoot) {
|
|
409
|
+
return resolve(projectRoot, ".deft", "review-monitor.json");
|
|
410
|
+
}
|
|
175
411
|
export function defaultSubagentStatusDir(projectRoot) {
|
|
176
|
-
return
|
|
412
|
+
return resolve(projectRoot, ".deft-scratch", "subagent-status");
|
|
413
|
+
}
|
|
414
|
+
export function fetchActiveMonitorFromGithub(repo, pr, options = {}) {
|
|
415
|
+
const listed = listReviewOwnerComments(repo, pr, options.seams ?? {});
|
|
416
|
+
if (!Array.isArray(listed)) {
|
|
417
|
+
return { error: listed.error };
|
|
418
|
+
}
|
|
419
|
+
return findActiveMonitorForPrFromComments(listed, pr, {
|
|
420
|
+
now: options.now,
|
|
421
|
+
headSha: options.headSha ?? null,
|
|
422
|
+
repo,
|
|
423
|
+
});
|
|
177
424
|
}
|
|
178
425
|
//# sourceMappingURL=record.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
+
import type { ReviewOwnerGithubSeams } from "./github-lease.js";
|
|
2
3
|
import { type ReviewMonitorRecord } from "./record.js";
|
|
3
4
|
import { type MonitoringTierProbe } from "./tier-detection.js";
|
|
4
5
|
export type ReviewMonitorCallSite = "solo" | "swarm-phase5-6" | "swarm-phase6-cascade" | "unspecified";
|
|
@@ -13,6 +14,7 @@ export interface VerifyReviewMonitorArgs {
|
|
|
13
14
|
readonly staleMinutes?: number;
|
|
14
15
|
readonly now?: Date;
|
|
15
16
|
readonly environ?: NodeJS.ProcessEnv;
|
|
17
|
+
readonly seams?: ReviewOwnerGithubSeams;
|
|
16
18
|
}
|
|
17
19
|
export interface VerifyReviewMonitorResult {
|
|
18
20
|
readonly exitCode: typeof EXIT_READY | typeof EXIT_NOT_READY | typeof EXIT_CONFIG_ERROR;
|