fraim-framework 2.0.123 → 2.0.124

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.
@@ -20,31 +20,25 @@
20
20
  * seekMentoring handler (e.g., when the local AIMentor errors).
21
21
  */
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.REQUIRED_QUALITY_FIELDS = exports.ALL_STAGE_CATEGORIES = exports.STAGE_DISPLAY_NAMES = exports.STAGE_CATEGORY_MAP = exports.QUALITY_PRODUCING_JOBS = exports.QUALITY_REGISTRY = void 0;
23
+ exports.REQUIRED_QUALITY_FIELDS = exports.ALL_STAGE_CATEGORIES = exports.STAGE_DISPLAY_NAMES = exports.STAGE_CATEGORY_MAP = exports.QUALITY_GATE_JOBS = exports.QUALITY_SCORE_JOBS = exports.QUALITY_PRODUCING_JOBS = exports.QUALITY_REGISTRY = void 0;
24
24
  exports.getRequiredFieldsForJob = getRequiredFieldsForJob;
25
25
  exports.validateQualityEvidence = validateQualityEvidence;
26
26
  exports.buildQualityRejectionMessage = buildQualityRejectionMessage;
27
- /**
28
- * Central registry of FRAIM jobs that interact with the quality telemetry system.
29
- * This is the SINGLE SOURCE OF TRUTH for:
30
- * 1. Stage mapping (where it appears on the dashboard).
31
- * 2. Enforcement (whether it MUST emit a quality score to complete).
32
- */
33
27
  exports.QUALITY_REGISTRY = {
34
28
  // Customer Development
35
- 'process-interview-notes': { stage: 'customer-development', enforced: true },
36
- 'triage-customer-needs': { stage: 'customer-development', enforced: true },
29
+ 'process-interview-notes': { stage: 'customer-development', enforced: true, telemetryKind: 'score' },
30
+ 'triage-customer-needs': { stage: 'customer-development', enforced: true, telemetryKind: 'gate' },
37
31
  'interview-preparation': { stage: 'customer-discovery', enforced: false },
38
32
  // Business Strategy
39
- 'review-business-strategy': { stage: 'business-strategy', enforced: true },
33
+ 'review-business-strategy': { stage: 'business-strategy', enforced: true, telemetryKind: 'score' },
40
34
  'business-plan-creation': { stage: 'business-strategy', enforced: false },
41
- 'branding-quality-audit': { stage: 'branding', enforced: true },
35
+ 'branding-quality-audit': { stage: 'branding', enforced: true, telemetryKind: 'score' },
42
36
  // Product Quality
43
- 'code-quality-assessment': { stage: 'product-quality', enforced: true },
37
+ 'code-quality-assessment': { stage: 'product-quality', enforced: true, telemetryKind: 'score' },
44
38
  // Test Quality
45
- 'test-quality-assessment': { stage: 'test-quality', enforced: true },
39
+ 'test-quality-assessment': { stage: 'test-quality', enforced: true, telemetryKind: 'score' },
46
40
  // Security
47
- 'security-review': { stage: 'security', enforced: true },
41
+ 'security-review': { stage: 'security', enforced: true, telemetryKind: 'score' },
48
42
  // Fundraising
49
43
  'investor-pitch-preparation': { stage: 'fundraising', enforced: false },
50
44
  // Go-to-Market
@@ -55,6 +49,15 @@ exports.QUALITY_REGISTRY = {
55
49
  * Derived enforcement list for backward compatibility.
56
50
  */
57
51
  exports.QUALITY_PRODUCING_JOBS = Object.keys(exports.QUALITY_REGISTRY).filter((job) => exports.QUALITY_REGISTRY[job].enforced);
52
+ /**
53
+ * Jobs that write scored review rows used by the Quality tab scorecards.
54
+ * Gate-only telemetry jobs (for example `triage-customer-needs`) are excluded.
55
+ */
56
+ exports.QUALITY_SCORE_JOBS = Object.keys(exports.QUALITY_REGISTRY).filter((job) => exports.QUALITY_REGISTRY[job].enforced && exports.QUALITY_REGISTRY[job].telemetryKind !== 'gate');
57
+ /**
58
+ * Jobs that emit gate-style telemetry without a composite score.
59
+ */
60
+ exports.QUALITY_GATE_JOBS = Object.keys(exports.QUALITY_REGISTRY).filter((job) => exports.QUALITY_REGISTRY[job].enforced && exports.QUALITY_REGISTRY[job].telemetryKind === 'gate');
58
61
  /**
59
62
  * @deprecated Use QUALITY_REGISTRY for new code.
60
63
  * Derived stage mappings for backward compatibility.
@@ -97,6 +100,12 @@ const CUSTOMER_DEV_REQUIRED_FIELDS = [
97
100
  { path: 'participant.authority', type: 'number' },
98
101
  { path: 'evidence.quoteSpecificityAvg', type: 'number' }
99
102
  ];
103
+ /**
104
+ * Gate-only customer development jobs emit a decision without a composite score.
105
+ */
106
+ const GATE_REQUIRED_FIELDS = [
107
+ { path: 'gateDecision', type: 'string' }
108
+ ];
100
109
  /**
101
110
  * Required fields for all other quality-producing jobs.
102
111
  * Only composite and coaching are universally required — sub-dimensions
@@ -112,6 +121,9 @@ const UNIVERSAL_REQUIRED_FIELDS = [
112
121
  */
113
122
  function getRequiredFieldsForJob(jobName) {
114
123
  const entry = exports.QUALITY_REGISTRY[jobName];
124
+ if (entry?.telemetryKind === 'gate') {
125
+ return GATE_REQUIRED_FIELDS;
126
+ }
115
127
  if (entry?.stage === 'customer-development') {
116
128
  return CUSTOMER_DEV_REQUIRED_FIELDS;
117
129
  }
@@ -174,38 +186,53 @@ function validateQualityEvidence(quality, jobName) {
174
186
  function buildQualityRejectionMessage(jobName, currentPhase, errors) {
175
187
  const errorBullets = errors.map(e => `- ${e}`).join('\n');
176
188
  const entry = exports.QUALITY_REGISTRY[jobName];
189
+ const isGateOnly = entry?.telemetryKind === 'gate';
177
190
  const isCustomerDev = entry?.stage === 'customer-development';
178
- const schemaExample = isCustomerDev
191
+ const schemaExample = isGateOnly
179
192
  ? [
180
193
  '```javascript',
181
194
  'evidence: {',
182
195
  ' quality: {',
183
- ' composite: <number 0-10>,',
184
- ' participant: { fit: <number 1-10>, urgency: <number 1-10>, authority: <number 1-10> },',
185
- ' evidence: {',
186
- ' quoteSpecificityAvg: <number 1-10>',
187
- ' // other fields are allowed and encouraged but not required',
188
- ' },',
189
- ' coaching: "<actionable recommendation>",',
190
- ' interviewee: "<name>",',
191
- ' company: "<company>"',
196
+ ' gateDecision: "<pass|flag|fail>",',
197
+ ' interviewsAnalyzed: <number>,',
198
+ ' distinctPainPatterns: <number>,',
199
+ ' gaps: ["<gap summary>"],',
200
+ ' coaching: "<actionable recommendation>"',
192
201
  ' }',
193
202
  '}',
194
203
  '```'
195
204
  ]
196
- : [
197
- '```javascript',
198
- 'evidence: {',
199
- ' quality: {',
200
- ' composite: <number 0-10>,',
201
- ' coaching: "<actionable recommendation>",',
202
- ' marketEvidence: { score: <number>, rationale: "<string>" }, // flat, NO "dimensions" wrapper',
203
- ' unitEconomics: { score: <number>, rationale: "<string>" },',
204
- ' // add other sub-scores as direct properties',
205
- ' }',
206
- '}',
207
- '```'
208
- ];
205
+ : isCustomerDev
206
+ ? [
207
+ '```javascript',
208
+ 'evidence: {',
209
+ ' quality: {',
210
+ ' composite: <number 0-10>,',
211
+ ' participant: { fit: <number 1-10>, urgency: <number 1-10>, authority: <number 1-10> },',
212
+ ' evidence: {',
213
+ ' quoteSpecificityAvg: <number 1-10>',
214
+ ' // other fields are allowed and encouraged but not required',
215
+ ' },',
216
+ ' coaching: "<actionable recommendation>",',
217
+ ' interviewee: "<name>",',
218
+ ' company: "<company>"',
219
+ ' }',
220
+ '}',
221
+ '```'
222
+ ]
223
+ : [
224
+ '```javascript',
225
+ 'evidence: {',
226
+ ' quality: {',
227
+ ' composite: <number 0-10>,',
228
+ ' coaching: "<actionable recommendation>",',
229
+ ' marketEvidence: { score: <number>, rationale: "<string>" }, // flat, NO "dimensions" wrapper',
230
+ ' unitEconomics: { score: <number>, rationale: "<string>" },',
231
+ ' // add other sub-scores as direct properties',
232
+ ' }',
233
+ '}',
234
+ '```'
235
+ ];
209
236
  const importantNote = [
210
237
  '',
211
238
  '> [!IMPORTANT]',
@@ -1689,12 +1689,21 @@ class FraimLocalMCPServer {
1689
1689
  return await this.finalizeLocalToolTextResponse(request, requestSessionId, requestId, rejection);
1690
1690
  }
1691
1691
  // Valid payload. Emit to the remote asynchronously.
1692
+ if (!this.repoInfo) {
1693
+ this.detectRepoInfo();
1694
+ }
1692
1695
  this.emitQualityScoreToRemote({
1693
1696
  jobName: args.jobName,
1694
1697
  jobId: args.jobId,
1695
1698
  sessionId: requestSessionId || args.sessionId || 'unknown',
1696
1699
  quality: args.evidence.quality,
1697
- artifactPath: args.evidence.quality.artifactPath
1700
+ artifactPath: args.evidence.quality.artifactPath,
1701
+ repoIdentifier: args.evidence?.reviewContext?.repoIdentifier || this.repoInfo?.url,
1702
+ reviewContext: {
1703
+ ...(args.evidence?.reviewContext || {}),
1704
+ repoIdentifier: args.evidence?.reviewContext?.repoIdentifier || this.repoInfo?.url,
1705
+ branchRef: args.evidence?.reviewContext?.branchRef || this.repoInfo?.branch
1706
+ }
1698
1707
  }).catch((err) => {
1699
1708
  // Best-effort: log but never fail the user-facing response.
1700
1709
  this.log(`⚠️ Quality score emission failed: ${err?.message || err}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.123",
3
+ "version": "2.0.124",
4
4
  "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
5
  "main": "index.js",
6
6
  "bin": {