wogiflow 2.26.2 → 2.29.1

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 (169) hide show
  1. package/.claude/commands/wogi-bug.md +30 -0
  2. package/.claude/commands/wogi-debug-hypothesis.md +33 -0
  3. package/.claude/commands/wogi-morning.md +1 -2
  4. package/.claude/commands/wogi-review.md +31 -2
  5. package/.claude/commands/wogi-start.md +32 -0
  6. package/.claude/commands/wogi-statusline-setup.md +12 -0
  7. package/.claude/commands/wogi-story.md +3 -2
  8. package/.claude/docs/claude-code-compatibility.md +40 -0
  9. package/.claude/docs/phases/01-explore.md +2 -1
  10. package/.claude/docs/phases/03-implement.md +4 -0
  11. package/.claude/docs/phases/04-verify.md +45 -0
  12. package/.claude/rules/README.md +36 -0
  13. package/.claude/rules/_internal/worker-tool-first-turn.md +82 -0
  14. package/.claude/rules/alternative-execpolicy-toml-command-policy.md +11 -0
  15. package/.claude/rules/alternative-hand-edit-ready-json-to-register-orpha.md +11 -0
  16. package/.claude/rules/alternative-permission-ruleset-per-phase.md +11 -0
  17. package/.claude/rules/alternative-short-name.md +12 -0
  18. package/.claude/rules/alternative-wogi-flow-as-mcp-client-oauth-manager.md +11 -0
  19. package/.claude/rules/architecture/hook-three-layer.md +68 -0
  20. package/.claude/rules/dual-repo-architecture-2026-02-28.md +18 -0
  21. package/.claude/rules/github-release-workflow-2026-01-30.md +16 -0
  22. package/.claude/settings.json +1 -1
  23. package/.workflow/agents/logic-adversary.md +2 -1
  24. package/.workflow/agents/personas/README.md +48 -0
  25. package/.workflow/agents/personas/platform-rigor.md +38 -0
  26. package/.workflow/agents/personas/scale-skeptic.md +28 -0
  27. package/.workflow/agents/personas/security-hawk.md +34 -0
  28. package/.workflow/agents/personas/simplicity-champion.md +37 -0
  29. package/.workflow/agents/personas/user-advocate.md +36 -0
  30. package/.workflow/bridges/base-bridge.js +46 -23
  31. package/.workflow/templates/claude-md.hbs +44 -122
  32. package/.workflow/templates/partials/feature-dossiers.hbs +33 -0
  33. package/.workflow/templates/partials/intent-grounded-reasoning.hbs +2 -12
  34. package/.workflow/templates/partials/methodology-rules.hbs +85 -79
  35. package/.workflow/templates/tier3-dom-field-inventory.md +102 -0
  36. package/lib/fuzzy-patch.js +251 -0
  37. package/lib/installer.js +8 -0
  38. package/lib/memory-proposal-store.js +458 -0
  39. package/lib/mode-schema.js +255 -0
  40. package/lib/skill-proposal-store.js +432 -0
  41. package/lib/skill-registry.js +1 -1
  42. package/lib/wogi-claude +149 -9
  43. package/lib/wogi-claude-expect.exp +113 -76
  44. package/lib/workspace-channel-server.js +19 -0
  45. package/lib/workspace-contracts.js +1 -1
  46. package/lib/workspace-dispatch-tracking.js +144 -0
  47. package/lib/workspace-gates.js +1 -1
  48. package/lib/workspace-ipc-sqlite.js +550 -0
  49. package/lib/workspace-messages.js +92 -0
  50. package/lib/workspace-routing.js +1 -1
  51. package/lib/workspace-task-injector.js +223 -0
  52. package/lib/workspace.js +23 -0
  53. package/lib/worktree-review.js +315 -0
  54. package/package.json +2 -2
  55. package/scripts/base-workflow-step.js +1 -1
  56. package/scripts/flow +28 -4
  57. package/scripts/flow-ac-scope-preservation.js +238 -0
  58. package/scripts/flow-auto-review-worker.js +75 -0
  59. package/scripts/flow-auto-review.js +102 -0
  60. package/scripts/flow-autonomous-detector.js +118 -0
  61. package/scripts/flow-autonomous-mode.js +153 -0
  62. package/scripts/flow-best-of-n.js +1 -1
  63. package/scripts/flow-bulk-loop.js +1 -1
  64. package/scripts/flow-checkpoint.js +2 -6
  65. package/scripts/flow-community-sync.js +1 -1
  66. package/scripts/flow-completion-summary.js +176 -0
  67. package/scripts/flow-completion-truth-gate.js +343 -4
  68. package/scripts/flow-config-defaults.js +52 -5
  69. package/scripts/flow-config-loader.js +3 -2
  70. package/scripts/flow-context-compact/expander.js +1 -1
  71. package/scripts/flow-context-compact/section-extractor.js +2 -2
  72. package/scripts/flow-context-gatherer.js +1 -1
  73. package/scripts/flow-context-generator.js +1 -1
  74. package/scripts/flow-context-scoring.js +1 -1
  75. package/scripts/flow-correct.js +1 -1
  76. package/scripts/flow-correction-detector.js +3 -2
  77. package/scripts/flow-decision-authority.js +66 -15
  78. package/scripts/flow-done.js +33 -1
  79. package/scripts/flow-epic-cascade.js +171 -0
  80. package/scripts/flow-epics.js +2 -7
  81. package/scripts/flow-eval-judge.js +1 -1
  82. package/scripts/flow-eval.js +1 -1
  83. package/scripts/flow-export-scanner.js +2 -6
  84. package/scripts/flow-failure-learning.js +1 -1
  85. package/scripts/flow-feature-dossier.js +787 -0
  86. package/scripts/flow-figma-extract.js +2 -2
  87. package/scripts/flow-figma-generate.js +1 -1
  88. package/scripts/flow-gate-confidence.js +1 -1
  89. package/scripts/flow-health.js +52 -1
  90. package/scripts/flow-hooks.js +1 -1
  91. package/scripts/flow-id.js +19 -3
  92. package/scripts/flow-instruction-richness.js +1 -1
  93. package/scripts/flow-knowledge-router.js +1 -1
  94. package/scripts/flow-knowledge-sync.js +1 -1
  95. package/scripts/flow-logic-adversary.js +76 -1
  96. package/scripts/flow-logic-rules.js +380 -0
  97. package/scripts/flow-long-input.js +5 -5
  98. package/scripts/flow-memory-sync.js +1 -1
  99. package/scripts/flow-memory.js +78 -7
  100. package/scripts/flow-migrate.js +1 -1
  101. package/scripts/flow-model-caller.js +1 -1
  102. package/scripts/flow-models.js +2 -2
  103. package/scripts/flow-morning.js +0 -17
  104. package/scripts/flow-multi-approach.js +1 -1
  105. package/scripts/flow-orchestrate-context.js +4 -4
  106. package/scripts/flow-orchestrate-templates.js +1 -1
  107. package/scripts/flow-orchestrate.js +8 -8
  108. package/scripts/flow-peer-review.js +1 -1
  109. package/scripts/flow-phase.js +9 -0
  110. package/scripts/flow-proactive-compact.js +1 -1
  111. package/scripts/flow-prompt-composer.js +3 -2
  112. package/scripts/flow-prompt-template.js +3 -2
  113. package/scripts/flow-providers.js +1 -1
  114. package/scripts/flow-question-queue.js +255 -0
  115. package/scripts/flow-repo-map.js +312 -0
  116. package/scripts/flow-review-passes/index.js +1 -1
  117. package/scripts/flow-review-passes/integration.js +1 -1
  118. package/scripts/flow-review-passes/structure.js +1 -1
  119. package/scripts/flow-revision-tracker.js +1 -1
  120. package/scripts/flow-section-resolver.js +1 -1
  121. package/scripts/flow-session-end.js +74 -5
  122. package/scripts/flow-session-state.js +103 -1
  123. package/scripts/flow-setup-hooks.js +1 -1
  124. package/scripts/flow-skeptical-evaluator.js +274 -0
  125. package/scripts/flow-skill-generator.js +3 -3
  126. package/scripts/flow-skill-learn.js +3 -6
  127. package/scripts/flow-skill-manage.js +248 -0
  128. package/scripts/flow-spec-verifier.js +1 -1
  129. package/scripts/flow-standards-checker.js +75 -0
  130. package/scripts/flow-standards-gate.js +1 -1
  131. package/scripts/flow-statusline-setup.js +8 -2
  132. package/scripts/flow-step-changelog.js +2 -2
  133. package/scripts/flow-step-coverage.js +1 -1
  134. package/scripts/flow-step-knowledge.js +1 -1
  135. package/scripts/flow-step-regression.js +1 -1
  136. package/scripts/flow-step-simplifier.js +1 -1
  137. package/scripts/flow-task-analyzer.js +1 -1
  138. package/scripts/flow-task-classifier.js +1 -1
  139. package/scripts/flow-task-enforcer.js +1 -1
  140. package/scripts/flow-template-extractor.js +1 -1
  141. package/scripts/flow-trap-zone.js +1 -1
  142. package/scripts/flow-utils.js +4 -0
  143. package/scripts/flow-worker-mcp-strip.js +122 -0
  144. package/scripts/flow-worker-question-classifier.js +51 -5
  145. package/scripts/flow-workspace-migrate-ipc.js +216 -0
  146. package/scripts/flow-workspace-summary.js +256 -0
  147. package/scripts/hooks/adapters/base-adapter.js +2 -2
  148. package/scripts/hooks/core/feature-dossier-gate.js +194 -0
  149. package/scripts/hooks/core/observation-capture.js +24 -0
  150. package/scripts/hooks/core/overdue-dispatches.js +20 -1
  151. package/scripts/hooks/core/phase-gate.js +15 -1
  152. package/scripts/hooks/core/phase-transition-auto-review.js +61 -0
  153. package/scripts/hooks/core/post-compact.js +5 -2
  154. package/scripts/hooks/core/pre-tool-orchestrator.js +21 -0
  155. package/scripts/hooks/core/routing-gate.js +58 -0
  156. package/scripts/hooks/core/session-context.js +108 -0
  157. package/scripts/hooks/core/session-end-memory-proposals.js +65 -0
  158. package/scripts/hooks/core/session-end-skill-proposals.js +58 -0
  159. package/scripts/hooks/core/session-end.js +25 -0
  160. package/scripts/hooks/core/setup-handler.js +1 -1
  161. package/scripts/hooks/core/task-boundary-reset.js +110 -4
  162. package/scripts/hooks/core/worker-boundary-gate.js +71 -0
  163. package/scripts/hooks/core/worker-tool-first-gate.js +275 -0
  164. package/scripts/hooks/entry/claude-code/post-tool-use.js +2 -2
  165. package/scripts/hooks/entry/claude-code/pre-tool-use.js +7 -2
  166. package/scripts/hooks/entry/claude-code/session-start.js +74 -30
  167. package/scripts/hooks/entry/claude-code/stop.js +47 -1
  168. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +17 -0
  169. package/.workflow/templates/partials/user-commands.hbs +0 -20
@@ -52,17 +52,17 @@ const {
52
52
  // Destructure parsing functions
53
53
  const {
54
54
  parseVTT: _parseVTT, parseSRT: _parseSRT, parseSubtitle: _parseSubtitle, mergeCues: _mergeCues, formatCuesAsText: _formatCuesAsText, getSubtitleStats: _getSubtitleStats,
55
- parseZoom: _parseZoom, parseTeams: _parseTeams, parseMeeting: _parseMeeting, mergeMeetingEntries: _mergeMeetingEntries, formatMeetingAsText: _formatMeetingAsText, getMeetingStats
55
+ parseZoom: _parseZoom, parseTeams: _parseTeams, parseMeeting: _parseMeeting, mergeMeetingEntries: _mergeMeetingEntries, formatMeetingAsText: _formatMeetingAsText, _getMeetingStats
56
56
  } = transcriptParsing;
57
57
 
58
58
  // Destructure chunking functions
59
59
  const {
60
60
  loadDurableSessions: _loadDurableSessions, listDurableSessions: _listDurableSessions, getDurableSession: _getDurableSession, switchDurableSession: _switchDurableSession,
61
61
  archiveDurableSession: _archiveDurableSession, deleteDurableSession: _deleteDurableSession, generateRecoverySummaryForSession: _generateRecoverySummaryForSession,
62
- getTimeSince: _getTimeSince, needsChunking: _needsChunking, planChunks: _planChunks, getChunkingStatus
62
+ getTimeSince: _getTimeSince, needsChunking: _needsChunking, planChunks: _planChunks, _getChunkingStatus
63
63
  } = transcriptChunking;
64
64
 
65
- const { listSupportedLanguages } = transcriptLanguage;
65
+ const { _listSupportedLanguages } = transcriptLanguage;
66
66
 
67
67
  // Destructure story functions
68
68
  const {
@@ -72,7 +72,7 @@ const {
72
72
  formatActionsPrompt: _formatActionsPrompt, getCompletionSummary: _getCompletionSummary, resetPresentation: _resetPresentation,
73
73
  startEditSession: _startEditSession, editUserStory: _editUserStory, editCriterion: _editCriterion, addCriterion: _addCriterion, removeCriterion: _removeCriterion,
74
74
  getEditChanges: _getEditChanges, commitEditSession: _commitEditSession, cancelEditSession: _cancelEditSession, getEditHistory: _getEditHistory, listEditableStories: _listEditableStories,
75
- previewExport: _previewExport, exportApprovedStories: _exportApprovedStories, finalizeDigestion
75
+ previewExport: _previewExport, exportApprovedStories: _exportApprovedStories, _finalizeDigestion
76
76
  } = transcriptStories;
77
77
 
78
78
  // Destructure constants
@@ -81,7 +81,7 @@ const {
81
81
  CORRECTION_PATTERNS: _CORRECTION_PATTERNS, ADDITIVE_PATTERNS: _ADDITIVE_PATTERNS,
82
82
  ENTITY_PATTERNS, VAGUE_PATTERNS, QUESTION_TEMPLATES,
83
83
  DETAIL_PATTERNS, QUESTION_TEMPLATES_BY_LANGUAGE, FOLLOWUP_TRIGGERS,
84
- UI_PATTERNS: _UI_PATTERNS, DATA_PATTERNS: _DATA_PATTERNS, INTERACTION_PATTERNS: _INTERACTION_PATTERNS, COMPLEXITY_LEVELS
84
+ UI_PATTERNS: _UI_PATTERNS, DATA_PATTERNS: _DATA_PATTERNS, INTERACTION_PATTERNS: _INTERACTION_PATTERNS, _COMPLEXITY_LEVELS
85
85
  } = longInputConstants;
86
86
 
87
87
  // Destructure voice functions
@@ -49,7 +49,7 @@ const DECISIONS_PATH = PATHS.decisions;
49
49
  /**
50
50
  * Extract patterns from facts by category
51
51
  */
52
- async function analyzePatterns() {
52
+ async function _analyzePatterns() {
53
53
  const facts = await memoryDb.getAllFacts();
54
54
  const patterns = {};
55
55
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Wogi Flow - Memory CLI (query / fetch / stats / tag)
4
+ * Wogi Flow - Memory CLI (query / fetch / stats / tag / propose)
5
5
  *
6
6
  * Unified query layer over WogiFlow's state-file landscape. Instead of
7
7
  * grepping across ready.json / decisions.md / feedback-patterns.md /
@@ -9,9 +9,13 @@
9
9
  * `flow memory query <filters>` or `flow memory fetch <ref>`.
10
10
  *
11
11
  * Story: wf-e64cacd0 (epic-episodic-memory, re-scoped post-pivot).
12
+ * Story: wf-4434851f (IGR artifact edit proposals — propose|approve|reject|list).
12
13
  *
13
14
  * Boundaries:
14
- * - Does NOT modify memory file contents (read-only across all sources).
15
+ * - Does NOT modify query source files.
16
+ * - The propose/approve surface stages edits to IGR artifacts
17
+ * (product / domain-model / user-journeys / glossary). Agents propose;
18
+ * the user approves at session-end. No silent writes.
15
19
  * - Tags are stored in a sidecar `.workflow/state/memory-tags.json` — the
16
20
  * source memory files stay immutable.
17
21
  *
@@ -21,6 +25,10 @@
21
25
  * flow memory stats
22
26
  * flow memory tag <ref> <#tag>
23
27
  * flow memory untag <ref> <#tag>
28
+ * flow memory propose --block <b> --op <o> --content <file> [--section <h>] [--rationale <text>]
29
+ * flow memory approve <id>
30
+ * flow memory reject <id> [--reason <text>]
31
+ * flow memory list [--status pending|approved|rejected] [--json]
24
32
  *
25
33
  * Usage (programmatic):
26
34
  * const { queryMemory, fetchByRef, memoryStats, addTag } = require('./flow-memory');
@@ -749,15 +757,78 @@ if (require.main === module) {
749
757
  return;
750
758
  }
751
759
 
760
+ if (cmd === 'propose' || cmd === 'approve' || cmd === 'reject' || cmd === 'list') {
761
+ const store = require('../lib/memory-proposal-store');
762
+ try {
763
+ if (cmd === 'propose') {
764
+ const record = store.createProposal({
765
+ block: args.block,
766
+ op: args.op,
767
+ contentFile: args.content,
768
+ section: args.section,
769
+ rationale: args.rationale,
770
+ proposedBy: args['proposed-by'],
771
+ });
772
+ if (args.json) { console.log(JSON.stringify(record, null, 2)); return; }
773
+ console.log(`Staged ${record.op} on ${record.block} (${record.id})`);
774
+ console.log(` content: ${record.contentPath}`);
775
+ if (record.section) console.log(` section: ${record.section}`);
776
+ if (record.rationale) console.log(` rationale: ${record.rationale}`);
777
+ console.log(` review at session-end or: flow memory list`);
778
+ return;
779
+ }
780
+ if (cmd === 'approve') {
781
+ const id = args._[1] || args.id;
782
+ if (!id) { console.error('Usage: flow memory approve <id>'); process.exit(1); }
783
+ const applied = store.approveProposal({ id });
784
+ if (args.json) { console.log(JSON.stringify(applied, null, 2)); return; }
785
+ console.log(`Approved ${applied.op} on ${applied.block} (${applied.id})`);
786
+ return;
787
+ }
788
+ if (cmd === 'reject') {
789
+ const id = args._[1] || args.id;
790
+ if (!id) { console.error('Usage: flow memory reject <id> [--reason <text>]'); process.exit(1); }
791
+ const rejected = store.rejectProposal({ id, reason: args.reason });
792
+ if (args.json) { console.log(JSON.stringify(rejected, null, 2)); return; }
793
+ console.log(`Rejected ${rejected.op} on ${rejected.block} (${rejected.id})`);
794
+ if (rejected.reason) console.log(` reason: ${rejected.reason}`);
795
+ return;
796
+ }
797
+ // list
798
+ const statusFilter = args.status && args.status !== true ? args.status : 'pending';
799
+ const list = store.listProposals({ status: statusFilter });
800
+ if (args.json) { console.log(JSON.stringify(list, null, 2)); return; }
801
+ if (list.length === 0) { console.log(`(no ${statusFilter} memory proposals)`); return; }
802
+ console.log(`Memory proposals (${statusFilter}, ${list.length}):`);
803
+ for (const r of list) {
804
+ const icon = r.op === 'append' ? '+' : r.op === 'replace-section' ? '~' : '!';
805
+ console.log(` ${icon} ${r.block} [${r.op}] ${r.id}`);
806
+ console.log(` proposedAt: ${r.proposedAt} by: ${r.proposedBy}`);
807
+ if (r.section) console.log(` section: ${r.section}`);
808
+ if (r.rationale) console.log(` rationale: ${r.rationale}`);
809
+ }
810
+ return;
811
+ } catch (err) {
812
+ console.error(`[memory:${cmd}] error: ${err.message}`);
813
+ process.exit(1);
814
+ }
815
+ }
816
+
752
817
  console.log(`Usage:
753
- flow memory query [--since=<dur>] [--task=<id>] [--kind=<k>] [--tag=<#t>] [--limit=N] [--json]
754
- flow memory fetch <ref> [--raw] [--json]
755
- flow memory stats [--json]
756
- flow memory tag <ref> <#tag>
757
- flow memory untag <ref> <#tag>
818
+ flow memory query [--since=<dur>] [--task=<id>] [--kind=<k>] [--tag=<#t>] [--limit=N] [--json]
819
+ flow memory fetch <ref> [--raw] [--json]
820
+ flow memory stats [--json]
821
+ flow memory tag <ref> <#tag>
822
+ flow memory untag <ref> <#tag>
823
+ flow memory propose --block <b> --op <o> --content <file> [--section <h>] [--rationale <text>]
824
+ flow memory approve <id>
825
+ flow memory reject <id> [--reason <text>]
826
+ flow memory list [--status pending|approved|rejected] [--json]
758
827
 
759
828
  Durations: 30m, 2h, 7d, 2w
760
829
  Kinds: ${Object.values(KINDS).join(', ')}
830
+ Blocks: product, domain-model, user-journeys, glossary
831
+ Ops: append, replace-section, replace-all (replace-all requires --rationale)
761
832
  Refs: wf-XXXXXXXX | R-NNN | CORR-NNN | <adversary-run-filename>`);
762
833
  process.exit(cmd ? 1 : 0);
763
834
  })().catch((err) => {
@@ -24,7 +24,7 @@ const {
24
24
  outputJson,
25
25
  checkSpecMigration,
26
26
  SPEC_FILE_MAP: _SPEC_FILE_MAP,
27
- getTodayDate
27
+ _getTodayDate
28
28
  } = require('./flow-utils')
29
29
  const { printHeader, printSection, color, success, warn, error, info } = require('./flow-output');;
30
30
 
@@ -18,7 +18,7 @@ const {
18
18
  getConfigValue: _getConfigValue,
19
19
  color: _color,
20
20
  warn: _warn,
21
- error
21
+ _error
22
22
  } = require('./flow-utils');
23
23
 
24
24
  // ============================================================
@@ -545,7 +545,7 @@ function getCostAnalysis(_options = {}) {
545
545
  }
546
546
 
547
547
  // Generate recommendations using CONFIG constants
548
- for (const [modelId, modelData] of Object.entries(analysis.byModel)) {
548
+ for (const [_modelId, modelData] of Object.entries(analysis.byModel)) {
549
549
  if (modelData.costTier === 'premium' && modelData.totalTasks > CONFIG.MIN_TASKS_FOR_RECOMMENDATION) {
550
550
  const avgCost = modelData.avgCostPerTask;
551
551
  if (avgCost > CONFIG.COST_OPTIMIZATION_THRESHOLD) {
@@ -826,7 +826,7 @@ function formatCostAnalysis(analysis) {
826
826
  const sortedModels = Object.entries(analysis.byModel)
827
827
  .sort((a, b) => b[1].totalCost - a[1].totalCost);
828
828
 
829
- for (const [modelId, data] of sortedModels) {
829
+ for (const [_modelId, data] of sortedModels) {
830
830
  output += ` ${color('cyan', data.displayName)} (${data.costTier})\n`;
831
831
  output += ` Total: $${data.totalCost.toFixed(4)} | Tasks: ${data.totalTasks} | Avg: $${data.avgCostPerTask.toFixed(4)}\n`;
832
832
  }
@@ -649,23 +649,6 @@ function printBriefing(briefing) {
649
649
  function main() {
650
650
  const { flags } = parseFlags(process.argv.slice(2));
651
651
 
652
- // Check if morning briefing is enabled
653
- const config = getConfig();
654
- const morningConfig = config.morningBriefing || {};
655
-
656
- if (morningConfig.enabled === false) {
657
- if (flags.json) {
658
- outputJson({
659
- success: false,
660
- error: 'Morning briefing is disabled in config',
661
- hint: 'Set morningBriefing.enabled: true in .workflow/config.json'
662
- });
663
- }
664
- console.log(color('yellow', 'Morning briefing is disabled.'));
665
- console.log(color('dim', 'Enable it in .workflow/config.json: morningBriefing.enabled: true'));
666
- process.exit(0);
667
- }
668
-
669
652
  // v2.6.1: Clean up stale state before briefing (safety net)
670
653
  // Morning briefing also cleans up stale auto-created tasks older than 24h
671
654
  const cleanedState = cleanupStaleState({ cleanupStaleTasks: true });
@@ -220,7 +220,7 @@ function calculateApproachScore(result) {
220
220
  if (result.code) score += 10;
221
221
 
222
222
  // Validation gates
223
- for (const [gate, passed] of Object.entries(result.validationResults)) {
223
+ for (const [_gate, passed] of Object.entries(result.validationResults)) {
224
224
  if (passed) score += 20;
225
225
  else score -= 10;
226
226
  }
@@ -440,7 +440,7 @@ class ProjectContextGenerator {
440
440
 
441
441
  // Collect all array exports for global warning
442
442
  const allArrayExports = [];
443
- for (const [name, info] of Object.entries(exportMap.components)) {
443
+ for (const [_name, info] of Object.entries(exportMap.components)) {
444
444
  if (info.arrayExports && info.arrayExports.length > 0) {
445
445
  allArrayExports.push(...info.arrayExports);
446
446
  }
@@ -479,7 +479,7 @@ class ProjectContextGenerator {
479
479
  if (Object.keys(exportMap.services).length > 0) {
480
480
  section += '### Services\n\n';
481
481
  section += '```typescript\n';
482
- for (const [name, info] of Object.entries(exportMap.services)) {
482
+ for (const [_name, info] of Object.entries(exportMap.services)) {
483
483
  if (info.exports.length > 0) {
484
484
  section += `import { ${info.exports.join(', ')} } from '${info.importPath}';\n`;
485
485
  }
@@ -491,7 +491,7 @@ class ProjectContextGenerator {
491
491
  if (Object.keys(exportMap.types).length > 0) {
492
492
  section += '### Types\n\n';
493
493
  section += '```typescript\n';
494
- for (const [name, info] of Object.entries(exportMap.types)) {
494
+ for (const [_name, info] of Object.entries(exportMap.types)) {
495
495
  if (info.types && info.types.length > 0) {
496
496
  section += `import type { ${info.types.join(', ')} } from '${info.importPath}';\n`;
497
497
  }
@@ -503,7 +503,7 @@ class ProjectContextGenerator {
503
503
  if (Object.keys(exportMap.utils).length > 0) {
504
504
  section += '### Utilities\n\n';
505
505
  section += '```typescript\n';
506
- for (const [name, info] of Object.entries(exportMap.utils)) {
506
+ for (const [_name, info] of Object.entries(exportMap.utils)) {
507
507
  if (info.exports.length > 0) {
508
508
  section += `import { ${info.exports.join(', ')} } from '${info.importPath}';\n`;
509
509
  }
@@ -17,7 +17,7 @@ const {
17
17
  loadRelatedCode
18
18
  } = require('./flow-instruction-richness');
19
19
 
20
- function log(color, ...args) {
20
+ function _log(color, ...args) {
21
21
  console.log(colors[color] + args.join(' ') + colors.reset);
22
22
  }
23
23
 
@@ -34,17 +34,17 @@ const {
34
34
  assessTaskComplexity,
35
35
  TOKEN_BUDGETS: _TOKEN_BUDGETS,
36
36
  getDefaultTokens,
37
- clampTokens
37
+ _clampTokens
38
38
  } = require('./flow-complexity');
39
39
 
40
40
  // Import instruction richness module
41
41
  const {
42
42
  getInstructionRichness,
43
43
  getVerbosityGuidance: _getVerbosityGuidance,
44
- loadProjectContext: loadRichnessContext,
44
+ loadProjectContext: _loadRichnessContext,
45
45
  loadPatterns: _loadPatterns,
46
46
  loadRelevantTypes: _loadRelevantTypes,
47
- loadRelatedCode
47
+ _loadRelatedCode
48
48
  } = require('./flow-instruction-richness');
49
49
 
50
50
  // Import export scanner module
@@ -67,7 +67,7 @@ const {
67
67
  createExecutorFromConfig: _createExecutorFromConfig,
68
68
  getExecutorConfig,
69
69
  MODEL_CAPABILITIES: _MODEL_CAPABILITIES,
70
- getModelContextLimit
70
+ _getModelContextLimit
71
71
  } = require('./flow-providers');
72
72
 
73
73
  // Import response parser for error recovery
@@ -86,7 +86,7 @@ const {
86
86
  analyzeFailure,
87
87
  refinePromptForRetry,
88
88
  recordSuccessfulRecovery,
89
- ERROR_CATEGORIES
89
+ _ERROR_CATEGORIES
90
90
  } = require('./flow-adaptive-learning');
91
91
 
92
92
  // Import pattern enforcer for active learning enforcement
@@ -94,7 +94,7 @@ const {
94
94
  injectPatterns,
95
95
  extractRelevantPatterns: _extractRelevantPatterns,
96
96
  validateAgainstPatterns: _validateAgainstPatterns,
97
- generateSessionSummary
97
+ _generateSessionSummary
98
98
  } = require('./flow-pattern-enforcer');
99
99
 
100
100
  // v2.0: Import durable session for unified step tracking
@@ -109,12 +109,12 @@ const {
109
109
 
110
110
  const {
111
111
  classifyTask,
112
- getTaskTypeContext
112
+ _getTaskTypeContext
113
113
  } = require('./flow-task-classifier');
114
114
 
115
115
  const {
116
116
  learnFromFailure,
117
- enhancePromptWithLearning
117
+ _enhancePromptWithLearning
118
118
  } = require('./flow-failure-learning');
119
119
 
120
120
  // ============================================================
@@ -342,7 +342,7 @@ function formatResults(comparison, results) {
342
342
  // ============================================================
343
343
 
344
344
  async function main() {
345
- const { flags, positional } = parseFlags(process.argv.slice(2));
345
+ const { flags, _positional } = parseFlags(process.argv.slice(2));
346
346
 
347
347
  if (flags.help) {
348
348
  console.log(`
@@ -28,6 +28,15 @@ if (command === 'transition') {
28
28
  const success = transitionPhase(from, to, taskId || null);
29
29
  if (success) {
30
30
  console.log(`Phase: ${from} → ${to}`);
31
+ // wf-8d635d0e / E1: fire background auto-review on coding → validating.
32
+ // Fails open — any error here must not fail the primary transition.
33
+ try {
34
+ const { maybeStartAutoReview } = require('./hooks/core/phase-transition-auto-review');
35
+ const result = maybeStartAutoReview(from, to, taskId || null);
36
+ if (result.started && process.env.DEBUG) {
37
+ console.error(`[auto-review] started pid=${result.handle?.pid} task=${taskId}`);
38
+ }
39
+ } catch (_err) { /* fail-open */ }
31
40
  } else {
32
41
  console.error(`Phase transition failed: ${from} → ${to}`);
33
42
  process.exit(1);
@@ -78,7 +78,7 @@ function getProactiveCompactionConfig() {
78
78
  * @returns {{ shouldCompact: boolean, reason: string, checkpoint: Object|null }}
79
79
  */
80
80
  function shouldCompactAtPhase(params) {
81
- const { phase, contextPercent, taskId } = params;
81
+ const { phase, contextPercent, _taskId } = params;
82
82
  const config = getProactiveCompactionConfig();
83
83
 
84
84
  // Disabled — skip
@@ -16,6 +16,7 @@
16
16
 
17
17
  const fs = require('node:fs');
18
18
  const path = require('node:path');
19
+ const { DANGEROUS_KEYS } = require('./flow-io');
19
20
  const {
20
21
  PROJECT_ROOT,
21
22
  parseFlags,
@@ -341,7 +342,7 @@ function applyTemplate(template, data) {
341
342
  }
342
343
 
343
344
  // Forbidden keys to prevent prototype pollution (case-insensitive)
344
- const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
345
+ // Consolidated to flow-io canonical (audit dup-002 / wf-9fc4970b).
345
346
 
346
347
  // Simple substitution: {{key}} or {{object.key}}
347
348
  return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
@@ -351,7 +352,7 @@ function applyTemplate(template, data) {
351
352
  for (const key of keys) {
352
353
  // Prevent prototype pollution attacks (case-insensitive check)
353
354
  const keyLower = key.toLowerCase();
354
- if (FORBIDDEN_KEYS.has(keyLower)) return match;
355
+ if (DANGEROUS_KEYS.has(keyLower)) return match;
355
356
  if (value === undefined || value === null) return match;
356
357
  // Only access own properties
357
358
  if (!Object.hasOwn(value, key)) return match;
@@ -38,8 +38,9 @@ const MODEL_TEMPLATE_MAP = {
38
38
  'gemini-2-flash': 'gemini-flash.yaml'
39
39
  };
40
40
 
41
- // Blocked keys for security (prototype pollution prevention)
42
- const BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
41
+ // Blocked keys for security (prototype pollution prevention).
42
+ // Consolidated to flow-io canonical (audit dup-002 / wf-9fc4970b).
43
+ const { DANGEROUS_KEYS: BLOCKED_KEYS } = require('./flow-io');
43
44
 
44
45
  // ============================================================
45
46
  // YAML Parser (lightweight, no dependency)
@@ -203,7 +203,7 @@ class BaseProvider {
203
203
  * Abstract — every concrete provider subclass (AnthropicProvider,
204
204
  * OpenAIProvider, OllamaProvider, etc.) must override this.
205
205
  */
206
- // eslint-disable-next-line no-unused-vars
206
+
207
207
  async complete(prompt, _options = {}) {
208
208
  throw new Error(`Provider ${this.name}: complete() not implemented — override in subclass`);
209
209
  }