@deftai/directive-core 0.93.0 → 0.94.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 +29 -0
- package/dist/cache/fetch.js +131 -4
- package/dist/cache/operations.d.ts +8 -0
- package/dist/cache/operations.js +67 -4
- package/dist/check/cached-orchestrator.js +11 -0
- package/dist/check/consumer-gate-integrity.d.ts +68 -0
- package/dist/check/consumer-gate-integrity.js +265 -0
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/orchestrator.js +10 -0
- package/dist/content-contracts/skills/greptile-detector.d.ts +9 -0
- package/dist/content-contracts/skills/greptile-detector.js +9 -2
- package/dist/doctor/main.js +57 -0
- package/dist/finish-loop/pr-finish-loop.js +1 -0
- package/dist/hooks/dispatcher.d.ts +24 -2
- package/dist/hooks/dispatcher.js +122 -11
- package/dist/hooks/readonly.d.ts +14 -0
- package/dist/hooks/readonly.js +126 -0
- package/dist/intake/issue-ingest.d.ts +20 -0
- package/dist/intake/issue-ingest.js +58 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +15 -1
- package/dist/policy/min-greptile-confidence.d.ts +57 -0
- package/dist/policy/min-greptile-confidence.js +123 -0
- package/dist/pr-merge-readiness/compute.d.ts +11 -1
- package/dist/pr-merge-readiness/compute.js +17 -4
- package/dist/pr-merge-readiness/evaluate.d.ts +8 -1
- package/dist/pr-merge-readiness/evaluate.js +7 -4
- package/dist/pr-merge-readiness/mergeability.d.ts +7 -1
- package/dist/pr-merge-readiness/mergeability.js +14 -2
- package/dist/pr-monitor/main.d.ts +2 -0
- package/dist/pr-monitor/main.js +31 -1
- package/dist/pr-monitor/monitor.js +5 -1
- package/dist/pr-monitor/readiness.js +3 -1
- package/dist/pr-monitor/types.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.js +28 -1
- package/dist/pr-wait-mergeable/main.d.ts +2 -0
- package/dist/pr-wait-mergeable/main.js +16 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +3 -0
- package/dist/pr-watch/constants.d.ts +4 -1
- package/dist/pr-watch/constants.js +5 -2
- package/dist/pr-watch/main.js +1 -0
- package/dist/pr-watch/probe.d.ts +1 -1
- package/dist/pr-watch/probe.js +4 -1
- package/dist/pr-watch/types.d.ts +7 -1
- package/dist/pr-watch/watch.js +3 -2
- package/dist/review-monitor/index.d.ts +1 -0
- package/dist/review-monitor/index.js +1 -0
- package/dist/review-monitor/l4-owner.d.ts +46 -0
- package/dist/review-monitor/l4-owner.js +262 -0
- package/dist/tool-events/classify.js +1 -0
- package/dist/triage/welcome/writers.js +16 -7
- package/dist/umbrella-current-shape/index.d.ts +45 -0
- package/dist/umbrella-current-shape/index.js +162 -8
- package/dist/vbrief-reconcile/index.d.ts +2 -2
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +31 -2
- package/dist/vbrief-reconcile/umbrellas.js +231 -19
- package/package.json +3 -3
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { DEFAULT_CONSUMER_MIN_GREPTILE_CONFIDENCE, formatMinConfidenceRequirement, meetsMinGreptileConfidence, } from "../policy/min-greptile-confidence.js";
|
|
1
2
|
import { INFORMAL_CLEAN_DIAGNOSTIC } from "./constants.js";
|
|
2
3
|
/** Return failure messages (empty list == merge-ready). */
|
|
3
|
-
export function evaluateGates(_prNumber, headSha, verdict, inline = null) {
|
|
4
|
+
export function evaluateGates(_prNumber, headSha, verdict, inline = null, options = {}) {
|
|
4
5
|
const failures = [];
|
|
6
|
+
const minConfidence = options.minConfidence ?? DEFAULT_CONSUMER_MIN_GREPTILE_CONFIDENCE;
|
|
5
7
|
if (!verdict.found) {
|
|
6
8
|
failures.push("No Greptile rolling-summary comment found on the PR. " +
|
|
7
9
|
"Either Greptile has not posted yet, or the bot login filter is wrong. " +
|
|
@@ -32,9 +34,10 @@ export function evaluateGates(_prNumber, headSha, verdict, inline = null) {
|
|
|
32
34
|
"Confidence is a required exit-condition input per " +
|
|
33
35
|
"skills/deft-directive-review-cycle/SKILL.md Phase 2 Step 6.");
|
|
34
36
|
}
|
|
35
|
-
else if (verdict.confidence
|
|
36
|
-
failures.push(`Greptile confidence is ${verdict.confidence}/5; exit condition requires
|
|
37
|
-
"Address remaining findings or push clarifying changes."
|
|
37
|
+
else if (!meetsMinGreptileConfidence(verdict.confidence, minConfidence)) {
|
|
38
|
+
failures.push(`Greptile confidence is ${verdict.confidence}/5; exit condition requires ${formatMinConfidenceRequirement(minConfidence)}. ` +
|
|
39
|
+
"Address remaining findings or push clarifying changes. " +
|
|
40
|
+
"Inspect the floor via `task policy:show --field=minGreptileConfidence` (#3095).");
|
|
38
41
|
}
|
|
39
42
|
if (verdict.p0Count > 0 || verdict.p1Count > 0) {
|
|
40
43
|
failures.push(`Greptile reports ${verdict.p0Count} P0 and ${verdict.p1Count} P1 findings ` +
|
|
@@ -42,5 +42,11 @@ export declare function verdictShaIsStale(verdict: GreptileVerdict, headSha: str
|
|
|
42
42
|
* regardless of GitHub mergeability (guardrail: do not merge a PR with a real
|
|
43
43
|
* P0/P1 review finding).
|
|
44
44
|
*/
|
|
45
|
-
export declare function verdictBlockIsSoftOnly(verdict: GreptileVerdict, headSha: string | null, inline?: InlineGreptileFindings | null
|
|
45
|
+
export declare function verdictBlockIsSoftOnly(verdict: GreptileVerdict, headSha: string | null, inline?: InlineGreptileFindings | null,
|
|
46
|
+
/**
|
|
47
|
+
* Resolved min Greptile confidence (1–5). Scores below this are HARD blockers
|
|
48
|
+
* and must never be reconciled away via GitHub CLEAN (#3095). Defaults to the
|
|
49
|
+
* consumer floor (4).
|
|
50
|
+
*/
|
|
51
|
+
minConfidence?: number): boolean;
|
|
46
52
|
//# sourceMappingURL=mergeability.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { meetsMinGreptileConfidence } from "../policy/min-greptile-confidence.js";
|
|
1
2
|
/** REST `mergeable_state` value that mirrors GraphQL `mergeStateStatus: CLEAN`. */
|
|
2
3
|
export const MERGE_STATE_CLEAN = "clean";
|
|
3
4
|
/**
|
|
@@ -75,7 +76,13 @@ export function verdictShaIsStale(verdict, headSha) {
|
|
|
75
76
|
* regardless of GitHub mergeability (guardrail: do not merge a PR with a real
|
|
76
77
|
* P0/P1 review finding).
|
|
77
78
|
*/
|
|
78
|
-
export function verdictBlockIsSoftOnly(verdict, headSha, inline = null
|
|
79
|
+
export function verdictBlockIsSoftOnly(verdict, headSha, inline = null,
|
|
80
|
+
/**
|
|
81
|
+
* Resolved min Greptile confidence (1–5). Scores below this are HARD blockers
|
|
82
|
+
* and must never be reconciled away via GitHub CLEAN (#3095). Defaults to the
|
|
83
|
+
* consumer floor (4).
|
|
84
|
+
*/
|
|
85
|
+
minConfidence = 4) {
|
|
79
86
|
if (inline !== null && inline.error !== null) {
|
|
80
87
|
return false;
|
|
81
88
|
}
|
|
@@ -99,7 +106,12 @@ export function verdictBlockIsSoftOnly(verdict, headSha, inline = null) {
|
|
|
99
106
|
if (verdict.errored) {
|
|
100
107
|
return false;
|
|
101
108
|
}
|
|
102
|
-
|
|
109
|
+
// Confidence below the resolved floor is a hard blocker (#2260 / #3095).
|
|
110
|
+
// Dogfood/policy may set min=5 so a 4/5 score stays hard even when GitHub
|
|
111
|
+
// reports CLEAN + MERGEABLE. Use the shared predicate so the export is on
|
|
112
|
+
// the production call path (SLizard #3095).
|
|
113
|
+
if (verdict.confidence !== null &&
|
|
114
|
+
!meetsMinGreptileConfidence(verdict.confidence, minConfidence)) {
|
|
103
115
|
return false;
|
|
104
116
|
}
|
|
105
117
|
if (verdict.p0Count > 0 || verdict.p1Count > 0) {
|
|
@@ -5,6 +5,8 @@ export interface ParsedMonitorArgs {
|
|
|
5
5
|
readonly repo: string | null;
|
|
6
6
|
readonly capMinutes: number;
|
|
7
7
|
readonly emitJson: boolean;
|
|
8
|
+
/** Project root for minGreptileConfidence (#3095 / #3102). Null = default cwd at run. */
|
|
9
|
+
readonly projectRoot: string | null;
|
|
8
10
|
readonly error?: string;
|
|
9
11
|
}
|
|
10
12
|
export declare function parseMonitorArgs(argv: readonly string[]): ParsedMonitorArgs;
|
package/dist/pr-monitor/main.js
CHANGED
|
@@ -13,6 +13,7 @@ export function parseMonitorArgs(argv) {
|
|
|
13
13
|
let repo = null;
|
|
14
14
|
let capMinutes = 60;
|
|
15
15
|
let emitJson = false;
|
|
16
|
+
let projectRoot = null;
|
|
16
17
|
for (let i = 0; i < argv.length; i += 1) {
|
|
17
18
|
const arg = argv[i];
|
|
18
19
|
if (arg === "--json") {
|
|
@@ -26,6 +27,7 @@ export function parseMonitorArgs(argv) {
|
|
|
26
27
|
repo,
|
|
27
28
|
capMinutes,
|
|
28
29
|
emitJson,
|
|
30
|
+
projectRoot,
|
|
29
31
|
error: "argument --repo: expected one argument",
|
|
30
32
|
};
|
|
31
33
|
}
|
|
@@ -35,6 +37,24 @@ export function parseMonitorArgs(argv) {
|
|
|
35
37
|
else if (arg?.startsWith("--repo=")) {
|
|
36
38
|
repo = arg.slice("--repo=".length);
|
|
37
39
|
}
|
|
40
|
+
else if (arg === "--project-root") {
|
|
41
|
+
const value = argv[i + 1];
|
|
42
|
+
if (value === undefined) {
|
|
43
|
+
return {
|
|
44
|
+
prNumber,
|
|
45
|
+
repo,
|
|
46
|
+
capMinutes,
|
|
47
|
+
emitJson,
|
|
48
|
+
projectRoot,
|
|
49
|
+
error: "argument --project-root: expected one argument",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
projectRoot = value;
|
|
53
|
+
i += 1;
|
|
54
|
+
}
|
|
55
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
56
|
+
projectRoot = arg.slice("--project-root=".length);
|
|
57
|
+
}
|
|
38
58
|
else if (arg === "--cap-minutes") {
|
|
39
59
|
const value = argv[i + 1];
|
|
40
60
|
if (value === undefined) {
|
|
@@ -43,6 +63,7 @@ export function parseMonitorArgs(argv) {
|
|
|
43
63
|
repo,
|
|
44
64
|
capMinutes,
|
|
45
65
|
emitJson,
|
|
66
|
+
projectRoot,
|
|
46
67
|
error: "argument --cap-minutes: expected one argument",
|
|
47
68
|
};
|
|
48
69
|
}
|
|
@@ -53,6 +74,7 @@ export function parseMonitorArgs(argv) {
|
|
|
53
74
|
repo,
|
|
54
75
|
capMinutes,
|
|
55
76
|
emitJson,
|
|
77
|
+
projectRoot,
|
|
56
78
|
error: `invalid --cap-minutes value: ${value}`,
|
|
57
79
|
};
|
|
58
80
|
}
|
|
@@ -68,6 +90,7 @@ export function parseMonitorArgs(argv) {
|
|
|
68
90
|
repo,
|
|
69
91
|
capMinutes,
|
|
70
92
|
emitJson,
|
|
93
|
+
projectRoot,
|
|
71
94
|
error: `invalid --cap-minutes value: ${value}`,
|
|
72
95
|
};
|
|
73
96
|
}
|
|
@@ -79,6 +102,7 @@ export function parseMonitorArgs(argv) {
|
|
|
79
102
|
repo,
|
|
80
103
|
capMinutes,
|
|
81
104
|
emitJson,
|
|
105
|
+
projectRoot,
|
|
82
106
|
error: `unrecognized arguments: ${arg}`,
|
|
83
107
|
};
|
|
84
108
|
}
|
|
@@ -90,6 +114,7 @@ export function parseMonitorArgs(argv) {
|
|
|
90
114
|
repo,
|
|
91
115
|
capMinutes,
|
|
92
116
|
emitJson,
|
|
117
|
+
projectRoot,
|
|
93
118
|
error: `invalid PR number: ${arg}`,
|
|
94
119
|
};
|
|
95
120
|
}
|
|
@@ -101,6 +126,7 @@ export function parseMonitorArgs(argv) {
|
|
|
101
126
|
repo,
|
|
102
127
|
capMinutes,
|
|
103
128
|
emitJson,
|
|
129
|
+
projectRoot,
|
|
104
130
|
error: `unrecognized arguments: ${arg}`,
|
|
105
131
|
};
|
|
106
132
|
}
|
|
@@ -111,10 +137,11 @@ export function parseMonitorArgs(argv) {
|
|
|
111
137
|
repo,
|
|
112
138
|
capMinutes,
|
|
113
139
|
emitJson,
|
|
140
|
+
projectRoot,
|
|
114
141
|
error: "the following arguments are required: pr_number",
|
|
115
142
|
};
|
|
116
143
|
}
|
|
117
|
-
return { prNumber, repo, capMinutes, emitJson };
|
|
144
|
+
return { prNumber, repo, capMinutes, emitJson, projectRoot };
|
|
118
145
|
}
|
|
119
146
|
export function runMonitor(argv, options = {}) {
|
|
120
147
|
const args = parseMonitorArgs(argv);
|
|
@@ -131,6 +158,9 @@ export function runMonitor(argv, options = {}) {
|
|
|
131
158
|
const { exitCode, payload, pollCount } = monitorFn(args.prNumber, repo, {
|
|
132
159
|
capMinutes: args.capMinutes,
|
|
133
160
|
runGh: options.runGh,
|
|
161
|
+
// CLI --project-root or cwd: production remote-monitor path must not silently
|
|
162
|
+
// resolve minGreptileConfidence from an unrelated cwd (#3095 residual / #3102).
|
|
163
|
+
projectRoot: args.projectRoot ?? process.cwd(),
|
|
134
164
|
});
|
|
135
165
|
const summaryLabel = summaryLabelForExit(exitCode);
|
|
136
166
|
const via = typeof payload.via === "string" ? payload.via : "?";
|
|
@@ -125,7 +125,11 @@ export function monitor(prNumber, repo, options = {}) {
|
|
|
125
125
|
const capSeconds = (options.capMinutes ?? 60) * 60;
|
|
126
126
|
const clockFn = options.clockFn ?? systemMonotonicClock;
|
|
127
127
|
const sleepFn = options.sleepFn ?? defaultSleep;
|
|
128
|
-
const callReadinessFn = options.callReadinessFn ??
|
|
128
|
+
const callReadinessFn = options.callReadinessFn ??
|
|
129
|
+
((n, r) => callReadiness(n, r, {
|
|
130
|
+
runGh: options.runGh,
|
|
131
|
+
projectRoot: options.projectRoot ?? process.cwd(),
|
|
132
|
+
}));
|
|
129
133
|
const startedAt = clockFn.now();
|
|
130
134
|
const intervalSchedule = cadenceIntervals(cadence);
|
|
131
135
|
const minCadenceSec = intervalSchedule.length > 0
|
|
@@ -27,7 +27,9 @@ export function callReadiness(prNumber, repo, options = {}) {
|
|
|
27
27
|
try {
|
|
28
28
|
process.stderr.write = captureWrite;
|
|
29
29
|
const runGh = options.runGh ?? defaultRunGh;
|
|
30
|
-
const result = computeGateResult(prNumber, repo, runGh
|
|
30
|
+
const result = computeGateResult(prNumber, repo, runGh, {
|
|
31
|
+
projectRoot: options.projectRoot ?? process.cwd(),
|
|
32
|
+
});
|
|
31
33
|
const payload = gateResultToDict(result);
|
|
32
34
|
const exitCode = exitCodeFor(result);
|
|
33
35
|
return {
|
|
@@ -17,6 +17,12 @@ export interface MonitorOptions {
|
|
|
17
17
|
readonly clockFn?: MonotonicClock;
|
|
18
18
|
readonly callReadinessFn?: CallReadinessFn;
|
|
19
19
|
readonly runGh?: RunGhFn;
|
|
20
|
+
/**
|
|
21
|
+
* Project root for plan.policy.review.minGreptileConfidence (#3095).
|
|
22
|
+
* When pr-monitor targets a remote repo from a different cwd, pass the
|
|
23
|
+
* monitored project's root so confidence policy is not resolved from cwd.
|
|
24
|
+
*/
|
|
25
|
+
readonly projectRoot?: string | null;
|
|
20
26
|
}
|
|
21
27
|
export interface MonitorRunResult {
|
|
22
28
|
readonly exitCode: number;
|
|
@@ -26,5 +32,7 @@ export interface MonitorRunResult {
|
|
|
26
32
|
export interface CallReadinessOptions {
|
|
27
33
|
readonly runGh?: RunGhFn;
|
|
28
34
|
readonly timeoutMs?: number;
|
|
35
|
+
/** Project root for minGreptileConfidence resolve (#3095). */
|
|
36
|
+
readonly projectRoot?: string | null;
|
|
29
37
|
}
|
|
30
38
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type AgentMergeEvaluateResult } from "../policy/require-human-merge.js";
|
|
2
2
|
import { type SemanticGreenFn } from "./semantic-green.js";
|
|
3
3
|
import type { MergeFn, MonitorFn, ProtectedCheckFn, WaitMergeableResult } from "./types.js";
|
|
4
|
+
/** Post-merge umbrella checklist + current-shape refresh (#1649). */
|
|
5
|
+
export type UmbrellaReconcileFn = (projectRoot: string, repo: string) => void;
|
|
4
6
|
export interface WaitMergeableOptions {
|
|
5
7
|
readonly protectedFn?: ProtectedCheckFn;
|
|
6
8
|
readonly monitorFn?: MonitorFn;
|
|
@@ -21,6 +23,12 @@ export interface WaitMergeableOptions {
|
|
|
21
23
|
readonly agentMergeFn?: (projectRoot: string) => AgentMergeEvaluateResult;
|
|
22
24
|
/** When true, skip human-merge / intent preflight (tests only). */
|
|
23
25
|
readonly skipHumanMergeGate?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Post-merge umbrella reconcile (#1649). Defaults to live reconcileUmbrellas.
|
|
28
|
+
* Pass `null` to skip (unit tests that inject mergeFn must pass null so they
|
|
29
|
+
* never mutate real GitHub current-shape comments from the worktree cwd).
|
|
30
|
+
*/
|
|
31
|
+
readonly umbrellaReconcileFn?: UmbrellaReconcileFn | null;
|
|
24
32
|
}
|
|
25
33
|
/** Run protected-check -> wait -> merge cascade (#1369). */
|
|
26
34
|
export declare function waitMergeableAndMerge(prNumber: number, repo: string, options: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
|
|
2
2
|
import { evaluateAgentMerge, } from "../policy/require-human-merge.js";
|
|
3
|
+
import { reconcileUmbrellas, renderUmbrellasReport } from "../vbrief-reconcile/umbrellas.js";
|
|
3
4
|
import { classifyMonitorOutcome, parseMonitorPayload } from "./classify.js";
|
|
4
5
|
import { EXIT_CONFIG_ERROR, EXIT_MERGED, EXIT_TIMEOUT_OR_ESCALATION } from "./constants.js";
|
|
5
6
|
import { makeResult } from "./result.js";
|
|
@@ -12,6 +13,12 @@ function isProtectedCheckConfigFailure(stderr) {
|
|
|
12
13
|
tail.includes("Cannot find module") ||
|
|
13
14
|
tail.includes("protected-check script not found:"));
|
|
14
15
|
}
|
|
16
|
+
function defaultPostMergeUmbrellaReconcile(projectRoot, repo) {
|
|
17
|
+
const [, umOutcome] = reconcileUmbrellas(projectRoot, { repo });
|
|
18
|
+
if (umOutcome.changed.length > 0 || umOutcome.errors.length > 0) {
|
|
19
|
+
process.stderr.write(`${renderUmbrellasReport(umOutcome)}\n`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
15
22
|
/** Run protected-check -> wait -> merge cascade (#1369). */
|
|
16
23
|
export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
17
24
|
const protectedFn = options.protectedFn ?? runProtectedCheck;
|
|
@@ -93,7 +100,9 @@ export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
|
93
100
|
error: semanticGreen.error,
|
|
94
101
|
});
|
|
95
102
|
}
|
|
96
|
-
const [monRc, monStdout, monStderr] = monitorFn(prNumber, repo, options.capMinutes
|
|
103
|
+
const [monRc, monStdout, monStderr] = monitorFn(prNumber, repo, options.capMinutes, {
|
|
104
|
+
projectRoot,
|
|
105
|
+
});
|
|
97
106
|
const monitorPayload = parseMonitorPayload(monStdout);
|
|
98
107
|
const [outcome, monitorExit] = classifyMonitorOutcome(monRc, monitorPayload);
|
|
99
108
|
if (outcome !== "clean") {
|
|
@@ -114,6 +123,24 @@ export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
|
114
123
|
}
|
|
115
124
|
const [mergeRc, mergeStdout, mergeStderr] = mergeFn(prNumber, repo);
|
|
116
125
|
if (mergeRc === 0) {
|
|
126
|
+
// Best-effort umbrella checklist + current-shape refresh after child merge (#1649).
|
|
127
|
+
// Failures must not flip a successful merge into an error exit.
|
|
128
|
+
// null = skip; undefined = live default (skipped under VITEST unless injected).
|
|
129
|
+
const umbrellaFn = options.umbrellaReconcileFn === null
|
|
130
|
+
? null
|
|
131
|
+
: options.umbrellaReconcileFn !== undefined
|
|
132
|
+
? options.umbrellaReconcileFn
|
|
133
|
+
: process.env.VITEST === "true"
|
|
134
|
+
? null
|
|
135
|
+
: defaultPostMergeUmbrellaReconcile;
|
|
136
|
+
if (umbrellaFn !== null) {
|
|
137
|
+
try {
|
|
138
|
+
umbrellaFn(projectRoot, repo);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
/* best-effort; merge remains authoritative */
|
|
142
|
+
}
|
|
143
|
+
}
|
|
117
144
|
return makeResult({
|
|
118
145
|
prNumber,
|
|
119
146
|
repo,
|
|
@@ -8,6 +8,8 @@ export interface ParsedWaitMergeableArgs {
|
|
|
8
8
|
readonly cascadeMode: boolean;
|
|
9
9
|
readonly requireMasterCiGreen: boolean;
|
|
10
10
|
readonly baseBranch: string | null;
|
|
11
|
+
/** Project root for minGreptileConfidence (#3095 / #3102). Null = cwd at run. */
|
|
12
|
+
readonly projectRoot: string | null;
|
|
11
13
|
readonly error?: string;
|
|
12
14
|
}
|
|
13
15
|
export declare function parseWaitMergeableArgs(argv: readonly string[]): ParsedWaitMergeableArgs;
|
|
@@ -19,6 +19,7 @@ export function parseWaitMergeableArgs(argv) {
|
|
|
19
19
|
let cascadeMode = false;
|
|
20
20
|
let requireMasterCiGreen = false;
|
|
21
21
|
let baseBranch = null;
|
|
22
|
+
let projectRoot = null;
|
|
22
23
|
const baseReturn = () => ({
|
|
23
24
|
prNumber,
|
|
24
25
|
repo,
|
|
@@ -28,6 +29,7 @@ export function parseWaitMergeableArgs(argv) {
|
|
|
28
29
|
cascadeMode,
|
|
29
30
|
requireMasterCiGreen,
|
|
30
31
|
baseBranch,
|
|
32
|
+
projectRoot,
|
|
31
33
|
});
|
|
32
34
|
for (let i = 0; i < argv.length; i += 1) {
|
|
33
35
|
const arg = argv[i];
|
|
@@ -51,6 +53,17 @@ export function parseWaitMergeableArgs(argv) {
|
|
|
51
53
|
else if (arg?.startsWith("--base-branch=")) {
|
|
52
54
|
baseBranch = arg.slice("--base-branch=".length);
|
|
53
55
|
}
|
|
56
|
+
else if (arg === "--project-root") {
|
|
57
|
+
const value = argv[i + 1];
|
|
58
|
+
if (value === undefined) {
|
|
59
|
+
return { ...baseReturn(), error: "argument --project-root: expected one argument" };
|
|
60
|
+
}
|
|
61
|
+
projectRoot = value;
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
65
|
+
projectRoot = arg.slice("--project-root=".length);
|
|
66
|
+
}
|
|
54
67
|
else if (arg === "--repo") {
|
|
55
68
|
const value = argv[i + 1];
|
|
56
69
|
if (value === undefined) {
|
|
@@ -154,6 +167,9 @@ export function runWaitMergeable(argv, options = {}) {
|
|
|
154
167
|
cascadeMode: args.cascadeMode,
|
|
155
168
|
requireMasterCiGreen: args.requireMasterCiGreen,
|
|
156
169
|
baseBranch: args.baseBranch,
|
|
170
|
+
// Explicit CLI root (or cwd) so remote-target cascade does not resolve
|
|
171
|
+
// minGreptileConfidence from an unrelated directory (#3102).
|
|
172
|
+
projectRoot: args.projectRoot ?? process.cwd(),
|
|
157
173
|
});
|
|
158
174
|
const summaryLabel = summaryLabelForExit(result.exitCode);
|
|
159
175
|
process.stderr.write(`[pr_wait_mergeable] PR #${result.prNumber} repo=${result.repo} ` +
|
|
@@ -12,6 +12,8 @@ export interface WaitMergeableResult {
|
|
|
12
12
|
}
|
|
13
13
|
export type SubprocessTriple = readonly [number, string, string];
|
|
14
14
|
export type ProtectedCheckFn = (prNumber: number, repo: string | null, protectedIssues: readonly number[]) => SubprocessTriple;
|
|
15
|
-
export type MonitorFn = (prNumber: number, repo: string, capMinutes: number
|
|
15
|
+
export type MonitorFn = (prNumber: number, repo: string, capMinutes: number, options?: {
|
|
16
|
+
readonly projectRoot?: string;
|
|
17
|
+
}) => SubprocessTriple;
|
|
16
18
|
export type MergeFn = (prNumber: number, repo: string | null) => SubprocessTriple;
|
|
17
19
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -35,6 +35,12 @@ export declare function runProtectedCheck(prNumber: number, repo: string | null,
|
|
|
35
35
|
export interface RunMonitorOptions {
|
|
36
36
|
readonly nodeExecutable?: string;
|
|
37
37
|
readonly timeout?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Project root for plan.policy.review.minGreptileConfidence when the
|
|
40
|
+
* cascade cwd is not the monitored project (#3095 residual / #3102).
|
|
41
|
+
* Defaults to process.cwd() when omitted.
|
|
42
|
+
*/
|
|
43
|
+
readonly projectRoot?: string;
|
|
38
44
|
}
|
|
39
45
|
/** Invoke pr-monitor CLI with --json and return (returncode, stdout, stderr). */
|
|
40
46
|
export declare function runMonitor(prNumber: number, repo: string, capMinutes: number, options?: RunMonitorOptions): SubprocessTriple;
|
|
@@ -105,6 +105,7 @@ export function runMonitor(prNumber, repo, capMinutes, options = {}) {
|
|
|
105
105
|
}
|
|
106
106
|
const node = options.nodeExecutable ?? process.execPath;
|
|
107
107
|
const timeoutSec = options.timeout ?? capMinutes * 60 + 60;
|
|
108
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
108
109
|
const cmd = [
|
|
109
110
|
scriptPath,
|
|
110
111
|
String(prNumber),
|
|
@@ -112,6 +113,8 @@ export function runMonitor(prNumber, repo, capMinutes, options = {}) {
|
|
|
112
113
|
repo,
|
|
113
114
|
"--cap-minutes",
|
|
114
115
|
String(capMinutes),
|
|
116
|
+
"--project-root",
|
|
117
|
+
projectRoot,
|
|
115
118
|
"--json",
|
|
116
119
|
];
|
|
117
120
|
// Stream the monitor's per-poll heartbeat live (a buffered poll looks like a
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* cannot promise to poll and then silently forget (2026-05-11 three-strikes on
|
|
7
7
|
* #1051 / #1054).
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* CLEAN: SHA-matched, non-errored, no P0/P1, confidence >= resolved min, CI green.
|
|
11
|
+
* Min defaults to consumer 4 (legacy > 3); dogfood/policy may raise to 5 (#3095).
|
|
12
|
+
*/
|
|
10
13
|
export declare const EXIT_CLEAN = 0;
|
|
11
14
|
/** NEW_P0_P1: blocking findings on the CURRENT (SHA-matched) review. */
|
|
12
15
|
export declare const EXIT_NEW_P0_P1 = 1;
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* cannot promise to poll and then silently forget (2026-05-11 three-strikes on
|
|
7
7
|
* #1051 / #1054).
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* CLEAN: SHA-matched, non-errored, no P0/P1, confidence >= resolved min, CI green.
|
|
11
|
+
* Min defaults to consumer 4 (legacy > 3); dogfood/policy may raise to 5 (#3095).
|
|
12
|
+
*/
|
|
10
13
|
export const EXIT_CLEAN = 0;
|
|
11
14
|
/** NEW_P0_P1: blocking findings on the CURRENT (SHA-matched) review. */
|
|
12
15
|
export const EXIT_NEW_P0_P1 = 1;
|
|
@@ -57,7 +60,7 @@ export const WATCH_HELP = "usage: task pr:watch -- <pr_number> [options]\n" +
|
|
|
57
60
|
" --project-root PATH Chdir before probing (optional)\n" +
|
|
58
61
|
"\n" +
|
|
59
62
|
"exit codes:\n" +
|
|
60
|
-
" 0 CLEAN SHA-matched review, confidence
|
|
63
|
+
" 0 CLEAN SHA-matched review, confidence >= policy min (default 4; dogfood 5), no P0/P1, CI green\n" +
|
|
61
64
|
" 1 NEW_P0_P1 Blocking findings on the current (SHA-matched) review\n" +
|
|
62
65
|
" 2 ERRORED | STALL | TIMEOUT | CI_BLOCKED | RUNNER_CAPACITY_STALL | config / usage error\n";
|
|
63
66
|
/**
|
package/dist/pr-watch/main.js
CHANGED
|
@@ -223,6 +223,7 @@ export function runWatch(argv, options = {}) {
|
|
|
223
223
|
clockFn: options.clockFn,
|
|
224
224
|
probeFn: options.probeFn,
|
|
225
225
|
stallThreshold: options.stallThreshold,
|
|
226
|
+
projectRoot: args.projectRoot ?? process.cwd(),
|
|
226
227
|
});
|
|
227
228
|
if (args.emitJson) {
|
|
228
229
|
process.stdout.write(emitWatchJson(result));
|
package/dist/pr-watch/probe.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ import type { WatchProbe } from "./types.js";
|
|
|
9
9
|
* detector (#1056 AC-2). All gh access routes through the injectable RunGhFn
|
|
10
10
|
* seam, which defaults to the UTF-8-safe execFile capture (#1366).
|
|
11
11
|
*/
|
|
12
|
-
export declare function probeOnce(prNumber: number, repoArg: string | null, runGh?: RunGhFn): WatchProbe;
|
|
12
|
+
export declare function probeOnce(prNumber: number, repoArg: string | null, runGh?: RunGhFn, projectRoot?: string | null): WatchProbe;
|
|
13
13
|
//# sourceMappingURL=probe.d.ts.map
|
package/dist/pr-watch/probe.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { detect, evaluateCleanGate, parseConfidence, parseLastReviewedShaMarkdownLink, parseLastReviewedShaNaiveInline, } from "../content-contracts/skills/greptile-detector.js";
|
|
2
|
+
import { resolveMinGreptileConfidence } from "../policy/min-greptile-confidence.js";
|
|
2
3
|
import { evaluateCiGate } from "../pr-merge-readiness/ci-gate.js";
|
|
3
4
|
import { GREPTILE_ERRORED_SENTINEL } from "../pr-merge-readiness/constants.js";
|
|
4
5
|
import { defaultRunGh, fetchCheckRunsRest, fetchGreptileBodyRest, fetchGreptileCommentBody, fetchPrHeadSha, fetchPrHeadShaRest, resolveRepo, } from "../pr-merge-readiness/gh.js";
|
|
@@ -32,9 +33,10 @@ function errorProbe(headSha, message) {
|
|
|
32
33
|
* detector (#1056 AC-2). All gh access routes through the injectable RunGhFn
|
|
33
34
|
* seam, which defaults to the UTF-8-safe execFile capture (#1366).
|
|
34
35
|
*/
|
|
35
|
-
export function probeOnce(prNumber, repoArg, runGh = defaultRunGh) {
|
|
36
|
+
export function probeOnce(prNumber, repoArg, runGh = defaultRunGh, projectRoot = null) {
|
|
36
37
|
const resolved = resolveRepo(repoArg, runGh);
|
|
37
38
|
const repo = resolved.repo;
|
|
39
|
+
const minConfidence = resolveMinGreptileConfidence(projectRoot ?? process.cwd()).min;
|
|
38
40
|
// 1. HEAD SHA -- primary `gh pr view`, then REST fallback when a repo resolved.
|
|
39
41
|
let headSha = fetchPrHeadSha(prNumber, repo, runGh);
|
|
40
42
|
if (headSha === null && repo !== null) {
|
|
@@ -88,6 +90,7 @@ export function probeOnce(prNumber, repoArg, runGh = defaultRunGh) {
|
|
|
88
90
|
ciFailures,
|
|
89
91
|
errored,
|
|
90
92
|
terminalCheckRun,
|
|
93
|
+
minConfidence,
|
|
91
94
|
});
|
|
92
95
|
return {
|
|
93
96
|
found,
|
package/dist/pr-watch/types.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type SleepFn = (seconds: number) => void;
|
|
|
34
34
|
export interface MonotonicClock {
|
|
35
35
|
now(): number;
|
|
36
36
|
}
|
|
37
|
-
export type ProbeFn = (prNumber: number, repo: string | null, runGh: RunGhFn) => WatchProbe;
|
|
37
|
+
export type ProbeFn = (prNumber: number, repo: string | null, runGh: RunGhFn, projectRoot?: string | null) => WatchProbe;
|
|
38
38
|
export interface WatchOptions {
|
|
39
39
|
readonly maxWaitMinutes?: number;
|
|
40
40
|
readonly pollSeconds?: number;
|
|
@@ -44,6 +44,12 @@ export interface WatchOptions {
|
|
|
44
44
|
readonly sleepFn?: SleepFn;
|
|
45
45
|
readonly clockFn?: MonotonicClock;
|
|
46
46
|
readonly probeFn?: ProbeFn;
|
|
47
|
+
/**
|
|
48
|
+
* Project root for resolving plan.policy.review.minGreptileConfidence (#3095).
|
|
49
|
+
* When omitted, probeOnce uses process.cwd(). Finish-loop / pr:watch MUST pass
|
|
50
|
+
* the caller project-root so a chdir elsewhere cannot apply the wrong floor.
|
|
51
|
+
*/
|
|
52
|
+
readonly projectRoot?: string | null;
|
|
47
53
|
}
|
|
48
54
|
export interface WatchResult {
|
|
49
55
|
readonly verdict: string;
|
package/dist/pr-watch/watch.js
CHANGED
|
@@ -44,7 +44,8 @@ export function watch(prNumber, repo, options = {}) {
|
|
|
44
44
|
const runGh = options.runGh ?? defaultRunGh;
|
|
45
45
|
const clockFn = options.clockFn ?? systemMonotonicClock;
|
|
46
46
|
const sleepFn = options.sleepFn ?? defaultSleep;
|
|
47
|
-
const
|
|
47
|
+
const projectRoot = options.projectRoot ?? null;
|
|
48
|
+
const probeFn = options.probeFn ?? ((n, r, gh, root) => probeOnce(n, r, gh, root ?? projectRoot));
|
|
48
49
|
const capSeconds = Math.max(0, maxWaitMinutes * 60);
|
|
49
50
|
// Bounded loop (time is still the authority via the cap check below) so no
|
|
50
51
|
// unbounded `while (true)`; +1 covers the trailing poll after the final wait.
|
|
@@ -62,7 +63,7 @@ export function watch(prNumber, repo, options = {}) {
|
|
|
62
63
|
pollCount: poll,
|
|
63
64
|
});
|
|
64
65
|
for (let poll = 1; poll <= maxPolls; poll += 1) {
|
|
65
|
-
const probe = probeFn(prNumber, repo, runGh);
|
|
66
|
+
const probe = probeFn(prNumber, repo, runGh, projectRoot);
|
|
66
67
|
lastProbe = probe;
|
|
67
68
|
const elapsed = Math.round(clockFn.now() - startedAt);
|
|
68
69
|
process.stderr.write(`${formatWatchStatus(poll, maxPolls, probe, elapsed)}\n`);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner Continuity / L4 owner gate (#3090).
|
|
3
|
+
*
|
|
4
|
+
* Exit 0 only when a fresh sticky review-owner lease exists on the PR,
|
|
5
|
+
* or the caller asserts `review_cycle: done` after Step 6.
|
|
6
|
+
* Freeform started/pending/initiated values are rejected.
|
|
7
|
+
* skipped / n/a / parent-retained are process evidence only — they do NOT
|
|
8
|
+
* satisfy this machine gate when --pr is required (lease-or-done).
|
|
9
|
+
*/
|
|
10
|
+
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY } from "./constants.js";
|
|
11
|
+
import type { ReviewOwnerGithubSeams } from "./github-lease.js";
|
|
12
|
+
import { type ReviewMonitorRecord } from "./record.js";
|
|
13
|
+
/** Allowed review_cycle evidence values (prefix form for in_progress/skipped). */
|
|
14
|
+
export type ReviewCycleEvidence = "done" | "n/a" | `in_progress:${string}` | `skipped:${string}`;
|
|
15
|
+
export declare const L4_OWNER_HELP: string;
|
|
16
|
+
export declare function parseReviewCycleEvidence(raw: string | null | undefined): {
|
|
17
|
+
ok: true;
|
|
18
|
+
value: ReviewCycleEvidence | null;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
/** Parse in_progress:<pr>#<ref> into parts; null if not that form. */
|
|
24
|
+
export declare function parseInProgressEvidence(value: string): {
|
|
25
|
+
pr: number;
|
|
26
|
+
ref: string;
|
|
27
|
+
} | null;
|
|
28
|
+
export interface VerifyL4OwnerArgs {
|
|
29
|
+
readonly pr: number;
|
|
30
|
+
readonly projectRoot: string;
|
|
31
|
+
readonly repo?: string | null;
|
|
32
|
+
readonly headSha?: string | null;
|
|
33
|
+
readonly reviewCycle?: string | null;
|
|
34
|
+
readonly now?: Date;
|
|
35
|
+
readonly seams?: ReviewOwnerGithubSeams;
|
|
36
|
+
}
|
|
37
|
+
export interface VerifyL4OwnerResult {
|
|
38
|
+
readonly exitCode: typeof EXIT_READY | typeof EXIT_NOT_READY | typeof EXIT_CONFIG_ERROR;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly monitorRecord: ReviewMonitorRecord | null;
|
|
41
|
+
readonly reviewCycle: ReviewCycleEvidence | null;
|
|
42
|
+
readonly path: "lease" | "done" | "none" | "illegal" | "config";
|
|
43
|
+
}
|
|
44
|
+
export declare function evaluateL4OwnerGate(args: VerifyL4OwnerArgs): VerifyL4OwnerResult;
|
|
45
|
+
export declare function l4OwnerResultToJson(result: VerifyL4OwnerResult): Record<string, unknown>;
|
|
46
|
+
//# sourceMappingURL=l4-owner.d.ts.map
|