@productbrain/mcp 0.0.1-beta.8754 → 0.0.1-beta.8765

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.
@@ -1171,23 +1171,29 @@ function verdictQualityPhrase(result2) {
1171
1171
  if (!v || criteria.length === 0) {
1172
1172
  return "Quality: not evaluated (no rubric for this type).";
1173
1173
  }
1174
- const total = criteria.length;
1175
- const passed = criteria.filter((c) => c.passed).length;
1176
- return v.passed ? `Quality: all ${total} rubric criteria pass.` : `Quality: ${passed}/${total} rubric criteria pass \u2014 see the coaching above.`;
1174
+ const gating = criteria.filter((c) => c.gating !== false);
1175
+ const total = gating.length;
1176
+ const passed = gating.filter((c) => c.passed).length;
1177
+ return v.passed ? `Quality: all ${total} gating criteria pass.` : `Quality: ${passed}/${total} gating criteria pass \u2014 see the coaching above.`;
1178
+ }
1179
+ function hintLines(criteria) {
1180
+ const hints = (criteria ?? []).filter((c) => c.gating === false && !c.passed);
1181
+ return hints.length === 0 ? [] : ["", ...hints.map((c) => `hint (does not block): ${c.id} \u2014 ${c.hint}`)];
1177
1182
  }
1178
1183
  function formatRubricCoaching(result2) {
1179
1184
  const { verdict, rogerMartin } = result2;
1180
1185
  if (!verdict || verdict.criteria.length === 0) return "";
1181
1186
  const lines = ["## Semantic Quality"];
1182
- const failed = (verdict.criteria ?? []).filter((c) => !c.passed);
1183
- const total = verdict.criteria?.length ?? 0;
1187
+ const gating = (verdict.criteria ?? []).filter((c) => c.gating !== false);
1188
+ const failed = gating.filter((c) => !c.passed);
1189
+ const total = gating.length;
1184
1190
  const passedCount = total - failed.length;
1185
1191
  if (verdict.passed) {
1186
- lines.push(`All ${total} rubric criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier).`);
1192
+ lines.push(`All ${total} gating criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier).`);
1187
1193
  } else {
1188
- lines.push(`${passedCount}/${total} criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier)`);
1194
+ lines.push(`${passedCount}/${total} gating criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier)`);
1189
1195
  lines.push("");
1190
- for (const c of verdict.criteria) {
1196
+ for (const c of gating) {
1191
1197
  const icon = c.passed ? "[x]" : "[ ]";
1192
1198
  const extra = c.passed ? "" : ` \u2014 ${c.hint}`;
1193
1199
  lines.push(`${icon} ${c.id}${extra}`);
@@ -1198,6 +1204,7 @@ function formatRubricCoaching(result2) {
1198
1204
  lines.push(`_Question to consider:_ ${verdict.weakest.questionTemplate}`);
1199
1205
  }
1200
1206
  }
1207
+ lines.push(...hintLines(verdict.criteria));
1201
1208
  if (rogerMartin) {
1202
1209
  lines.push("");
1203
1210
  lines.push("### Roger Martin Test");
@@ -1215,17 +1222,18 @@ function formatRubricCoaching(result2) {
1215
1222
  function formatRubricVerdictSection(verdict) {
1216
1223
  if (!verdict || !verdict.criteria || verdict.criteria.length === 0) return "";
1217
1224
  const lines = ["## Semantic Quality"];
1218
- const failed = verdict.criteria.filter((c) => !c.passed);
1219
- const total = verdict.criteria.length;
1225
+ const gating = verdict.criteria.filter((c) => c.gating !== false);
1226
+ const failed = gating.filter((c) => !c.passed);
1227
+ const total = gating.length;
1220
1228
  const passedCount = total - failed.length;
1221
1229
  const durationSuffix = verdict.llmDurationMs ? ` in ${(verdict.llmDurationMs / 1e3).toFixed(1)}s` : "";
1222
1230
  const statusNote = verdict.llmStatus === "pending" ? " \u2014 LLM evaluation in progress..." : verdict.llmStatus === "failed" ? ` \u2014 LLM evaluation failed${verdict.llmError ? `: ${verdict.llmError}` : ""}, showing heuristic results` : verdict.source === "llm" && durationSuffix ? ` \u2014 evaluated${durationSuffix}` : "";
1223
1231
  if (failed.length === 0) {
1224
- lines.push(`All ${total} rubric criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier, ${verdict.source} evaluation).${statusNote}`);
1232
+ lines.push(`All ${total} gating criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier, ${verdict.source} evaluation).${statusNote}`);
1225
1233
  } else {
1226
- lines.push(`${passedCount}/${total} criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier, ${verdict.source} evaluation)${statusNote}`);
1234
+ lines.push(`${passedCount}/${total} gating criteria pass for \`${verdict.canonicalKey}\` (${verdict.tier} tier, ${verdict.source} evaluation)${statusNote}`);
1227
1235
  lines.push("");
1228
- for (const c of verdict.criteria) {
1236
+ for (const c of gating) {
1229
1237
  const icon = c.passed ? "[x]" : "[ ]";
1230
1238
  const extra = c.passed ? "" : ` \u2014 ${c.hint}`;
1231
1239
  lines.push(`${icon} ${c.id}${extra}`);
@@ -1235,6 +1243,7 @@ function formatRubricVerdictSection(verdict) {
1235
1243
  lines.push(`**Top improvement:** ${verdict.weakest.hint}`);
1236
1244
  }
1237
1245
  }
1246
+ lines.push(...hintLines(verdict.criteria));
1238
1247
  if (verdict.rogerMartin) {
1239
1248
  const rm = verdict.rogerMartin;
1240
1249
  lines.push("");
@@ -2302,7 +2311,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
2302
2311
  try {
2303
2312
  const wsForTracking = await getWorkspaceContext();
2304
2313
  const v = verdictResult.verdict;
2305
- const failedCount = (v.criteria ?? []).filter((c) => !c.passed).length;
2314
+ const failedCount = (v.criteria ?? []).filter((c) => !c.passed && c.gating !== false).length;
2306
2315
  trackQualityVerdict(wsForTracking.workspaceId, {
2307
2316
  entry_id: finalEntryId,
2308
2317
  entry_type: v.canonicalKey ?? resolvedCollection,
@@ -2310,7 +2319,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
2310
2319
  context: "capture",
2311
2320
  passed: v.passed,
2312
2321
  source: verdictResult.source ?? "heuristic",
2313
- criteria_total: v.criteria?.length ?? 0,
2322
+ criteria_total: (v.criteria ?? []).filter((c) => c.gating !== false).length,
2314
2323
  criteria_failed: failedCount,
2315
2324
  llm_scheduled: v.tier !== "passive",
2316
2325
  methodology_version: v.methodologyVersion
@@ -2999,7 +3008,7 @@ No DB writes \u2014 call without \`preview:true\` to accept for real.` }],
2999
3008
  if (coachingResult?.verdict) {
3000
3009
  try {
3001
3010
  const v = coachingResult.verdict;
3002
- const failedCount = (v.criteria ?? []).filter((c) => !c.passed).length;
3011
+ const failedCount = (v.criteria ?? []).filter((c) => !c.passed && c.gating !== false).length;
3003
3012
  trackQualityVerdict(wsCtx.workspaceId, {
3004
3013
  entry_id: entryId,
3005
3014
  entry_type: v.canonicalKey ?? entry.canonicalKey ?? "",
@@ -3007,7 +3016,7 @@ No DB writes \u2014 call without \`preview:true\` to accept for real.` }],
3007
3016
  context: "commit",
3008
3017
  passed: v.passed,
3009
3018
  source: coachingResult.source ?? "heuristic",
3010
- criteria_total: v.criteria?.length ?? 0,
3019
+ criteria_total: (v.criteria ?? []).filter((c) => c.gating !== false).length,
3011
3020
  criteria_failed: failedCount,
3012
3021
  llm_scheduled: v.tier !== "passive",
3013
3022
  methodology_version: v.methodologyVersion
@@ -10531,7 +10540,9 @@ var qualityCheckOutputSchema = z21.object({
10531
10540
  criteria: z21.array(z21.object({
10532
10541
  id: z21.string(),
10533
10542
  passed: z21.boolean(),
10534
- hint: z21.string().optional()
10543
+ hint: z21.string().optional(),
10544
+ /** TASK-588 / DEC-1908: false = a coaching hint; an unmet one never fails the verdict. */
10545
+ gating: z21.literal(false).optional()
10535
10546
  }))
10536
10547
  });
10537
10548
  var qualityReevaluateOutputSchema = z21.object({
@@ -10636,9 +10647,10 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10636
10647
  }
10637
10648
  const criteria = verdict.criteria ?? [];
10638
10649
  const lines = [`# Quality Check: ${entryId}`, ""];
10650
+ const staleWhy = res.staleReason === "methodology_changed_since_judged" ? "the rubric changed since it was judged" : "the entry was edited since it was judged";
10639
10651
  if (res.stale) {
10640
10652
  lines.push(
10641
- `> STALE \u2014 this verdict was judged against an earlier version of \`${entryId}\`, which has been edited since. Run \`quality action=re-evaluate entryId="${entryId}"\` for a current verdict.`,
10653
+ `> STALE \u2014 this verdict for \`${entryId}\` no longer counts: ${staleWhy}. Run \`quality action=re-evaluate entryId="${entryId}"\` for a current verdict.`,
10642
10654
  ""
10643
10655
  );
10644
10656
  }
@@ -10660,10 +10672,11 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10660
10672
  });
10661
10673
  } catch {
10662
10674
  }
10663
- const passedCount = criteria.filter((c) => c.passed).length;
10675
+ const gatingCriteria = criteria.filter((c) => c.gating !== false);
10676
+ const passedCount = gatingCriteria.filter((c) => c.passed).length;
10664
10677
  return successResult(
10665
10678
  lines.join("\n"),
10666
- `Quality check for ${entryId}: ${verdict.tier ?? "unknown"} tier, ${passedCount}/${criteria.length} criteria pass.` + (res.stale ? " STALE \u2014 the entry was edited after this verdict; re-evaluate for a current one." : ""),
10679
+ `Quality check for ${entryId}: ${verdict.tier ?? "unknown"} tier, ${passedCount}/${gatingCriteria.length} gating criteria pass.` + (res.stale ? ` STALE \u2014 ${staleWhy}; re-evaluate for a current one.` : ""),
10667
10680
  {
10668
10681
  entryId,
10669
10682
  entryFound: true,
@@ -10671,7 +10684,7 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10671
10684
  stale: res.stale ?? false,
10672
10685
  tier: verdict.tier,
10673
10686
  passed: verdict.passed,
10674
- criteria: criteria.map((c) => ({ id: c.id, passed: c.passed, hint: c.hint }))
10687
+ criteria: criteria.map((c) => ({ id: c.id, passed: c.passed, hint: c.hint, ...c.gating === false ? { gating: false } : {} }))
10675
10688
  },
10676
10689
  [
10677
10690
  { tool: "relations", description: "Suggest connections", parameters: { action: "suggest", entryId } }
@@ -10710,14 +10723,15 @@ async function handleReEvaluate(entryId, context) {
10710
10723
  context,
10711
10724
  passed: result2.verdict.passed ?? false,
10712
10725
  source: "heuristic",
10713
- criteria_total: result2.verdict.criteria?.length ?? 0,
10714
- criteria_failed: result2.verdict.criteria?.filter((c) => !c.passed).length ?? 0,
10726
+ // TASK-588 / DEC-1908: totals and fails count gating criteria; a gating:false one is a hint.
10727
+ criteria_total: result2.verdict.criteria?.filter((c) => c.gating !== false).length ?? 0,
10728
+ criteria_failed: result2.verdict.criteria?.filter((c) => !c.passed && c.gating !== false).length ?? 0,
10715
10729
  llm_scheduled: llmScheduled,
10716
10730
  methodology_version: result2.verdict.methodologyVersion
10717
10731
  });
10718
10732
  } catch {
10719
10733
  }
10720
- const criteria = result2.verdict.criteria ?? [];
10734
+ const criteria = (result2.verdict.criteria ?? []).filter((c) => c.gating !== false);
10721
10735
  const reEvalScore = criteria.filter((c) => c.passed).length;
10722
10736
  const reEvalMaxScore = criteria.length;
10723
10737
  return {
@@ -14143,4 +14157,4 @@ export {
14143
14157
  createProductBrainServer,
14144
14158
  initFeatureFlags
14145
14159
  };
14146
- //# sourceMappingURL=chunk-4GJ3KH6Q.js.map
14160
+ //# sourceMappingURL=chunk-ZY4ORNVP.js.map