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/README.md +4 -4
- package/docs/installation.md +23 -0
- package/docs/policies.md +21 -0
- package/docs/testing.md +2 -1
- package/package.json +2 -2
- package/src/lib/audit.ts +52 -2
- package/src/lib/custom-tools.ts +154 -3
- package/src/lib/dogfood.ts +147 -0
- package/src/lib/evidence.ts +19 -1
- package/src/lib/guard-dispatcher.ts +54 -3
- package/src/lib/state.ts +221 -1
- package/src/lib/types.ts +64 -1
- package/src/lib/v2-plugin.ts +12 -1
- package/src/lib/verify.ts +18 -1
- package/src/workflow-guard.ts +29 -2
- package/test/test.mts +686 -0
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<
|
|
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);
|
package/src/workflow-guard.ts
CHANGED
|
@@ -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);
|