@productbrain/mcp 0.0.1-beta.4895 → 0.0.1-beta.4900

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.
@@ -2007,10 +2007,8 @@ var captureSuccessOutputSchema = z5.object({
2007
2007
  entryId: z5.string(),
2008
2008
  collection: z5.string(),
2009
2009
  name: z5.string(),
2010
- // "draft_on_failure" is the status emitted when an auto-commit is REFUSED/failed (the entry
2011
- // stays a draft) — the same path that now also emits coherencyRefusal. The strict schema must
2012
- // accept it or a refused response fails to validate on `status` (codex review, completing the
2013
- // coherencyRefusal contract fix).
2010
+ // "draft_on_failure" is emitted when an auto-commit is REFUSED/failed (stays a draft) — the
2011
+ // same path that also emits coherencyRefusal; the strict schema must accept it or fail.
2014
2012
  status: z5.enum(["draft", "committed", "proposed", "draft_on_failure"]),
2015
2013
  // WP-480 S1: `qualityScore` (required, client N/10) deleted — a named, accepted
2016
2014
  // breaking output-contract change. The server verdict stays as `qualityVerdict`.
@@ -2033,16 +2031,15 @@ var captureSuccessOutputSchema = z5.object({
2033
2031
  status: z5.literal("proposal-pending")
2034
2032
  }).optional(),
2035
2033
  // WP-465 surface parity: structured coherency refusal when an auto-commit is gate-refused.
2036
- // Loose record (matching the qualityVerdict convention above) the CoherencyRefusal contract
2037
- // is authored in convex/lib/gates/coherencyControls.ts; re-declaring its shape here would create
2038
- // a second source of truth. Required because the schema is `.strict()` (an undeclared key throws).
2034
+ // Loose record the CoherencyRefusal contract is authored in
2035
+ // convex/lib/gates/coherencyControls.ts; re-declaring it here would be a second SSOT.
2039
2036
  coherencyRefusal: z5.record(z5.unknown()).optional(),
2040
- // WP-485 Slice 2b round 4 (Codex P2, FEAT-1370): the server's contradiction advisory
2041
- // (ContradictionAdvisory, declared below) is emitted into structuredContent at line ~2203
2042
- // but was missing here since this schema is `.strict()`, any real capture response
2043
- // carrying the advisory failed validation outright with `unrecognized_keys`. Loose record,
2044
- // same single-source-of-truth rationale as coherencyRefusal above.
2045
- contradictionAdvisory: z5.record(z5.unknown()).optional()
2037
+ // WP-485 Slice 2b round 4 (Codex P2, FEAT-1370): the server's contradiction advisory is emitted
2038
+ // into structuredContent but was missing here since this schema is `.strict()`, a real capture
2039
+ // response carrying it failed validation with `unrecognized_keys`. Loose record, same rationale as coherencyRefusal above.
2040
+ contradictionAdvisory: z5.record(z5.unknown()).optional(),
2041
+ fallbackApplied: z5.boolean().optional()
2042
+ // round 2 (PRRT_kwDORS4ils6cTNbF): classifier field above still names the original (pre-substitution) prediction
2046
2043
  }).strict();
2047
2044
  var captureClassifierOnlyOutputSchema = z5.object({
2048
2045
  classifier: captureClassifierSchema
@@ -2157,12 +2154,40 @@ async function handleCapture(server, { collection, name, description, context, e
2157
2154
  if (!resolvedCollection) {
2158
2155
  return buildClassifierUnknownResult();
2159
2156
  }
2160
- if (resolvedCollection === "chains" && !canonicalKey && !explicitCollectionProvided && classifierMeta?.candidates?.some(
2161
- (c) => c.collection === "work-packages"
2162
- )) {
2157
+ let canonicalKeyAutoInjected = false;
2158
+ if (resolvedCollection === "chains" && !canonicalKey && !explicitCollectionProvided && classifierMeta?.candidates?.some((c) => c.collection === "work-packages")) {
2163
2159
  canonicalKey = "work_package";
2160
+ canonicalKeyAutoInjected = true;
2164
2161
  }
2165
2162
  const tAfterClassify = Date.now();
2163
+ let fallbackApplied = false;
2164
+ let col = await kernelQuery("chain.getCollection", { slug: resolvedCollection });
2165
+ if (!col && !explicitCollectionProvided) {
2166
+ const allCollections = await getCollections();
2167
+ const fallback = allCollections.find((c) => c.slug === "insights") ?? allCollections.find((c) => c.stratum === "system");
2168
+ if (fallback && canonicalKey === "work_package" && !canonicalKeyAutoInjected && fallback.slug !== "chains") {
2169
+ const msg = `\`work_package\` entries must be in the \`chains\` collection, not \`${fallback.slug}\`. \`${resolvedCollection}\` doesn't exist here, so there's no chains-equivalent drawer to fall back into.`;
2170
+ return { content: [{ type: "text", text: msg }], structuredContent: failure("VALIDATION_FAILED", `canonicalKey 'work_package' requires 'chains'; fallback target '${fallback.slug}' doesn't qualify.`, msg) };
2171
+ }
2172
+ if (fallback) {
2173
+ resolvedCollection = fallback.slug;
2174
+ col = fallback;
2175
+ fallbackApplied = true;
2176
+ if (canonicalKeyAutoInjected) canonicalKey = void 0;
2177
+ }
2178
+ }
2179
+ if (!col) {
2180
+ const msg = `Collection \`${resolvedCollection}\` not found. Creating a collection is not self-serve \u2014 ask a platform admin to provision it, or enable the module ontology pack that owns it. Use \`collections action=list\` to see what exists.`;
2181
+ return {
2182
+ content: [{ type: "text", text: msg }],
2183
+ structuredContent: failure(
2184
+ "NOT_FOUND",
2185
+ `Collection '${resolvedCollection}' not found.`,
2186
+ msg,
2187
+ [{ tool: "collections", description: "List collections", parameters: { action: "list" } }]
2188
+ )
2189
+ };
2190
+ }
2166
2191
  if (entryId && resolvedCollection !== "business-rules" && resolvedCollection !== "standards") {
2167
2192
  return {
2168
2193
  content: [{
@@ -2181,32 +2206,6 @@ async function handleCapture(server, { collection, name, description, context, e
2181
2206
  };
2182
2207
  }
2183
2208
  const profile = await getProfile(resolvedCollection);
2184
- const col = await kernelQuery("chain.getCollection", { slug: resolvedCollection });
2185
- if (!col) {
2186
- const displayName2 = resolvedCollection.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2187
- return {
2188
- content: [{
2189
- type: "text",
2190
- text: `Collection \`${resolvedCollection}\` not found.
2191
-
2192
- **To create it**, run:
2193
- \`\`\`
2194
- collections action=create slug="${resolvedCollection}" name="${displayName2}" description="..."
2195
- \`\`\`
2196
-
2197
- Or use \`collections action=list\` to see available collections.`
2198
- }],
2199
- structuredContent: failure(
2200
- "NOT_FOUND",
2201
- `Collection '${resolvedCollection}' not found.`,
2202
- "Create the collection first, or use collections action=list to see available ones.",
2203
- [
2204
- { tool: "collections", description: "Create collection", parameters: { action: "create", slug: resolvedCollection, name: displayName2 } },
2205
- { tool: "collections", description: "List collections", parameters: { action: "list" } }
2206
- ]
2207
- )
2208
- };
2209
- }
2210
2209
  const skipAutoDiscoveryEarly = links && links.length > 0;
2211
2210
  const groundingStart = Date.now();
2212
2211
  const groundingPromise = !skipAutoDiscoveryEarly && (name || description) ? kernelQuery("chain.suggestLinksForCapture", {
@@ -2768,7 +2767,9 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
2768
2767
  if (classifierMeta) {
2769
2768
  lines.push("");
2770
2769
  lines.push("## Classification");
2771
- if (classifierMeta.abstained) {
2770
+ if (fallbackApplied) {
2771
+ lines.push(`Filed into \`${resolvedCollection}\` \u2014 the classifier's original pick was unavailable; routed here as a fallback, not a classifier decision.`);
2772
+ } else if (classifierMeta.abstained) {
2772
2773
  lines.push(`No classifier coverage for \`${resolvedCollection}\` \u2014 entry accepted as-is.`);
2773
2774
  } else if (classifierMeta.autoRouted) {
2774
2775
  lines.push(`Auto-routed to \`${resolvedCollection}\` (${classifierMeta.topConfidence}% confidence).`);
@@ -3047,6 +3048,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
3047
3048
  // output-contract change, pre-launch).
3048
3049
  qualityVerdict: verdictResult?.verdict ? { ...verdictResult.verdict, source: verdictResult.source ?? "heuristic" } : void 0,
3049
3050
  ...classifierMeta && { classifier: classifierMeta },
3051
+ ...fallbackApplied && { fallbackApplied: true },
3050
3052
  ...studioUrl && { studioUrl },
3051
3053
  ...entryWarnings.length > 0 && { warnings: entryWarnings },
3052
3054
  ...normalizationMeta && (Object.keys(normalizationMeta.remapped).length > 0 || normalizationMeta.rejected.length > 0) && {
@@ -16496,4 +16498,4 @@ export {
16496
16498
  createProductBrainServer,
16497
16499
  initFeatureFlags
16498
16500
  };
16499
- //# sourceMappingURL=chunk-UYEGGXDV.js.map
16501
+ //# sourceMappingURL=chunk-LGEW725M.js.map