@deftai/directive-core 0.92.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.
Files changed (111) hide show
  1. package/dist/cache/fetch.d.ts +29 -0
  2. package/dist/cache/fetch.js +131 -4
  3. package/dist/cache/operations.d.ts +8 -0
  4. package/dist/cache/operations.js +67 -4
  5. package/dist/check/cached-orchestrator.js +11 -0
  6. package/dist/check/consumer-gate-integrity.d.ts +68 -0
  7. package/dist/check/consumer-gate-integrity.js +265 -0
  8. package/dist/check/index.d.ts +1 -0
  9. package/dist/check/index.js +1 -0
  10. package/dist/check/orchestrator.js +10 -0
  11. package/dist/content-contracts/skills/greptile-detector.d.ts +9 -0
  12. package/dist/content-contracts/skills/greptile-detector.js +9 -2
  13. package/dist/doctor/index.d.ts +1 -0
  14. package/dist/doctor/index.js +1 -0
  15. package/dist/doctor/main.js +69 -0
  16. package/dist/doctor/openclaw-l2-adapter.d.ts +26 -0
  17. package/dist/doctor/openclaw-l2-adapter.js +199 -0
  18. package/dist/finish-loop/pr-finish-loop.js +1 -0
  19. package/dist/hooks/dispatcher.d.ts +24 -2
  20. package/dist/hooks/dispatcher.js +122 -11
  21. package/dist/hooks/readonly.d.ts +14 -0
  22. package/dist/hooks/readonly.js +126 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/init-deposit/hygiene.js +22 -0
  26. package/dist/init-deposit/index.d.ts +3 -0
  27. package/dist/init-deposit/index.js +3 -0
  28. package/dist/init-deposit/init-deposit.js +10 -0
  29. package/dist/init-deposit/refresh.js +11 -0
  30. package/dist/init-deposit/scaffold.js +3 -133
  31. package/dist/init-deposit/skill-discovery-deposit.d.ts +65 -0
  32. package/dist/init-deposit/skill-discovery-deposit.js +287 -0
  33. package/dist/init-deposit/skill-discovery-hosts.d.ts +94 -0
  34. package/dist/init-deposit/skill-discovery-hosts.js +217 -0
  35. package/dist/init-deposit/slash-deposit.d.ts +46 -0
  36. package/dist/init-deposit/slash-deposit.js +174 -0
  37. package/dist/intake/issue-ingest.d.ts +20 -0
  38. package/dist/intake/issue-ingest.js +58 -0
  39. package/dist/policy/host-slash-commands.d.ts +28 -0
  40. package/dist/policy/host-slash-commands.js +103 -0
  41. package/dist/policy/index.d.ts +2 -0
  42. package/dist/policy/index.js +61 -7
  43. package/dist/policy/min-greptile-confidence.d.ts +57 -0
  44. package/dist/policy/min-greptile-confidence.js +123 -0
  45. package/dist/pr-merge-readiness/compute.d.ts +11 -1
  46. package/dist/pr-merge-readiness/compute.js +17 -4
  47. package/dist/pr-merge-readiness/evaluate.d.ts +8 -1
  48. package/dist/pr-merge-readiness/evaluate.js +7 -4
  49. package/dist/pr-merge-readiness/mergeability.d.ts +7 -1
  50. package/dist/pr-merge-readiness/mergeability.js +14 -2
  51. package/dist/pr-monitor/main.d.ts +2 -0
  52. package/dist/pr-monitor/main.js +31 -1
  53. package/dist/pr-monitor/monitor.js +5 -1
  54. package/dist/pr-monitor/readiness.js +3 -1
  55. package/dist/pr-monitor/types.d.ts +8 -0
  56. package/dist/pr-wait-mergeable/cascade.d.ts +8 -0
  57. package/dist/pr-wait-mergeable/cascade.js +28 -1
  58. package/dist/pr-wait-mergeable/main.d.ts +2 -0
  59. package/dist/pr-wait-mergeable/main.js +16 -0
  60. package/dist/pr-wait-mergeable/types.d.ts +3 -1
  61. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
  62. package/dist/pr-wait-mergeable/wrappers.js +3 -0
  63. package/dist/pr-watch/constants.d.ts +4 -1
  64. package/dist/pr-watch/constants.js +5 -2
  65. package/dist/pr-watch/main.js +1 -0
  66. package/dist/pr-watch/probe.d.ts +1 -1
  67. package/dist/pr-watch/probe.js +4 -1
  68. package/dist/pr-watch/types.d.ts +7 -1
  69. package/dist/pr-watch/watch.js +3 -2
  70. package/dist/review-monitor/index.d.ts +1 -0
  71. package/dist/review-monitor/index.js +1 -0
  72. package/dist/review-monitor/l4-owner.d.ts +46 -0
  73. package/dist/review-monitor/l4-owner.js +262 -0
  74. package/dist/slash/emitters.d.ts +102 -0
  75. package/dist/slash/emitters.js +148 -0
  76. package/dist/slash/generator.d.ts +98 -0
  77. package/dist/slash/generator.js +145 -0
  78. package/dist/slash/index.d.ts +16 -0
  79. package/dist/slash/index.js +16 -0
  80. package/dist/slash/openclaw-adapter.d.ts +64 -0
  81. package/dist/slash/openclaw-adapter.js +198 -0
  82. package/dist/slash/openclaw-deposit.d.ts +73 -0
  83. package/dist/slash/openclaw-deposit.js +279 -0
  84. package/dist/slash/openclaw-slugs.d.ts +52 -0
  85. package/dist/slash/openclaw-slugs.js +126 -0
  86. package/dist/slash/product-set.d.ts +50 -0
  87. package/dist/slash/product-set.js +142 -0
  88. package/dist/tool-events/classify.js +1 -0
  89. package/dist/triage/welcome/writers.js +16 -7
  90. package/dist/umbrella-current-shape/index.d.ts +45 -0
  91. package/dist/umbrella-current-shape/index.js +162 -8
  92. package/dist/vbrief-reconcile/index.d.ts +2 -2
  93. package/dist/vbrief-reconcile/index.js +1 -1
  94. package/dist/vbrief-reconcile/types.d.ts +14 -0
  95. package/dist/vbrief-reconcile/umbrellas.d.ts +31 -2
  96. package/dist/vbrief-reconcile/umbrellas.js +231 -19
  97. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  98. package/dist/vbrief-validate/plan-hooks.js +50 -0
  99. package/dist/xbrief/create.d.ts +36 -0
  100. package/dist/xbrief/create.js +285 -0
  101. package/dist/xbrief/index.d.ts +14 -0
  102. package/dist/xbrief/index.js +42 -0
  103. package/dist/xbrief/paths.d.ts +37 -0
  104. package/dist/xbrief/paths.js +123 -0
  105. package/dist/xbrief/styles.d.ts +36 -0
  106. package/dist/xbrief/styles.js +235 -0
  107. package/dist/xbrief/types.d.ts +50 -0
  108. package/dist/xbrief/types.js +17 -0
  109. package/dist/xbrief/verify.d.ts +30 -0
  110. package/dist/xbrief/verify.js +251 -0
  111. package/package.json +11 -3
@@ -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 ?? ((n, r) => callReadiness(n, r, { runGh: options.runGh }));
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) => SubprocessTriple;
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
- /** CLEAN: SHA-matched, non-errored, no P0/P1, confidence > 3, CI green. */
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
- /** CLEAN: SHA-matched, non-errored, no P0/P1, confidence > 3, CI green. */
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 > 3, no P0/P1, CI green\n" +
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
  /**
@@ -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));
@@ -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
@@ -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,
@@ -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;
@@ -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 probeFn = options.probeFn ?? probeOnce;
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`);
@@ -1,5 +1,6 @@
1
1
  export * from "./constants.js";
2
2
  export * from "./github-lease.js";
3
+ export * from "./l4-owner.js";
3
4
  export * from "./lease-comment.js";
4
5
  export * from "./record.js";
5
6
  export * from "./tier-detection.js";
@@ -1,5 +1,6 @@
1
1
  export * from "./constants.js";
2
2
  export * from "./github-lease.js";
3
+ export * from "./l4-owner.js";
3
4
  export * from "./lease-comment.js";
4
5
  export * from "./record.js";
5
6
  export * from "./tier-detection.js";
@@ -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