wogiflow 2.10.0 → 2.11.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "taskId": null,
3
+ "uniqueFiles": [],
4
+ "thresholdReached": false,
5
+ "scopeInventory": null,
6
+ "warnedAt": null
7
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "deploys": []
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "routes": [],
3
+ "lastUpdated": null
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "tasks": {}
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -187,12 +187,61 @@ const CONFIG_DEFAULTS = {
187
187
  implementationGate: { enabled: true },
188
188
  todoWriteGate: { enabled: true, blockImplementationWithoutTask: true },
189
189
  routingGate: { enabled: true },
190
+ commitLogGate: { enabled: true },
190
191
  loopEnforcement: { enabled: true },
191
192
  hypothesisGate: {
192
193
  enabled: true,
193
194
  _comment_hypothesisGate: 'Blocks premature "fixed"/"should work" claims during bug investigation until hypothesis is verified. Pattern: hypothesis → verify → confirm → communicate.',
194
195
  blockedPhrases: ['fixed', 'should work', 'go try', 'go refresh'],
195
196
  requireExplicitVerification: true
197
+ },
198
+ deployGate: {
199
+ _comment_deployGate: 'Blocks deploy commands unless a valid HMAC-signed verification artifact exists. Off by default — opt in via `flow deploy-gate init`.',
200
+ enabled: false,
201
+ commands: [],
202
+ sourcePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue', '**/*.svelte', '**/*.css'],
203
+ requireForPriorities: ['P0', 'P1'],
204
+ blockWriteToVerifications: true,
205
+ minVerifiedRoutes: 3,
206
+ rejectLoginOnly: true
207
+ },
208
+ strikeEscalation: {
209
+ _comment_strikeEscalation: 'Mechanical strike counter. Blocks Edit/Write after repeated verification failures on the same task.',
210
+ enabled: true,
211
+ blockThreshold: 2,
212
+ escalateThreshold: 3,
213
+ hardBlockThreshold: 4,
214
+ productionCrashThreshold: 2
215
+ },
216
+ bugfixScope: {
217
+ _comment_bugfixScope: 'Pauses L3 bugfixes after N unique non-test files edited, requiring a scope inventory.',
218
+ enabled: true,
219
+ mode: 'warn',
220
+ fileThreshold: 3,
221
+ excludePatterns: ['*.test.*', '*.spec.*', '*.d.ts', '__tests__/**', '__mocks__/**'],
222
+ keywordMatchThreshold: 2,
223
+ fanOutThreshold: 10
224
+ },
225
+ revertFirst: {
226
+ _comment_revertFirst: 'For production crashes: recommends reverting before forward-fixing. Off by default.',
227
+ enabled: false,
228
+ keywords: ['production', 'crash', 'down', 'outage', '500 errors', 'users can\u0027t', 'site is broken', 'live issue'],
229
+ deployHistoryRetention: 50,
230
+ oldDeployWarningDays: 7
231
+ },
232
+ scopeMutation: {
233
+ _comment_scopeMutation: 'Agnostic gate: fix tasks creating 2+ new files → warn. Deleting pre-existing files → warn. No framework pattern matching.',
234
+ enabled: true,
235
+ newFileThreshold: 2,
236
+ mode: 'warn'
237
+ },
238
+ gitSafety: {
239
+ _comment_gitSafety: 'Auto-backup before destructive git operations. Prevents accidental loss of work from prior sessions.',
240
+ enabled: true,
241
+ maxBackwardCommits: 3,
242
+ ageThresholdHours: 24,
243
+ autoBackup: true,
244
+ maxBackupBranches: 3
196
245
  }
197
246
  },
198
247
 
@@ -342,6 +391,13 @@ const CONFIG_DEFAULTS = {
342
391
  similarityThreshold: 0.8,
343
392
  similarityWarningThreshold: 0.6
344
393
  },
394
+ // --- Workspace Sovereignty ---
395
+ workspace: {
396
+ _comment_workerGatesSovereign: 'When true, workers cannot skip their own quality gates even if the manager instructs them to',
397
+ workerGatesSovereign: true,
398
+ managerCanOverrideLevel: false,
399
+ managerCanSkipGates: false
400
+ },
345
401
  checkpoint: { enabled: false },
346
402
  regressionTesting: { enabled: false },
347
403
 
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Deploy Gate CLI
5
+ *
6
+ * Commands:
7
+ * flow deploy-gate init — Interactive setup wizard
8
+ * flow deploy-gate status — Show gate status and artifact info
9
+ * flow deploy-gate verify — Check if a valid artifact exists
10
+ * flow deploy-gate routes — Show route inventory
11
+ * flow deploy-gate add-route <path> — Manually add a route
12
+ * flow deploy-gate history — Show deploy history
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const fs = require('node:fs');
18
+ const path = require('node:path');
19
+ const { PATHS, getConfig, safeJsonParse, writeJson } = require('./flow-utils');
20
+ const {
21
+ isDeployGateEnabled,
22
+ getDeployGateConfig,
23
+ findLatestArtifact,
24
+ getRouteInventory,
25
+ addRoute,
26
+ getLastGoodDeploy,
27
+ DEPLOY_ROUTES_PATH,
28
+ DEPLOY_HISTORY_PATH
29
+ } = require('./hooks/core/deploy-gate');
30
+
31
+ // ============================================================
32
+ // CLI Commands
33
+ // ============================================================
34
+
35
+ function cmdInit() {
36
+ console.log('━━━ Deploy Gate Setup Wizard ━━━\n');
37
+
38
+ // 1. Detect package.json scripts
39
+ const pkgPath = path.join(PATHS.root, 'package.json');
40
+ const pkg = safeJsonParse(pkgPath, {});
41
+ const scripts = pkg.scripts || {};
42
+ const deployScripts = [];
43
+
44
+ for (const [name, cmd] of Object.entries(scripts)) {
45
+ if (/deploy|publish|release|sync|push/i.test(name) || /deploy|s3.*sync|vercel|netlify|fly.*deploy/i.test(cmd)) {
46
+ deployScripts.push({ name, cmd });
47
+ }
48
+ }
49
+
50
+ if (deployScripts.length > 0) {
51
+ console.log('Detected deploy-related scripts:');
52
+ for (const s of deployScripts) {
53
+ console.log(` - npm run ${s.name}: ${s.cmd}`);
54
+ }
55
+ } else {
56
+ console.log('No deploy scripts detected in package.json.');
57
+ }
58
+
59
+ // 2. Suggest common deploy command patterns
60
+ const suggestedCommands = [];
61
+ for (const s of deployScripts) {
62
+ suggestedCommands.push(`npm run ${s.name}`);
63
+ // Also extract the raw command for direct matching
64
+ if (s.cmd) suggestedCommands.push(s.cmd.split('&&')[0].trim());
65
+ }
66
+
67
+ // Add common defaults if none detected
68
+ if (suggestedCommands.length === 0) {
69
+ suggestedCommands.push(
70
+ 'aws s3 sync',
71
+ 'vercel deploy',
72
+ 'netlify deploy',
73
+ 'fly deploy',
74
+ 'docker push'
75
+ );
76
+ console.log('\nSuggested common deploy patterns (add the ones your project uses):');
77
+ for (const cmd of suggestedCommands) {
78
+ console.log(` - ${cmd}`);
79
+ }
80
+ }
81
+
82
+ // 3. Detect framework
83
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
84
+ let framework = 'unknown';
85
+ if (deps['next']) framework = 'nextjs';
86
+ else if (deps['vite'] || deps['@vitejs/plugin-react']) framework = 'vite';
87
+ else if (deps['@angular/core']) framework = 'angular';
88
+ else if (deps['vue']) framework = 'vue';
89
+ else if (deps['svelte']) framework = 'svelte';
90
+ else if (deps['express'] || deps['fastify'] || deps['@nestjs/core']) framework = 'backend';
91
+
92
+ console.log(`\nDetected framework: ${framework}`);
93
+
94
+ // 4. Suggest verification method
95
+ let verificationMethod = 'checklist';
96
+ if (deps['playwright'] || deps['@playwright/test']) verificationMethod = 'playwright';
97
+ if (framework === 'backend') verificationMethod = 'api-test';
98
+
99
+ console.log(`Suggested verification method: ${verificationMethod}`);
100
+
101
+ // 5. Generate initial route inventory from common patterns
102
+ console.log('\nScanning for routes...');
103
+ const routes = scanForRoutes(framework);
104
+ if (routes.length > 0) {
105
+ console.log(`Found ${routes.length} route(s):`);
106
+ for (const r of routes) {
107
+ console.log(` - ${r}`);
108
+ addRoute(r, 'init-wizard');
109
+ }
110
+ } else {
111
+ console.log('No routes auto-detected. Add routes manually with: flow deploy-gate add-route <path>');
112
+ }
113
+
114
+ // 6. Write suggested config
115
+ console.log('\n━━━ Suggested Configuration ━━━');
116
+ console.log('Add to .workflow/config.json under "enforcement":\n');
117
+ const suggestedConfig = {
118
+ deployGate: {
119
+ enabled: true,
120
+ commands: suggestedCommands.slice(0, 5),
121
+ sourcePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue', '**/*.svelte', '**/*.css'],
122
+ requireForPriorities: ['P0', 'P1'],
123
+ blockWriteToVerifications: true
124
+ }
125
+ };
126
+ console.log(JSON.stringify(suggestedConfig, null, 2));
127
+
128
+ console.log('\n━━━ Setup Complete ━━━');
129
+ console.log('Next steps:');
130
+ console.log(' 1. Add the config above to .workflow/config.json');
131
+ console.log(' 2. Customize the commands list for your deploy workflow');
132
+ console.log(' 3. Run `flow deploy-gate status` to verify setup');
133
+ }
134
+
135
+ function cmdStatus() {
136
+ const config = getConfig();
137
+ const enabled = isDeployGateEnabled(config);
138
+ const gateConfig = getDeployGateConfig(config);
139
+
140
+ console.log('━━━ Deploy Gate Status ━━━\n');
141
+ console.log(`Enabled: ${enabled ? '✓ YES' : '✗ NO'}`);
142
+
143
+ if (!enabled) {
144
+ console.log('\nRun `flow deploy-gate init` to set up the deploy gate.');
145
+ return;
146
+ }
147
+
148
+ console.log(`Commands blocked: ${gateConfig.commands.length > 0 ? gateConfig.commands.join(', ') : '(none configured)'}`);
149
+ console.log(`Require verification for: ${gateConfig.requireForPriorities.join(', ')}`);
150
+ console.log(`Block Write to artifacts: ${gateConfig.blockWriteToVerifications}`);
151
+
152
+ // Check for valid artifact
153
+ const artifactResult = findLatestArtifact();
154
+ console.log(`\nLatest artifact: ${artifactResult.found ? '✓ VALID' : '✗ ' + (artifactResult.reason || 'NOT FOUND')}`);
155
+ if (artifactResult.found) {
156
+ const a = artifactResult.artifact;
157
+ console.log(` Method: ${a.method}`);
158
+ console.log(` Task: ${a.taskId}`);
159
+ console.log(` Created: ${a.createdAt}`);
160
+ console.log(` Routes verified: ${(a.routes || []).length}`);
161
+ console.log(` Evidence tier: ${a.evidenceTier}`);
162
+ }
163
+
164
+ // Route inventory
165
+ const inventory = getRouteInventory();
166
+ console.log(`\nRoute inventory: ${inventory.routes.length} route(s)`);
167
+
168
+ // Deploy history
169
+ const lastDeploy = getLastGoodDeploy();
170
+ if (lastDeploy.found) {
171
+ console.log(`Last deploy: ${lastDeploy.deploy.commitHash.slice(0, 8)} (${lastDeploy.deploy.timestamp})`);
172
+ } else {
173
+ console.log('Last deploy: (no history)');
174
+ }
175
+ }
176
+
177
+ function cmdVerify() {
178
+ const artifactResult = findLatestArtifact();
179
+ if (artifactResult.found) {
180
+ console.log('✓ Valid verification artifact found');
181
+ console.log(JSON.stringify(artifactResult.artifact, null, 2));
182
+ process.exit(0);
183
+ } else {
184
+ console.log(`✗ No valid artifact: ${artifactResult.reason}`);
185
+ process.exit(1);
186
+ }
187
+ }
188
+
189
+ function cmdRoutes() {
190
+ const inventory = getRouteInventory();
191
+ console.log('━━━ Route Inventory ━━━\n');
192
+ if (inventory.routes.length === 0) {
193
+ console.log('No routes registered. Add with: flow deploy-gate add-route <path>');
194
+ return;
195
+ }
196
+ for (const r of inventory.routes) {
197
+ console.log(` ${r.path} — added ${r.addedAt} (${r.source})`);
198
+ }
199
+ console.log(`\nTotal: ${inventory.routes.length} route(s)`);
200
+ }
201
+
202
+ function cmdAddRoute(routePath) {
203
+ if (!routePath) {
204
+ console.error('Usage: flow deploy-gate add-route <path>');
205
+ process.exit(1);
206
+ }
207
+ const added = addRoute(routePath, 'manual');
208
+ if (added) {
209
+ console.log(`✓ Added route: ${routePath}`);
210
+ } else {
211
+ console.log(`Route already exists: ${routePath}`);
212
+ }
213
+ }
214
+
215
+ function cmdHistory() {
216
+ const history = safeJsonParse(DEPLOY_HISTORY_PATH, { deploys: [] });
217
+ console.log('━━━ Deploy History ━━━\n');
218
+ if (history.deploys.length === 0) {
219
+ console.log('No deploy history recorded.');
220
+ return;
221
+ }
222
+ for (const d of history.deploys.slice(0, 10)) {
223
+ console.log(` ${d.commitHash.slice(0, 8)} | ${d.timestamp} | ${d.environment}`);
224
+ }
225
+ console.log(`\nShowing ${Math.min(10, history.deploys.length)} of ${history.deploys.length} deploys`);
226
+ }
227
+
228
+ // ============================================================
229
+ // Route Scanning Helpers
230
+ // ============================================================
231
+
232
+ function scanForRoutes(framework) {
233
+ const routes = [];
234
+ const { execSync } = require('node:child_process');
235
+
236
+ try {
237
+ // Next.js: pages or app directory
238
+ if (framework === 'nextjs') {
239
+ const patterns = ['app/**/page.tsx', 'app/**/page.jsx', 'pages/**/*.tsx', 'pages/**/*.jsx', 'src/app/**/page.tsx'];
240
+ for (const pattern of patterns) {
241
+ try {
242
+ const files = execSync(`git ls-files '${pattern}'`, { encoding: 'utf-8', cwd: PATHS.root }).trim();
243
+ if (files) {
244
+ for (const f of files.split('\n')) {
245
+ const route = '/' + f.replace(/^(src\/)?(app|pages)\//, '').replace(/(page|index)\.(tsx|jsx)$/, '').replace(/\/$/, '') || '/';
246
+ if (!routes.includes(route)) routes.push(route);
247
+ }
248
+ }
249
+ } catch (_err) {
250
+ // Pattern didn't match
251
+ }
252
+ }
253
+ }
254
+
255
+ // React Router / Vite: look for route definitions
256
+ if (framework === 'vite' || framework === 'unknown') {
257
+ try {
258
+ const routeFiles = execSync("git ls-files | grep -iE '(router|routes|App)\\.(tsx|jsx|ts|js)$'", { encoding: 'utf-8', cwd: PATHS.root }).trim();
259
+ if (routeFiles) {
260
+ for (const f of routeFiles.split('\n')) {
261
+ try {
262
+ const content = fs.readFileSync(path.join(PATHS.root, f), 'utf-8');
263
+ const pathMatches = content.matchAll(/path:\s*['"]([^'"]+)['"]/g);
264
+ for (const m of pathMatches) {
265
+ if (!routes.includes(m[1])) routes.push(m[1]);
266
+ }
267
+ } catch (_err) {
268
+ // Skip unreadable files
269
+ }
270
+ }
271
+ }
272
+ } catch (_err) {
273
+ // No route files found
274
+ }
275
+ }
276
+
277
+ // Express/backend: look for route handlers
278
+ if (framework === 'backend') {
279
+ try {
280
+ const apiFiles = execSync("git ls-files | grep -iE '(routes|controller|api).*\\.(ts|js)$'", { encoding: 'utf-8', cwd: PATHS.root }).trim();
281
+ if (apiFiles) {
282
+ for (const f of apiFiles.split('\n').slice(0, 20)) {
283
+ try {
284
+ const content = fs.readFileSync(path.join(PATHS.root, f), 'utf-8');
285
+ const pathMatches = content.matchAll(/\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi);
286
+ for (const m of pathMatches) {
287
+ const route = `${m[1].toUpperCase()} ${m[2]}`;
288
+ if (!routes.includes(route)) routes.push(route);
289
+ }
290
+ } catch (_err) {
291
+ // Skip unreadable files
292
+ }
293
+ }
294
+ }
295
+ } catch (_err) {
296
+ // No API files found
297
+ }
298
+ }
299
+ } catch (_err) {
300
+ // Scanning failed — non-critical
301
+ }
302
+
303
+ return routes;
304
+ }
305
+
306
+ // ============================================================
307
+ // CLI Entrypoint
308
+ // ============================================================
309
+
310
+ const args = process.argv.slice(2);
311
+ const command = args[0];
312
+
313
+ switch (command) {
314
+ case 'init':
315
+ cmdInit();
316
+ break;
317
+ case 'status':
318
+ cmdStatus();
319
+ break;
320
+ case 'verify':
321
+ cmdVerify();
322
+ break;
323
+ case 'routes':
324
+ cmdRoutes();
325
+ break;
326
+ case 'add-route':
327
+ cmdAddRoute(args[1]);
328
+ break;
329
+ case 'history':
330
+ cmdHistory();
331
+ break;
332
+ default:
333
+ console.log('Usage: flow deploy-gate <init|status|verify|routes|add-route|history>');
334
+ process.exit(1);
335
+ }