@syntesseraai/opencode-feature-factory 0.6.21 → 0.7.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,118 @@
1
+ /**
2
+ * Deterministic gate evaluation functions.
3
+ *
4
+ * These replace the LLM-evaluated gate commands with code that applies
5
+ * threshold logic directly. The LLM still produces the synthesis; the
6
+ * gate decision is computed here.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Planning gate
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Evaluate the planning consensus gate.
13
+ *
14
+ * - `>=75` consensus score → APPROVED
15
+ * - `50-74` → REWORK
16
+ * - `<50` → BLOCKED
17
+ */
18
+ export function evaluatePlanningGate(consensus) {
19
+ if (consensus.consensusScore >= 75) {
20
+ return { decision: 'APPROVED' };
21
+ }
22
+ if (consensus.consensusScore >= 50) {
23
+ return {
24
+ decision: 'REWORK',
25
+ feedback: `Consensus score ${consensus.consensusScore}/100. Divergent elements:\n${consensus.divergentElements}\nOpen questions:\n${consensus.openQuestions}`,
26
+ };
27
+ }
28
+ return {
29
+ decision: 'BLOCKED',
30
+ reason: `Consensus score too low (${consensus.consensusScore}/100). Open questions:\n${consensus.openQuestions}`,
31
+ };
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // Review gate
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Evaluate the review approval gate.
38
+ *
39
+ * - confidence >=95 AND unresolvedIssues == 0 → APPROVED
40
+ * - iteration >= maxIterations → ESCALATE
41
+ * - otherwise → REWORK
42
+ */
43
+ export function evaluateReviewGate(synthesis, iteration, maxIterations = 10) {
44
+ if (synthesis.overallConfidence >= 95 && synthesis.unresolvedIssues === 0) {
45
+ return { decision: 'APPROVED' };
46
+ }
47
+ if (iteration >= maxIterations) {
48
+ return {
49
+ decision: 'ESCALATE',
50
+ reason: `Max review iterations reached (${maxIterations}). Confidence: ${synthesis.overallConfidence}, unresolved: ${synthesis.unresolvedIssues}`,
51
+ };
52
+ }
53
+ return {
54
+ decision: 'REWORK',
55
+ feedback: synthesis.reworkInstructions ??
56
+ `Confidence ${synthesis.overallConfidence}/100 with ${synthesis.unresolvedIssues} unresolved issues.`,
57
+ };
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Documentation gate
61
+ // ---------------------------------------------------------------------------
62
+ /**
63
+ * Evaluate the documentation approval gate.
64
+ *
65
+ * - confidence >95, no change requested, 0 unresolved → APPROVED
66
+ * - iteration >= maxIterations → ESCALATE
67
+ * - otherwise → REWORK
68
+ */
69
+ export function evaluateDocGate(review, iteration, maxIterations = 5) {
70
+ if (review.verdict === 'APPROVED' && review.unresolvedIssues === 0 && review.confidence > 95) {
71
+ return { decision: 'APPROVED' };
72
+ }
73
+ if (iteration >= maxIterations) {
74
+ return {
75
+ decision: 'ESCALATE',
76
+ reason: `Max doc iterations reached (${maxIterations}). Confidence: ${review.confidence}, unresolved: ${review.unresolvedIssues}`,
77
+ };
78
+ }
79
+ return {
80
+ decision: 'REWORK',
81
+ feedback: review.reworkInstructions ??
82
+ `Documentation review verdict: ${review.verdict}. ${review.unresolvedIssues} unresolved issues.`,
83
+ };
84
+ }
85
+ // ---------------------------------------------------------------------------
86
+ // Mini-loop implementation gate
87
+ // ---------------------------------------------------------------------------
88
+ /**
89
+ * Evaluate the mini-loop implementation gate.
90
+ *
91
+ * - confidence >95, no change requested, 0 blocking issues → APPROVED
92
+ * - iteration >= maxIterations → ESCALATE
93
+ * - otherwise → REWORK
94
+ */
95
+ export function evaluateMiniLoopImplGate(review, iteration, maxIterations = 10) {
96
+ if (review.confidence > 95 && !review.changeRequested && review.unresolvedIssues === 0) {
97
+ return { decision: 'APPROVED' };
98
+ }
99
+ if (iteration >= maxIterations) {
100
+ return {
101
+ decision: 'ESCALATE',
102
+ reason: `Max mini-loop iterations reached (${maxIterations}). Confidence: ${review.confidence}, unresolved: ${review.unresolvedIssues}`,
103
+ };
104
+ }
105
+ return {
106
+ decision: 'REWORK',
107
+ feedback: review.reworkInstructions ??
108
+ `Change requested: ${review.changeRequested}, confidence: ${review.confidence}, unresolved: ${review.unresolvedIssues}`,
109
+ };
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Mini-loop documentation gate
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Evaluate the mini-loop documentation gate.
116
+ * Same thresholds as the pipeline doc gate.
117
+ */
118
+ export const evaluateMiniLoopDocGate = evaluateDocGate;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared workflow orchestration utilities.
3
+ *
4
+ * Re-exports the fan-out, gate, and type modules as a single convenient
5
+ * entry-point for tool implementations.
6
+ */
7
+ export { fanOut, promptSession, extractText, type Client } from './fan-out.js';
8
+ export { evaluatePlanningGate, evaluateReviewGate, evaluateDocGate, evaluateMiniLoopImplGate, evaluateMiniLoopDocGate, } from './gate-evaluator.js';
9
+ export * from './types.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared workflow orchestration utilities.
3
+ *
4
+ * Re-exports the fan-out, gate, and type modules as a single convenient
5
+ * entry-point for tool implementations.
6
+ */
7
+ export { fanOut, promptSession, extractText } from './fan-out.js';
8
+ export { evaluatePlanningGate, evaluateReviewGate, evaluateDocGate, evaluateMiniLoopImplGate, evaluateMiniLoopDocGate, } from './gate-evaluator.js';
9
+ export * from './types.js';
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Workflow type definitions for the Feature Factory pipeline and mini-loop.
3
+ *
4
+ * These types represent the structured data that flows between phases
5
+ * of the workflow, replacing the untyped $RESULT[name] string interpolation
6
+ * used by the subtask2 command approach.
7
+ */
8
+ /** A model identifier split into provider and model parts. */
9
+ export interface ModelId {
10
+ providerID: string;
11
+ modelID: string;
12
+ }
13
+ /** Named model configuration used for fan-out. */
14
+ export interface NamedModel {
15
+ /** Human label used in prompts and logs (e.g. "opus", "gemini", "codex") */
16
+ tag: string;
17
+ /** Provider/model split consumed by the SDK. */
18
+ model: ModelId;
19
+ }
20
+ export declare const PLANNING_MODELS: readonly NamedModel[];
21
+ export declare const REVIEW_MODELS: readonly NamedModel[];
22
+ export declare const ORCHESTRATOR_MODEL: ModelId;
23
+ export declare const BUILD_MODEL: ModelId;
24
+ export declare const DOC_MODEL: ModelId;
25
+ export declare const VALIDATE_MODEL: ModelId;
26
+ export declare const DOC_REVIEW_MODEL: ModelId;
27
+ export type GateDecision = 'APPROVED' | 'REWORK' | 'BLOCKED' | 'ESCALATE';
28
+ export interface GateResult {
29
+ decision: GateDecision;
30
+ /** Feedback for the next loop iteration when decision is REWORK. */
31
+ feedback?: string;
32
+ /** Reason the gate blocked or escalated. */
33
+ reason?: string;
34
+ }
35
+ export interface PlanProposal {
36
+ tag: string;
37
+ requirementsSummary: string;
38
+ architectureValidation: string;
39
+ implementationSteps: string;
40
+ risksAndMitigations: string;
41
+ testingStrategy: string;
42
+ /** The full raw text returned by the model (kept for transparency). */
43
+ raw: string;
44
+ }
45
+ export interface ConsensusPlan {
46
+ consensusScore: number;
47
+ agreedElements: string;
48
+ divergentElements: string;
49
+ synthesizedPlan: string;
50
+ openQuestions: string;
51
+ raw: string;
52
+ }
53
+ export interface PlanningPhaseResult {
54
+ proposals: PlanProposal[];
55
+ consensus: ConsensusPlan;
56
+ gate: GateResult;
57
+ /** The final plan text accepted by the gate (only set when APPROVED). */
58
+ finalPlan?: string;
59
+ }
60
+ export interface TaskBreakdown {
61
+ taskId: string;
62
+ title: string;
63
+ description: string;
64
+ targetFiles: string[];
65
+ dependencies: string[];
66
+ acceptanceCriteria: string;
67
+ }
68
+ export interface ValidatedBatch {
69
+ batchId: string;
70
+ tasks: TaskBreakdown[];
71
+ parallelizable: boolean;
72
+ architecturalRisks: string[];
73
+ }
74
+ export interface ImplementationReport {
75
+ filesChanged: string[];
76
+ testsRun: string[];
77
+ testsPassed: boolean;
78
+ openIssues: string[];
79
+ raw: string;
80
+ }
81
+ export interface BuildPhaseResult {
82
+ tasks: TaskBreakdown[];
83
+ batches: ValidatedBatch[];
84
+ implementation: ImplementationReport;
85
+ }
86
+ export interface ReviewReport {
87
+ tag: string;
88
+ findings: string;
89
+ confidence: number;
90
+ raw: string;
91
+ }
92
+ export interface ReviewSynthesis {
93
+ overallConfidence: number;
94
+ consolidatedFindings: string;
95
+ unresolvedIssues: number;
96
+ verdict: 'APPROVED' | 'REWORK_REQUIRED';
97
+ reworkInstructions?: string;
98
+ raw: string;
99
+ }
100
+ export interface ReviewPhaseResult {
101
+ reviews: ReviewReport[];
102
+ synthesis: ReviewSynthesis;
103
+ gate: GateResult;
104
+ }
105
+ export interface DocUpdate {
106
+ filesChanged: string[];
107
+ rationale: string;
108
+ raw: string;
109
+ }
110
+ export interface DocReview {
111
+ verdict: 'APPROVED' | 'REWORK_REQUIRED';
112
+ unresolvedIssues: number;
113
+ confidence: number;
114
+ reworkInstructions?: string;
115
+ raw: string;
116
+ }
117
+ export interface DocPhaseResult {
118
+ update: DocUpdate;
119
+ review: DocReview;
120
+ gate: GateResult;
121
+ }
122
+ export interface PipelineState {
123
+ requirements: string;
124
+ planning?: PlanningPhaseResult;
125
+ build?: BuildPhaseResult;
126
+ review?: ReviewPhaseResult;
127
+ documentation?: DocPhaseResult;
128
+ }
129
+ export interface MiniLoopState {
130
+ requirements: string;
131
+ implementation?: ImplementationReport;
132
+ review?: {
133
+ confidence: number;
134
+ changeRequested: boolean;
135
+ unresolvedIssues: number;
136
+ reworkInstructions?: string;
137
+ raw: string;
138
+ };
139
+ documentation?: DocPhaseResult;
140
+ }
141
+ /** Parse "provider/model" string into a ModelId. */
142
+ export declare function parseModelString(s: string): ModelId;
143
+ /**
144
+ * Parse a comma-separated list of `tag:provider/model` entries into NamedModel[].
145
+ *
146
+ * Example input: `"opus:anthropic/claude-opus-4-6, gemini:google/gemini-2.5-pro"`
147
+ */
148
+ export declare function parseNamedModels(s: string): NamedModel[];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Workflow type definitions for the Feature Factory pipeline and mini-loop.
3
+ *
4
+ * These types represent the structured data that flows between phases
5
+ * of the workflow, replacing the untyped $RESULT[name] string interpolation
6
+ * used by the subtask2 command approach.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Default model roster
10
+ // ---------------------------------------------------------------------------
11
+ export const PLANNING_MODELS = [
12
+ { tag: 'opus', model: { providerID: 'anthropic', modelID: 'claude-opus-4-6' } },
13
+ { tag: 'gemini', model: { providerID: 'opencode', modelID: 'gemini-3.1-pro' } },
14
+ { tag: 'codex', model: { providerID: 'openai', modelID: 'gpt-5.3-codex' } },
15
+ ];
16
+ export const REVIEW_MODELS = PLANNING_MODELS;
17
+ export const ORCHESTRATOR_MODEL = {
18
+ providerID: 'openai',
19
+ modelID: 'gpt-5.4',
20
+ };
21
+ export const BUILD_MODEL = {
22
+ providerID: 'openai',
23
+ modelID: 'gpt-5.3-codex',
24
+ };
25
+ export const DOC_MODEL = BUILD_MODEL;
26
+ export const VALIDATE_MODEL = {
27
+ providerID: 'opencode',
28
+ modelID: 'gemini-3.1-pro',
29
+ };
30
+ export const DOC_REVIEW_MODEL = VALIDATE_MODEL;
31
+ // ---------------------------------------------------------------------------
32
+ // Helpers
33
+ // ---------------------------------------------------------------------------
34
+ /** Parse "provider/model" string into a ModelId. */
35
+ export function parseModelString(s) {
36
+ const slash = s.indexOf('/');
37
+ if (slash === -1)
38
+ return { providerID: s, modelID: s };
39
+ return { providerID: s.slice(0, slash), modelID: s.slice(slash + 1) };
40
+ }
41
+ /**
42
+ * Parse a comma-separated list of `tag:provider/model` entries into NamedModel[].
43
+ *
44
+ * Example input: `"opus:anthropic/claude-opus-4-6, gemini:google/gemini-2.5-pro"`
45
+ */
46
+ export function parseNamedModels(s) {
47
+ return s
48
+ .split(',')
49
+ .map((entry) => entry.trim())
50
+ .filter((entry) => entry.length > 0)
51
+ .map((entry) => {
52
+ const colon = entry.indexOf(':');
53
+ if (colon === -1) {
54
+ // No tag — use the model id as the tag
55
+ const model = parseModelString(entry);
56
+ return { tag: model.modelID, model };
57
+ }
58
+ const tag = entry.slice(0, colon).trim();
59
+ const model = parseModelString(entry.slice(colon + 1).trim());
60
+ return { tag, model };
61
+ });
62
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@syntesseraai/opencode-feature-factory",
4
- "version": "0.6.21",
4
+ "version": "0.7.0",
5
5
  "type": "module",
6
6
  "description": "OpenCode plugin for Feature Factory agents - provides sub-agents and skills for validation, review, security, and architecture assessment",
7
7
  "license": "MIT",