@deftai/directive-core 0.79.2 → 0.79.3
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/check/orchestrator.d.ts +4 -0
- package/dist/check/orchestrator.js +9 -1
- package/dist/platform/resolve-version.d.ts +2 -6
- package/dist/platform/resolve-version.js +3 -129
- package/dist/pr-merge-readiness/gh.js +13 -5
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.d.ts +18 -0
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +76 -0
- package/dist/pr-monitor/monitor.js +10 -1
- package/dist/pr-watch/constants.d.ts +5 -0
- package/dist/pr-watch/constants.js +27 -0
- package/dist/pr-watch/index.d.ts +1 -1
- package/dist/pr-watch/index.js +1 -1
- package/dist/pr-watch/main.d.ts +3 -0
- package/dist/pr-watch/main.js +29 -3
- package/dist/release/constants.d.ts +5 -0
- package/dist/release/constants.js +14 -2
- package/dist/release/flags.js +16 -0
- package/dist/release/main.js +7 -0
- package/dist/release/pipeline.js +7 -3
- package/dist/release/preflight.js +8 -1
- package/dist/release/skip-ci-incident.d.ts +22 -0
- package/dist/release/skip-ci-incident.js +68 -0
- package/dist/release/types.d.ts +2 -0
- package/dist/release/version.js +68 -32
- package/dist/release-e2e/entrypoint-worker.js +1 -0
- package/dist/release-e2e/entrypoint.js +13 -0
- package/dist/review-monitor/constants.d.ts +14 -0
- package/dist/review-monitor/constants.js +55 -0
- package/dist/review-monitor/index.d.ts +5 -0
- package/dist/review-monitor/index.js +5 -0
- package/dist/review-monitor/record.d.ts +50 -0
- package/dist/review-monitor/record.js +178 -0
- package/dist/review-monitor/tier-detection.d.ts +15 -0
- package/dist/review-monitor/tier-detection.js +61 -0
- package/dist/review-monitor/verify.d.ts +32 -0
- package/dist/review-monitor/verify.js +171 -0
- package/package.json +7 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { closeSync, existsSync, fdatasyncSync, mkdirSync, openSync, readFileSync, renameSync, writeSync, } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { DEFAULT_STALE_MINUTES, REVIEW_MONITOR_FILENAME, SCHEMA_VERSION } from "./constants.js";
|
|
5
|
+
function resolveMainWorktreeRoot(startDir) {
|
|
6
|
+
let root = resolve(startDir);
|
|
7
|
+
try {
|
|
8
|
+
const out = execFileSync("git", ["rev-parse", "--git-common-dir"], {
|
|
9
|
+
cwd: startDir,
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
12
|
+
}).trim();
|
|
13
|
+
if (out.length > 0) {
|
|
14
|
+
const commonDir = isAbsolute(out) ? out : resolve(startDir, out);
|
|
15
|
+
root = dirname(commonDir);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Not a git work tree -- fall back to startDir.
|
|
20
|
+
}
|
|
21
|
+
return root;
|
|
22
|
+
}
|
|
23
|
+
export function reviewMonitorPath(projectRoot) {
|
|
24
|
+
const root = resolveMainWorktreeRoot(projectRoot);
|
|
25
|
+
return join(root, ".deft", REVIEW_MONITOR_FILENAME);
|
|
26
|
+
}
|
|
27
|
+
function atomicWriteJson(path, payload) {
|
|
28
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
29
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
30
|
+
const text = `${JSON.stringify(payload, null, 2)}\n`;
|
|
31
|
+
const fd = openSync(tmp, "w");
|
|
32
|
+
try {
|
|
33
|
+
writeSync(fd, text, undefined, "utf8");
|
|
34
|
+
fdatasyncSync(fd);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
closeSync(fd);
|
|
38
|
+
}
|
|
39
|
+
renameSync(tmp, path);
|
|
40
|
+
}
|
|
41
|
+
export function emptyReviewMonitorFile() {
|
|
42
|
+
return { schema_version: SCHEMA_VERSION, records: [] };
|
|
43
|
+
}
|
|
44
|
+
export function readReviewMonitorFile(path) {
|
|
45
|
+
if (!existsSync(path)) {
|
|
46
|
+
return { data: emptyReviewMonitorFile(), error: null };
|
|
47
|
+
}
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
51
|
+
}
|
|
52
|
+
catch (exc) {
|
|
53
|
+
return { data: null, error: `${path}: invalid JSON (${String(exc)}).` };
|
|
54
|
+
}
|
|
55
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
56
|
+
return { data: null, error: `${path}: review-monitor file must be a JSON object.` };
|
|
57
|
+
}
|
|
58
|
+
const obj = parsed;
|
|
59
|
+
const recordsRaw = obj.records;
|
|
60
|
+
const records = [];
|
|
61
|
+
if (Array.isArray(recordsRaw)) {
|
|
62
|
+
for (const entry of recordsRaw) {
|
|
63
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const e = entry;
|
|
67
|
+
const pr = e.pr;
|
|
68
|
+
const monitorAgentId = e.monitor_agent_id;
|
|
69
|
+
const platformPrimitive = e.platform_primitive;
|
|
70
|
+
const startedAt = e.started_at;
|
|
71
|
+
const worktreePath = e.worktree_path;
|
|
72
|
+
if (typeof pr !== "number" ||
|
|
73
|
+
!Number.isInteger(pr) ||
|
|
74
|
+
pr <= 0 ||
|
|
75
|
+
typeof monitorAgentId !== "string" ||
|
|
76
|
+
monitorAgentId.trim().length === 0 ||
|
|
77
|
+
typeof platformPrimitive !== "string" ||
|
|
78
|
+
typeof startedAt !== "string" ||
|
|
79
|
+
typeof worktreePath !== "string") {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
records.push({
|
|
83
|
+
pr,
|
|
84
|
+
repo: typeof e.repo === "string" ? e.repo : null,
|
|
85
|
+
head_sha: typeof e.head_sha === "string" ? e.head_sha : null,
|
|
86
|
+
platform_primitive: platformPrimitive,
|
|
87
|
+
monitor_agent_id: monitorAgentId.trim(),
|
|
88
|
+
started_at: startedAt,
|
|
89
|
+
worktree_path: worktreePath,
|
|
90
|
+
parent_session_id: typeof e.parent_session_id === "string" ? e.parent_session_id : null,
|
|
91
|
+
ended_at: typeof e.ended_at === "string" ? e.ended_at : null,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
data: {
|
|
97
|
+
schema_version: typeof obj.schema_version === "number" ? obj.schema_version : SCHEMA_VERSION,
|
|
98
|
+
records,
|
|
99
|
+
},
|
|
100
|
+
error: null,
|
|
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,
|
|
123
|
+
};
|
|
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
|
+
}
|
|
131
|
+
export function parseIso8601Utc(value) {
|
|
132
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const trimmed = value.trim();
|
|
136
|
+
let candidate = trimmed;
|
|
137
|
+
if (trimmed.endsWith("Z")) {
|
|
138
|
+
candidate = `${trimmed.slice(0, -1)}+00:00`;
|
|
139
|
+
}
|
|
140
|
+
if (!candidate.endsWith("+00:00")) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const parsed = new Date(candidate);
|
|
144
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
export function isRecordActive(record, options) {
|
|
150
|
+
if (record.ended_at !== null) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const started = parseIso8601Utc(record.started_at);
|
|
154
|
+
if (started === null) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const now = options.now ?? new Date();
|
|
158
|
+
const staleMinutes = options.staleMinutes ?? DEFAULT_STALE_MINUTES;
|
|
159
|
+
const ageMs = now.getTime() - started.getTime();
|
|
160
|
+
if (ageMs > staleMinutes * 60 * 1000) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (options.headSha !== undefined && options.headSha !== null && record.head_sha !== null) {
|
|
164
|
+
return record.head_sha === options.headSha;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
export function findActiveMonitorForPr(file, pr, options) {
|
|
169
|
+
const matches = file.records.filter((r) => r.pr === pr && isRecordActive(r, options));
|
|
170
|
+
if (matches.length === 0) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return matches.sort((a, b) => b.started_at.localeCompare(a.started_at))[0] ?? null;
|
|
174
|
+
}
|
|
175
|
+
export function defaultSubagentStatusDir(projectRoot) {
|
|
176
|
+
return join(resolve(projectRoot), ".deft-scratch", "subagent-status");
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MONITORING_TIER_1, MONITORING_TIER_2, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
+
export type PlatformPrimitive = "start_agent" | "spawn_subagent" | "cursor-task";
|
|
3
|
+
export interface MonitoringTierProbe {
|
|
4
|
+
readonly tier: typeof MONITORING_TIER_1 | typeof MONITORING_TIER_2 | typeof MONITORING_TIER_3;
|
|
5
|
+
readonly primitive: PlatformPrimitive | null;
|
|
6
|
+
readonly descriptor: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
10
|
+
* (#1877 / #2655). Prefer `task platform:capabilities` when available (#1357);
|
|
11
|
+
* this probe does not block MVP.
|
|
12
|
+
*/
|
|
13
|
+
export declare function probeMonitoringTier(environ?: NodeJS.ProcessEnv): MonitoringTierProbe;
|
|
14
|
+
export declare function isTier1(probe: MonitoringTierProbe): boolean;
|
|
15
|
+
//# sourceMappingURL=tier-detection.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MONITORING_TIER_1, MONITORING_TIER_2, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
3
|
+
function envTruthy(environ, name) {
|
|
4
|
+
return TRUTHY.has((environ[name] ?? "").trim().toLowerCase());
|
|
5
|
+
}
|
|
6
|
+
function probeOverride(environ) {
|
|
7
|
+
const raw = (environ.DEFT_MONITOR_TIER ?? environ.DEFT_MONITOR_TIER_OVERRIDE ?? "").trim();
|
|
8
|
+
if (raw === "1" || raw.toLowerCase() === "tier1") {
|
|
9
|
+
const primitive = environ.DEFT_MONITOR_TIER1_PRIMITIVE ?? "cursor-task";
|
|
10
|
+
return { tier: MONITORING_TIER_1, primitive, descriptor: "override-tier1" };
|
|
11
|
+
}
|
|
12
|
+
if (raw === "3" || raw.toLowerCase() === "tier3") {
|
|
13
|
+
return { tier: MONITORING_TIER_3, primitive: null, descriptor: "generic-terminal" };
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
19
|
+
* (#1877 / #2655). Prefer `task platform:capabilities` when available (#1357);
|
|
20
|
+
* this probe does not block MVP.
|
|
21
|
+
*/
|
|
22
|
+
export function probeMonitoringTier(environ = process.env) {
|
|
23
|
+
const override = probeOverride(environ);
|
|
24
|
+
if (override !== null) {
|
|
25
|
+
return override;
|
|
26
|
+
}
|
|
27
|
+
if (envTruthy(environ, "DEFT_PROBE_START_AGENT") || envTruthy(environ, "DEFT_HAS_START_AGENT")) {
|
|
28
|
+
return { tier: MONITORING_TIER_1, primitive: "start_agent", descriptor: "warp-orchestrated" };
|
|
29
|
+
}
|
|
30
|
+
if (envTruthy(environ, "WARP_IS_WARP_TERMINAL") || envTruthy(environ, "WARP_TERMINAL_SESSION")) {
|
|
31
|
+
return { tier: MONITORING_TIER_1, primitive: "start_agent", descriptor: "warp-manual" };
|
|
32
|
+
}
|
|
33
|
+
if (envTruthy(environ, "CURSOR_COMPOSER")) {
|
|
34
|
+
return { tier: MONITORING_TIER_1, primitive: "cursor-task", descriptor: "cursor-composer" };
|
|
35
|
+
}
|
|
36
|
+
if (envTruthy(environ, "CURSOR_AGENT")) {
|
|
37
|
+
return {
|
|
38
|
+
tier: MONITORING_TIER_1,
|
|
39
|
+
primitive: "cursor-task",
|
|
40
|
+
descriptor: "cursor-cloud-agent",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const runtime = (environ.DEFT_AGENT_RUNTIME ?? "").trim().toLowerCase();
|
|
44
|
+
if (envTruthy(environ, "DEFT_PROBE_GROK_BUILD") ||
|
|
45
|
+
envTruthy(environ, "GROK_BUILD") ||
|
|
46
|
+
runtime === "grok-build") {
|
|
47
|
+
return { tier: MONITORING_TIER_1, primitive: "spawn_subagent", descriptor: "grok-build" };
|
|
48
|
+
}
|
|
49
|
+
if (envTruthy(environ, "DEFT_PROBE_SPAWN_SUBAGENT") ||
|
|
50
|
+
envTruthy(environ, "DEFT_HAS_SPAWN_SUBAGENT")) {
|
|
51
|
+
return { tier: MONITORING_TIER_1, primitive: "spawn_subagent", descriptor: "grok-build" };
|
|
52
|
+
}
|
|
53
|
+
if (envTruthy(environ, "DEFT_MONITOR_TIER2") || envTruthy(environ, "DEFT_HAS_AUTO_REINVOKE")) {
|
|
54
|
+
return { tier: MONITORING_TIER_2, primitive: null, descriptor: "yield-between-polls" };
|
|
55
|
+
}
|
|
56
|
+
return { tier: MONITORING_TIER_3, primitive: null, descriptor: "generic-terminal" };
|
|
57
|
+
}
|
|
58
|
+
export function isTier1(probe) {
|
|
59
|
+
return probe.tier === MONITORING_TIER_1;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=tier-detection.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
+
import { type ReviewMonitorRecord } from "./record.js";
|
|
3
|
+
import { type MonitoringTierProbe } from "./tier-detection.js";
|
|
4
|
+
export type ReviewMonitorCallSite = "solo" | "swarm-phase5-6" | "swarm-phase6-cascade" | "unspecified";
|
|
5
|
+
export interface VerifyReviewMonitorArgs {
|
|
6
|
+
readonly pr: number;
|
|
7
|
+
readonly projectRoot: string;
|
|
8
|
+
readonly repo?: string | null;
|
|
9
|
+
readonly headSha?: string | null;
|
|
10
|
+
readonly callSite?: ReviewMonitorCallSite;
|
|
11
|
+
readonly approach3?: boolean;
|
|
12
|
+
readonly approach3Warned?: boolean;
|
|
13
|
+
readonly staleMinutes?: number;
|
|
14
|
+
readonly now?: Date;
|
|
15
|
+
readonly environ?: NodeJS.ProcessEnv;
|
|
16
|
+
}
|
|
17
|
+
export interface VerifyReviewMonitorResult {
|
|
18
|
+
readonly exitCode: typeof EXIT_READY | typeof EXIT_NOT_READY | typeof EXIT_CONFIG_ERROR;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
readonly tier: MonitoringTierProbe;
|
|
21
|
+
readonly monitorRecord: ReviewMonitorRecord | null;
|
|
22
|
+
readonly heartbeatActive: boolean;
|
|
23
|
+
readonly callSite: ReviewMonitorCallSite;
|
|
24
|
+
}
|
|
25
|
+
export declare function hasActivePollingHeartbeat(projectRoot: string, pr: number, options?: {
|
|
26
|
+
now?: Date;
|
|
27
|
+
staleMinutes?: number;
|
|
28
|
+
}): boolean;
|
|
29
|
+
export declare function evaluateReviewMonitorGate(args: VerifyReviewMonitorArgs): VerifyReviewMonitorResult;
|
|
30
|
+
export declare function verifyResultToJson(result: VerifyReviewMonitorResult): Record<string, unknown>;
|
|
31
|
+
export { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3 };
|
|
32
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { sweepScratchDirs } from "../orchestration/subagent-monitor.js";
|
|
4
|
+
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3, } from "./constants.js";
|
|
5
|
+
import { defaultSubagentStatusDir, findActiveMonitorForPr, readReviewMonitorFile, reviewMonitorPath, } from "./record.js";
|
|
6
|
+
import { isTier1, probeMonitoringTier } from "./tier-detection.js";
|
|
7
|
+
function spawnRedirect(probe) {
|
|
8
|
+
const primitive = probe.primitive ?? "sub-agent";
|
|
9
|
+
return (`Spawn an Approach 1 review-monitor via ${primitive} (background), include ` +
|
|
10
|
+
"`templates/agent-prompt-preamble.md` and `templates/swarm-greptile-poller-prompt.md`, " +
|
|
11
|
+
"then register:\n" +
|
|
12
|
+
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> " +
|
|
13
|
+
`--platform-primitive ${primitive}\n` +
|
|
14
|
+
"Re-run: task verify:review-monitor -- --pr <N>");
|
|
15
|
+
}
|
|
16
|
+
export function hasActivePollingHeartbeat(projectRoot, pr, options = {}) {
|
|
17
|
+
const dir = defaultSubagentStatusDir(projectRoot);
|
|
18
|
+
if (!existsSync(dir)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
if (!statSync(dir).isDirectory()) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const result = sweepScratchDirs([{ readPath: dir, label: dir }], {
|
|
30
|
+
thresholdMinutes: options.staleMinutes ?? 30,
|
|
31
|
+
now: options.now,
|
|
32
|
+
});
|
|
33
|
+
return result.records.some((rec) => rec.pr_number === pr &&
|
|
34
|
+
rec.failures.length === 0 &&
|
|
35
|
+
!rec.is_stale &&
|
|
36
|
+
!rec.is_terminal &&
|
|
37
|
+
(rec.phase === "polling" || rec.phase === "starting"));
|
|
38
|
+
}
|
|
39
|
+
export function evaluateReviewMonitorGate(args) {
|
|
40
|
+
const projectRoot = resolve(args.projectRoot);
|
|
41
|
+
let isDir = false;
|
|
42
|
+
try {
|
|
43
|
+
isDir = statSync(projectRoot).isDirectory();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
isDir = false;
|
|
47
|
+
}
|
|
48
|
+
if (!isDir) {
|
|
49
|
+
return {
|
|
50
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
51
|
+
message: `verify_review_monitor: --project-root is not a directory: ${projectRoot}`,
|
|
52
|
+
tier: probeMonitoringTier(args.environ),
|
|
53
|
+
monitorRecord: null,
|
|
54
|
+
heartbeatActive: false,
|
|
55
|
+
callSite: args.callSite ?? "unspecified",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const tier = probeMonitoringTier(args.environ);
|
|
59
|
+
const callSite = args.callSite ?? "unspecified";
|
|
60
|
+
const staleMinutes = args.staleMinutes ?? 30;
|
|
61
|
+
const now = args.now ?? new Date();
|
|
62
|
+
if (args.approach3 === true) {
|
|
63
|
+
if (isTier1(tier)) {
|
|
64
|
+
return {
|
|
65
|
+
exitCode: EXIT_NOT_READY,
|
|
66
|
+
message: "verify_review_monitor: Approach 3 blocking poll is forbidden when Tier 1 is available (#2655).\n" +
|
|
67
|
+
` Detected tier=${tier.tier} descriptor=${tier.descriptor ?? "unknown"} primitive=${tier.primitive ?? "none"}.\n` +
|
|
68
|
+
` ${spawnRedirect(tier)}`,
|
|
69
|
+
tier,
|
|
70
|
+
monitorRecord: null,
|
|
71
|
+
heartbeatActive: false,
|
|
72
|
+
callSite,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (tier.tier === MONITORING_TIER_3 && args.approach3Warned !== true) {
|
|
76
|
+
return {
|
|
77
|
+
exitCode: EXIT_NOT_READY,
|
|
78
|
+
message: "verify_review_monitor: Approach 3 requires explicit user warning acknowledgment (#2655).\n" +
|
|
79
|
+
" Warn the operator that the conversation pane will lock during polling, then re-run with --approach3-warned.",
|
|
80
|
+
tier,
|
|
81
|
+
monitorRecord: null,
|
|
82
|
+
heartbeatActive: false,
|
|
83
|
+
callSite,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
exitCode: EXIT_READY,
|
|
88
|
+
message: `verify_review_monitor: Tier 3 Approach 3 path allowed (call-site=${callSite}).`,
|
|
89
|
+
tier,
|
|
90
|
+
monitorRecord: null,
|
|
91
|
+
heartbeatActive: false,
|
|
92
|
+
callSite,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!isTier1(tier)) {
|
|
96
|
+
return {
|
|
97
|
+
exitCode: EXIT_READY,
|
|
98
|
+
message: `verify_review_monitor: Tier ${tier.tier} (${tier.descriptor ?? "unknown"}) — ` +
|
|
99
|
+
"no active review-monitor required (#2655).",
|
|
100
|
+
tier,
|
|
101
|
+
monitorRecord: null,
|
|
102
|
+
heartbeatActive: false,
|
|
103
|
+
callSite,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const path = reviewMonitorPath(projectRoot);
|
|
107
|
+
const { data, error } = readReviewMonitorFile(path);
|
|
108
|
+
if (data === null) {
|
|
109
|
+
return {
|
|
110
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
111
|
+
message: `verify_review_monitor: ${error ?? "could not read review-monitor state"}`,
|
|
112
|
+
tier,
|
|
113
|
+
monitorRecord: null,
|
|
114
|
+
heartbeatActive: false,
|
|
115
|
+
callSite,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const monitorRecord = findActiveMonitorForPr(data, args.pr, {
|
|
119
|
+
now,
|
|
120
|
+
staleMinutes,
|
|
121
|
+
headSha: args.headSha ?? null,
|
|
122
|
+
});
|
|
123
|
+
const heartbeatActive = hasActivePollingHeartbeat(projectRoot, args.pr, {
|
|
124
|
+
now,
|
|
125
|
+
staleMinutes,
|
|
126
|
+
});
|
|
127
|
+
if (monitorRecord !== null || heartbeatActive) {
|
|
128
|
+
const via = monitorRecord !== null ? "review-monitor record" : "subagent heartbeat";
|
|
129
|
+
return {
|
|
130
|
+
exitCode: EXIT_READY,
|
|
131
|
+
message: `verify_review_monitor: active review-monitor for PR #${args.pr} via ${via} ` +
|
|
132
|
+
`(call-site=${callSite}, tier=1, descriptor=${tier.descriptor ?? "unknown"}).`,
|
|
133
|
+
tier,
|
|
134
|
+
monitorRecord,
|
|
135
|
+
heartbeatActive,
|
|
136
|
+
callSite,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const siteHint = callSite === "swarm-phase5-6"
|
|
140
|
+
? "Swarm Phase 5→6 handoff (#1386)"
|
|
141
|
+
: callSite === "swarm-phase6-cascade"
|
|
142
|
+
? "Swarm Phase 6 post force-push (#380)"
|
|
143
|
+
: "Solo drive-to merge-ready / review-cycle ownership";
|
|
144
|
+
return {
|
|
145
|
+
exitCode: EXIT_NOT_READY,
|
|
146
|
+
message: `verify_review_monitor: Tier 1 available but no active review-monitor for PR #${args.pr} (#2655).\n` +
|
|
147
|
+
` Call site: ${siteHint}.\n` +
|
|
148
|
+
` Detected descriptor=${tier.descriptor ?? "unknown"} primitive=${tier.primitive ?? "none"}.\n` +
|
|
149
|
+
` ${spawnRedirect(tier)}`,
|
|
150
|
+
tier,
|
|
151
|
+
monitorRecord: null,
|
|
152
|
+
heartbeatActive: false,
|
|
153
|
+
callSite,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function verifyResultToJson(result) {
|
|
157
|
+
return {
|
|
158
|
+
call_site: result.callSite,
|
|
159
|
+
exit_code: result.exitCode,
|
|
160
|
+
heartbeat_active: result.heartbeatActive,
|
|
161
|
+
message: result.message,
|
|
162
|
+
monitor_agent_id: result.monitorRecord?.monitor_agent_id ?? null,
|
|
163
|
+
monitor_record: result.monitorRecord,
|
|
164
|
+
ready: result.exitCode === EXIT_READY,
|
|
165
|
+
tier: result.tier.tier,
|
|
166
|
+
tier_descriptor: result.tier.descriptor,
|
|
167
|
+
tier_primitive: result.tier.primitive,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3 };
|
|
171
|
+
//# sourceMappingURL=verify.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.3",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -242,6 +242,10 @@
|
|
|
242
242
|
"types": "./dist/swarm/index.d.ts",
|
|
243
243
|
"default": "./dist/swarm/index.js"
|
|
244
244
|
},
|
|
245
|
+
"./review-monitor": {
|
|
246
|
+
"types": "./dist/review-monitor/index.d.ts",
|
|
247
|
+
"default": "./dist/review-monitor/index.js"
|
|
248
|
+
},
|
|
245
249
|
"./platform": {
|
|
246
250
|
"types": "./dist/platform/index.d.ts",
|
|
247
251
|
"default": "./dist/platform/index.js"
|
|
@@ -305,8 +309,8 @@
|
|
|
305
309
|
"provenance": true
|
|
306
310
|
},
|
|
307
311
|
"dependencies": {
|
|
308
|
-
"@deftai/directive-content": "^0.79.
|
|
309
|
-
"@deftai/directive-types": "^0.79.
|
|
312
|
+
"@deftai/directive-content": "^0.79.3",
|
|
313
|
+
"@deftai/directive-types": "^0.79.3",
|
|
310
314
|
"archiver": "^8.0.0"
|
|
311
315
|
},
|
|
312
316
|
"scripts": {
|