@productbrain/mcp 0.0.1-beta.3391 → 0.0.1-beta.3399

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.
@@ -11756,11 +11756,87 @@ async function handleAuditRun(entryId, phase) {
11756
11756
  };
11757
11757
  }
11758
11758
 
11759
+ // src/tools/spine-check.ts
11760
+ async function handleSpineCheck(story, refresh = false) {
11761
+ const scheduleResult = await kernelMutation(
11762
+ "quality.scheduleSpineCheck",
11763
+ { includeStory: story, refresh }
11764
+ );
11765
+ const row = await kernelQuery("quality.getLatestSpineVerdict", {});
11766
+ const stillRunning = scheduleResult.scheduled;
11767
+ if (!row) {
11768
+ const text = stillRunning ? "Spine check started \u2014 no prior result exists yet. It runs asynchronously (LLM calls can take 25s+ each); re-run this in a few seconds to read the result." : "No spine check has been run yet, and none is currently running. Pass refresh:true to start one.";
11769
+ return {
11770
+ content: [{ type: "text", text }],
11771
+ structuredContent: success(text, { hasVerdict: false, checkStarted: stillRunning })
11772
+ };
11773
+ }
11774
+ const { verdict } = row;
11775
+ if (verdict.noSpineEntries) {
11776
+ return {
11777
+ content: [{ type: "text", text: "No spine entries yet \u2014 nothing to check." }],
11778
+ structuredContent: success("No spine entries yet.", { hasVerdict: true, noSpineEntries: true })
11779
+ };
11780
+ }
11781
+ const lines = ["## Spine Check"];
11782
+ if (stillRunning) {
11783
+ lines.push(`> A new check was just triggered and is still running asynchronously \u2014 the result below is from the PREVIOUS run (${new Date(row.createdAt).toISOString()}). Re-run in a few seconds for the fresh result.`, "");
11784
+ } else if (scheduleResult.reason === "already-in-flight") {
11785
+ lines.push(`> A check is already running (started earlier, not by this call) \u2014 showing the best available result (${new Date(row.createdAt).toISOString()}). Re-run in a few seconds.`, "");
11786
+ } else if (row.stale) {
11787
+ lines.push(`> STALE \u2014 ${row.staleMemberIds.length} member entry(ies) changed since this was computed. Pass refresh:true to recompute.`, "");
11788
+ }
11789
+ const s = verdict.structural;
11790
+ lines.push(`Structural completeness: ${s.passed ? "PASS" : "FAIL"}`);
11791
+ if (s.missing.length > 0) lines.push(`Missing: ${s.missing.join(", ")}`);
11792
+ if (s.duplicateSingletons.purpose) lines.push(`Duplicate purpose singletons (which is canonical?): ${s.duplicateSingletons.purpose.join(", ")}`);
11793
+ if (s.duplicateSingletons.vision) lines.push(`Duplicate vision singletons (which is canonical?): ${s.duplicateSingletons.vision.join(", ")}`);
11794
+ if (s.unconnectedStrategyIds.length > 0) lines.push(`Unconnected strategies (no informs edge to any vision): ${s.unconnectedStrategyIds.join(", ")}`);
11795
+ if ((s.strategiesWithUnknownConnectivity ?? []).length > 0) {
11796
+ lines.push(`Connectivity unknown (relation set truncated \u2014 not confirmed disconnected): ${s.strategiesWithUnknownConnectivity.join(", ")}`);
11797
+ }
11798
+ if (s.visionConnectedToPurpose === false) lines.push("No vision informs any purpose.");
11799
+ if (s.visionPurposeConnectivityTruncated) lines.push("Vision\u2192purpose connectivity unknown (relation set truncated).");
11800
+ if (verdict.strategiesTruncated) lines.push("Strategy set truncated \u2014 not every strategy was graded this pass.");
11801
+ if (verdict.perStrategy.length > 0) {
11802
+ lines.push("", `Per-strategy refusal heuristic (${verdict.perStrategy.length}):`);
11803
+ for (const p of verdict.perStrategy) {
11804
+ const refusal = p.refusal.hasRefusalSignal ? "names an explicit alternative it declines" : "no explicit refusal language detected";
11805
+ const rm = p.rogerMartin ? `; Roger Martin test: ${p.rogerMartin.isStrategicChoice ? "genuine strategic choice" : "NOT a genuine strategic choice"}` : "; Roger Martin test: FAILED TO EVALUATE (timeout, budget limit, or malformed response) \u2014 not scored";
11806
+ lines.push(`- ${p.entryId}: ${refusal}${rm}`);
11807
+ }
11808
+ }
11809
+ if (verdict.story) {
11810
+ lines.push("", `One-story judgment [unvalidated, on-demand]: ${verdict.story.passed ? "PASS" : "FAIL"}`);
11811
+ if (!verdict.story.passed && verdict.story.failedCriterion) lines.push(`Failed criterion: ${verdict.story.failedCriterion}`);
11812
+ } else if (row.includesStoryPass) {
11813
+ lines.push("", "One-story judgment: requested, but the LLM pass did not return a usable result (timeout or parse failure). Re-run to retry.");
11814
+ } else {
11815
+ lines.push("", "One-story judgment: not run this pass \u2014 pass story=true to run it.");
11816
+ }
11817
+ return {
11818
+ content: [{ type: "text", text: lines.join("\n") }],
11819
+ structuredContent: success(
11820
+ `Spine check: structural ${s.passed ? "PASS" : "FAIL"}${verdict.story ? `, story ${verdict.story.passed ? "PASS" : "FAIL"} [unvalidated]` : ""}${stillRunning ? " (new run still in progress \u2014 showing previous result)" : ""}.`,
11821
+ {
11822
+ hasVerdict: true,
11823
+ noSpineEntries: false,
11824
+ structuralPassed: s.passed,
11825
+ storyPassed: verdict.story?.passed ?? null,
11826
+ stale: row.stale,
11827
+ stillRunning,
11828
+ scheduled: scheduleResult.scheduled,
11829
+ scheduleReason: scheduleResult.reason
11830
+ }
11831
+ )
11832
+ };
11833
+ }
11834
+
11759
11835
  // src/tools/quality.ts
11760
- var QUALITY_ACTIONS = ["check", "re-evaluate", "verify-chain", "audit"];
11836
+ var QUALITY_ACTIONS = ["check", "re-evaluate", "verify-chain", "audit", "spine-check"];
11761
11837
  var qualitySchema = z22.object({
11762
11838
  action: z22.enum(QUALITY_ACTIONS).describe(
11763
- "'check': read the entry's server quality verdict (tier + criteria). 're-evaluate': trigger fresh evaluation. 'verify-chain': verify entries against the codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. 'audit': hygiene audit for a bet entry \u2014 absorbs `audit`."
11839
+ "'check': read the entry's server quality verdict (tier + criteria). 're-evaluate': trigger fresh evaluation. 'verify-chain': verify entries against the codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. 'audit': hygiene audit for a bet entry \u2014 absorbs `audit`. 'spine-check': structural completeness of the purpose+vision+strategy spine \u2014 story:true also runs the on-demand one-story judgment."
11764
11840
  ),
11765
11841
  entryId: z22.string().max(200).optional().describe("For 'check'/'re-evaluate'/'audit': entry ID, e.g. 'TEN-graph-db', '<PREFIX>-<n>'."),
11766
11842
  context: z22.enum(["capture", "commit", "review"]).default("review").optional().describe("For re-evaluate: evaluation context"),
@@ -11768,6 +11844,10 @@ var qualitySchema = z22.object({
11768
11844
  mode: z22.enum(["report", "fix"]).optional().describe("For 'verify-chain': 'report' = read-only trust report (default). 'fix' = also update drifted codeMapping statuses."),
11769
11845
  phase: z22.enum(["shaping", "handoff"]).optional().describe(
11770
11846
  "For 'audit': 'shaping' checks shaping-phase fields only (default). 'handoff' checks all required fields including buildContract/buildSequence/exclusions/risks."
11847
+ ),
11848
+ story: z22.boolean().optional().describe("For 'spine-check': also run the on-demand one-story LLM judgment (never scheduled; result labelled [unvalidated])."),
11849
+ refresh: z22.boolean().optional().describe(
11850
+ "For 'spine-check': force a new run even if a fresh verdict already exists. Default false \u2014 a plain call NEVER re-triggers work just to read the last result; it auto-schedules only when no run is already in flight AND the stored verdict is absent/stale/missing the requested story pass."
11771
11851
  )
11772
11852
  });
11773
11853
  var qualityCheckVariant = z22.object({ action: z22.literal("check"), entryId: z22.string().max(200) });
@@ -11786,17 +11866,24 @@ var qualityAuditVariant = z22.object({
11786
11866
  entryId: z22.string().max(200),
11787
11867
  phase: z22.enum(["shaping", "handoff"]).optional().default("shaping")
11788
11868
  });
11869
+ var qualitySpineCheckVariant = z22.object({
11870
+ action: z22.literal("spine-check"),
11871
+ story: z22.boolean().optional().default(false),
11872
+ refresh: z22.boolean().optional().default(false)
11873
+ });
11789
11874
  var qualityActionUnion = z22.discriminatedUnion("action", [
11790
11875
  qualityCheckVariant,
11791
11876
  qualityReEvaluateVariant,
11792
11877
  qualityVerifyChainVariant,
11793
- qualityAuditVariant
11878
+ qualityAuditVariant,
11879
+ qualitySpineCheckVariant
11794
11880
  ]);
11795
11881
  var QUALITY_ACTION_SPECS = {
11796
11882
  check: { params: ["entryId"], description: "entryId is required." },
11797
11883
  "re-evaluate": { params: ["entryId", "context"], description: "entryId is required." },
11798
11884
  "verify-chain": { params: ["collection", "mode"], description: "All params optional; collection defaults to 'glossary'." },
11799
- audit: { params: ["entryId", "phase"], description: "entryId is required." }
11885
+ audit: { params: ["entryId", "phase"], description: "entryId is required." },
11886
+ "spine-check": { params: ["story", "refresh"], description: "All params optional; story and refresh default to false \u2014 a plain call never re-triggers work, it just reads back the latest verdict (auto-refreshing only when stale/absent)." }
11800
11887
  };
11801
11888
  var qualityCheckOutputSchema = z22.object({
11802
11889
  entryId: z22.string(),
@@ -11820,18 +11907,26 @@ var qualityReevaluateOutputSchema = z22.object({
11820
11907
  maxScore: z22.number(),
11821
11908
  improved: z22.boolean()
11822
11909
  });
11910
+ var qualitySpineCheckOutputSchema = z22.object({
11911
+ hasVerdict: z22.boolean(),
11912
+ noSpineEntries: z22.boolean().optional(),
11913
+ structuralPassed: z22.boolean().optional(),
11914
+ storyPassed: z22.boolean().nullable().optional(),
11915
+ stale: z22.boolean().optional()
11916
+ });
11823
11917
  function registerQualityTools(server) {
11824
11918
  const qualityHandlers = {
11825
11919
  check: (data) => handleCheck(data.entryId),
11826
11920
  "re-evaluate": (data) => handleReEvaluate(data.entryId, data.context ?? "review"),
11827
11921
  "verify-chain": (data) => handleVerifyChain(server, { collection: data.collection ?? "glossary", mode: data.mode ?? "report" }),
11828
- audit: (data) => handleAuditRun(data.entryId, data.phase ?? "shaping")
11922
+ audit: (data) => handleAuditRun(data.entryId, data.phase ?? "shaping"),
11923
+ "spine-check": (data) => handleSpineCheck(data.story ?? false, data.refresh ?? false)
11829
11924
  };
11830
11925
  server.registerTool(
11831
11926
  "quality",
11832
11927
  {
11833
11928
  title: "Quality",
11834
- description: "Assess, re-evaluate, and verify entry/bet quality. Four actions:\n\n- **check**: Read the entry's server quality verdict (tier + criteria) \u2014 it does not re-score. Suggests links when the entry has no relations.\n- **re-evaluate**: Trigger a fresh quality evaluation. Returns heuristic verdict immediately, schedules LLM evaluation in background.\n- **verify-chain**: Verify knowledge entries against the actual codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. Produces a trust report with a trust score.\n- **audit**: Run hygiene audit on a bet entry (13/18 gates) \u2014 absorbs `audit`.",
11929
+ description: "Assess, re-evaluate, and verify entry/bet quality. Five actions:\n\n- **check**: Read the entry's server quality verdict (tier + criteria) \u2014 it does not re-score. Suggests links when the entry has no relations.\n- **re-evaluate**: Trigger a fresh quality evaluation. Returns heuristic verdict immediately, schedules LLM evaluation in background.\n- **verify-chain**: Verify knowledge entries against the actual codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. Produces a trust report with a trust score.\n- **audit**: Run hygiene audit on a bet entry (13/18 gates) \u2014 absorbs `audit`.\n- **spine-check**: Structural completeness of the purpose+vision+strategy spine \u2014 duplicate singletons, connectedness, per-strategy refusal heuristic; story:true also runs the on-demand one-story LLM judgment (never scheduled, result labelled [unvalidated]). Async: a plain call reads back the latest stored verdict and auto-schedules a new run ONLY when none is already in flight AND the stored one is absent/stale/missing the requested story pass \u2014 repeated polling never re-triggers work. Pass refresh:true to force a fresh run regardless.",
11835
11930
  inputSchema: qualitySchema,
11836
11931
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
11837
11932
  },
@@ -16309,4 +16404,4 @@ export {
16309
16404
  createProductBrainServer,
16310
16405
  initFeatureFlags
16311
16406
  };
16312
- //# sourceMappingURL=chunk-KFXZUDFK.js.map
16407
+ //# sourceMappingURL=chunk-CY3YQR26.js.map