opencode-workflow-guard 1.18.0 → 1.19.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/src/lib/verify.ts CHANGED
@@ -231,11 +231,26 @@ export function resolveVerifyTimeoutMs(root: string): number {
231
231
  return typeof configured === "number" && Number.isFinite(configured) && configured > 0 ? configured : 30_000;
232
232
  }
233
233
 
234
+ export interface VerifyRun {
235
+ passed: boolean;
236
+ output: string;
237
+ durationMs: number;
238
+ /**
239
+ * Set when the command was killed at the timeout rather than exiting on its
240
+ * own. A timeout is not a failing assertion: it says nothing about whether
241
+ * the change is correct, and the remedy is a longer limit, not a code fix.
242
+ * Callers that report a reason must attribute it, or the agent chases a
243
+ * failure that does not exist.
244
+ */
245
+ timedOut?: boolean;
246
+ timeoutMs?: number;
247
+ }
248
+
234
249
  export async function runVerify(
235
250
  command: string,
236
251
  root: string,
237
252
  timeoutMs = 30_000,
238
- ): Promise<{ passed: boolean; output: string; durationMs: number }> {
253
+ ): Promise<VerifyRun> {
239
254
  const start = Date.now();
240
255
  const allowLive = process.env.WORKFLOW_GUARD_ALLOW_LIVE === "1";
241
256
 
@@ -294,6 +309,8 @@ export async function runVerify(
294
309
  passed: false,
295
310
  output: output + `\n(Verification timed out after ${Math.round(timeoutMs / 1000)}s)`,
296
311
  durationMs: Date.now() - start,
312
+ timedOut: true,
313
+ timeoutMs,
297
314
  });
298
315
  });
299
316
  }, timeoutMs);
@@ -24,9 +24,11 @@ import { ToolInvocationLifecycle } from "./lib/tool-lifecycle.ts";
24
24
  import { ToolOutcomeTracker, type ToolOutcomePart } from "./lib/tool-outcomes.ts";
25
25
  import { guardToolCallImpl, isMutatingSurfaceTool, isReadOnlyRole, SHELL_TOOL_NAMES } from "./lib/guard-dispatcher.ts";
26
26
  import { createCustomTools, buildWorkflowGuardSystemGuidance } from "./lib/custom-tools.ts";
27
+ import { evaluateDogfoodGate } from "./lib/dogfood.ts";
27
28
  import type { PolicyDecision, TodoSdkClient } from "./lib/types.ts";
28
29
  export { isReadOnlyRole } from "./lib/guard-dispatcher.ts";
29
30
  export { extractReviewFollowups, buildWorkflowGuardSystemGuidance } from "./lib/custom-tools.ts";
31
+ export { evaluateDogfoodGate } from "./lib/dogfood.ts";
30
32
 
31
33
  // ── Types ────────────────────────────────────────────────────────────────────
32
34
  export type {
@@ -47,7 +49,7 @@ export type {
47
49
  LearningOpportunity,
48
50
  } from "./lib/types.ts";
49
51
  export type { TodoSdkClient };
50
- export { verificationEvidence, reviewEvidence, toolOutcomeEvidence, policyDecisionEvidence, lifecycleStateEvidence, agentAssertionEvidence, evidenceMatchesSubject, isEvidenceFresh } from "./lib/evidence.ts";
52
+ export { verificationEvidence, reviewEvidence, dogfoodEvidence, toolOutcomeEvidence, policyDecisionEvidence, lifecycleStateEvidence, agentAssertionEvidence, evidenceMatchesSubject, isEvidenceFresh } from "./lib/evidence.ts";
51
53
  export { getRalphOutcome } from "./policies/continuation.ts";
52
54
 
53
55
  // ── State & runtime context ──────────────────────────────────────────────────
@@ -76,6 +78,10 @@ import {
76
78
  isReviewRequired,
77
79
  isSubagentReviewRequired,
78
80
  isDocumentationRequired,
81
+ isDogfoodRequired,
82
+ getDogfoodDeployMatrix,
83
+ getDogfoodDepth,
84
+ isDogfoodActor,
79
85
  isLiveDocsRequired,
80
86
  getLiveDocFailureThreshold,
81
87
  getResearchToolPatterns,
@@ -88,6 +94,10 @@ import {
88
94
  getLastReviewResultForWorkspace,
89
95
  resetReviewState,
90
96
  sessionReviews,
97
+ recordDogfoodResult,
98
+ getLastDogfoodResultForWorkspace,
99
+ getDogfoodRecordsForWorkspace,
100
+ resetDogfoodState,
91
101
  setSessionWorkspace,
92
102
  getSessionWorkspace,
93
103
  getSessionBoundWorkspace,
@@ -117,6 +127,10 @@ export {
117
127
  isReviewRequired,
118
128
  isSubagentReviewRequired,
119
129
  isDocumentationRequired,
130
+ isDogfoodRequired,
131
+ getDogfoodDeployMatrix,
132
+ getDogfoodDepth,
133
+ isDogfoodActor,
120
134
  isLiveDocsRequired,
121
135
  getLiveDocFailureThreshold,
122
136
  getResearchToolPatterns,
@@ -127,6 +141,10 @@ export {
127
141
  getLastReviewResult,
128
142
  getLastReviewResultForWorkspace,
129
143
  resetReviewState,
144
+ recordDogfoodResult,
145
+ getLastDogfoodResultForWorkspace,
146
+ getDogfoodRecordsForWorkspace,
147
+ resetDogfoodState,
130
148
  setSessionWorkspace,
131
149
  getSessionWorkspace,
132
150
  getSessionBoundWorkspace,
@@ -194,7 +212,7 @@ import {
194
212
  summarizeInput,
195
213
  } from "./lib/audit.ts";
196
214
 
197
- export { getAuditFilePath, getVerifyCacheFilePath, getReviewCacheFilePath, getVerifyHistoryFilePath, persistVerifyCache, loadVerifyCache, persistReviewCache, loadReviewCache, getRecentAuditEntries, getRecentVerifyHistory } from "./lib/audit.ts";
215
+ export { getAuditFilePath, getVerifyCacheFilePath, getReviewCacheFilePath, getDogfoodCacheFilePath, getVerifyHistoryFilePath, persistVerifyCache, loadVerifyCache, persistReviewCache, loadReviewCache, persistDogfoodCache, loadDogfoodCache, getRecentAuditEntries, getRecentVerifyHistory } from "./lib/audit.ts";
198
216
  export { summarizeInput };
199
217
 
200
218
  export function managedConfigDiagnostic(platform = process.platform, env: NodeJS.ProcessEnv = process.env): string {
@@ -694,6 +712,14 @@ export const WorkflowGuard: V1Plugin = async (ctx: Parameters<V1Plugin>[0]) => {
694
712
  `- Secondary Review: ${lastR.passed ? "APPROVED" : "CHANGES REQUESTED"} by ${lastR.reviewer}`,
695
713
  );
696
714
  }
715
+ const dogfoodCtxGate = evaluateDogfoodGate(effectiveRoot);
716
+ if (dogfoodCtxGate.required) {
717
+ stateLines.push(
718
+ dogfoodCtxGate.missingHosts.length === 0
719
+ ? `- Dogfood Evidence: PASS on ${(dogfoodCtxGate.matchedRecords ?? []).map((record) => record.host.label).join(", ") || "recorded host"}`
720
+ : `- Dogfood Evidence: MISSING for ${dogfoodCtxGate.missingHosts.join(", ")} (required before PR creation)`,
721
+ );
722
+ }
697
723
  let openFollowups: ReturnType<typeof listReviewFollowups> = [];
698
724
  try { openFollowups = followupStore ? listReviewFollowups(followupStore, "open", 8) : []; } catch {}
699
725
  if (openFollowups.length > 0) {
@@ -838,6 +864,7 @@ export const WorkflowGuard: V1Plugin = async (ctx: Parameters<V1Plugin>[0]) => {
838
864
  projectMemoryEnabled,
839
865
  learningEnabled,
840
866
  recoveryCheckpointsEnabled: isRecoveryCheckpointsEnabled(effectiveRoot),
867
+ dogfoodRequired: isDogfoodRequired(effectiveRoot),
841
868
  isReadOnly,
842
869
  });
843
870
  output.system.push(guidance);