@productbrain/mcp 0.0.1-beta.10847 → 0.0.1-beta.10865
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.
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
|
|
51
51
|
// src/server.ts
|
|
52
52
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
53
|
-
import { z as
|
|
53
|
+
import { z as z35 } from "zod/v3";
|
|
54
54
|
|
|
55
55
|
// src/lib/mcpGatewayClientMessages.ts
|
|
56
56
|
function agentSessionNotActiveMessage(status2) {
|
|
@@ -1016,29 +1016,29 @@ function verdictQualityPhrase(result2) {
|
|
|
1016
1016
|
return v.passed ? `Quality: all ${total} rubric criteria pass.` : `Quality: ${passed}/${total} rubric criteria pass \u2014 see the coaching above.`;
|
|
1017
1017
|
}
|
|
1018
1018
|
function formatRubricCoaching(result2) {
|
|
1019
|
-
const { verdict, rogerMartin } = result2;
|
|
1020
|
-
if (!
|
|
1019
|
+
const { verdict: verdict2, rogerMartin } = result2;
|
|
1020
|
+
if (!verdict2 || verdict2.criteria.length === 0) return "";
|
|
1021
1021
|
const lines = ["## Semantic Quality"];
|
|
1022
|
-
const failed = (
|
|
1023
|
-
const total =
|
|
1022
|
+
const failed = (verdict2.criteria ?? []).filter((c) => !c.passed);
|
|
1023
|
+
const total = verdict2.criteria?.length ?? 0;
|
|
1024
1024
|
const passedCount = total - failed.length;
|
|
1025
|
-
const checklist = renderChecklist(
|
|
1025
|
+
const checklist = renderChecklist(verdict2.canonicalKey, verdict2.criteria, result2.source !== "llm");
|
|
1026
1026
|
if (checklist) {
|
|
1027
|
-
lines.push(`Quality checklist for \`${
|
|
1028
|
-
} else if (
|
|
1029
|
-
lines.push(`All ${total} rubric criteria pass for \`${
|
|
1027
|
+
lines.push(`Quality checklist for \`${verdict2.canonicalKey}\`:`, "", ...checklist);
|
|
1028
|
+
} else if (verdict2.passed) {
|
|
1029
|
+
lines.push(`All ${total} rubric criteria pass for \`${verdict2.canonicalKey}\` (${verdict2.tier} tier).`);
|
|
1030
1030
|
} else {
|
|
1031
|
-
lines.push(`${passedCount}/${total} criteria pass for \`${
|
|
1031
|
+
lines.push(`${passedCount}/${total} criteria pass for \`${verdict2.canonicalKey}\` (${verdict2.tier} tier)`);
|
|
1032
1032
|
lines.push("");
|
|
1033
|
-
for (const c of
|
|
1033
|
+
for (const c of verdict2.criteria) {
|
|
1034
1034
|
const icon = c.passed ? "[x]" : "[ ]";
|
|
1035
1035
|
const extra = c.passed ? "" : ` \u2014 ${c.hint}`;
|
|
1036
1036
|
lines.push(`${icon} ${c.id}${extra}`);
|
|
1037
1037
|
}
|
|
1038
|
-
if (
|
|
1038
|
+
if (verdict2.weakest) {
|
|
1039
1039
|
lines.push("");
|
|
1040
|
-
lines.push(`**Coaching hint:** ${
|
|
1041
|
-
lines.push(`_Question to consider:_ ${
|
|
1040
|
+
lines.push(`**Coaching hint:** ${verdict2.weakest.hint}`);
|
|
1041
|
+
lines.push(`_Question to consider:_ ${verdict2.weakest.questionTemplate}`);
|
|
1042
1042
|
}
|
|
1043
1043
|
}
|
|
1044
1044
|
if (rogerMartin) {
|
|
@@ -1055,34 +1055,34 @@ function formatRubricCoaching(result2) {
|
|
|
1055
1055
|
}
|
|
1056
1056
|
return lines.join("\n");
|
|
1057
1057
|
}
|
|
1058
|
-
function formatRubricVerdictSection(
|
|
1059
|
-
if (!
|
|
1058
|
+
function formatRubricVerdictSection(verdict2) {
|
|
1059
|
+
if (!verdict2 || !verdict2.criteria || verdict2.criteria.length === 0) return "";
|
|
1060
1060
|
const lines = ["## Semantic Quality"];
|
|
1061
|
-
const failed =
|
|
1062
|
-
const total =
|
|
1061
|
+
const failed = verdict2.criteria.filter((c) => !c.passed);
|
|
1062
|
+
const total = verdict2.criteria.length;
|
|
1063
1063
|
const passedCount = total - failed.length;
|
|
1064
|
-
const durationSuffix =
|
|
1065
|
-
const statusNote =
|
|
1066
|
-
const checklist = renderChecklist(
|
|
1064
|
+
const durationSuffix = verdict2.llmDurationMs ? ` in ${(verdict2.llmDurationMs / 1e3).toFixed(1)}s` : "";
|
|
1065
|
+
const statusNote = verdict2.llmStatus === "pending" ? " \u2014 LLM evaluation in progress..." : verdict2.llmStatus === "failed" ? ` \u2014 LLM evaluation failed${verdict2.llmError ? `: ${verdict2.llmError}` : ""}, showing heuristic results` : verdict2.source === "llm" && durationSuffix ? ` \u2014 evaluated${durationSuffix}` : "";
|
|
1066
|
+
const checklist = renderChecklist(verdict2.canonicalKey, verdict2.criteria, verdict2.llmStatus === "pending");
|
|
1067
1067
|
if (checklist) {
|
|
1068
|
-
lines.push(`Quality checklist for \`${
|
|
1068
|
+
lines.push(`Quality checklist for \`${verdict2.canonicalKey}\` (${verdict2.source} evaluation)${statusNote}:`, "", ...checklist);
|
|
1069
1069
|
} else if (failed.length === 0) {
|
|
1070
|
-
lines.push(`All ${total} rubric criteria pass for \`${
|
|
1070
|
+
lines.push(`All ${total} rubric criteria pass for \`${verdict2.canonicalKey}\` (${verdict2.tier} tier, ${verdict2.source} evaluation).${statusNote}`);
|
|
1071
1071
|
} else {
|
|
1072
|
-
lines.push(`${passedCount}/${total} criteria pass for \`${
|
|
1072
|
+
lines.push(`${passedCount}/${total} criteria pass for \`${verdict2.canonicalKey}\` (${verdict2.tier} tier, ${verdict2.source} evaluation)${statusNote}`);
|
|
1073
1073
|
lines.push("");
|
|
1074
|
-
for (const c of
|
|
1074
|
+
for (const c of verdict2.criteria) {
|
|
1075
1075
|
const icon = c.passed ? "[x]" : "[ ]";
|
|
1076
1076
|
const extra = c.passed ? "" : ` \u2014 ${c.hint}`;
|
|
1077
1077
|
lines.push(`${icon} ${c.id}${extra}`);
|
|
1078
1078
|
}
|
|
1079
|
-
if (
|
|
1079
|
+
if (verdict2.weakest) {
|
|
1080
1080
|
lines.push("");
|
|
1081
|
-
lines.push(`**Top improvement:** ${
|
|
1081
|
+
lines.push(`**Top improvement:** ${verdict2.weakest.hint}`);
|
|
1082
1082
|
}
|
|
1083
1083
|
}
|
|
1084
|
-
if (
|
|
1085
|
-
const rm =
|
|
1084
|
+
if (verdict2.rogerMartin) {
|
|
1085
|
+
const rm = verdict2.rogerMartin;
|
|
1086
1086
|
lines.push("");
|
|
1087
1087
|
lines.push("### Roger Martin Test");
|
|
1088
1088
|
if (rm.isStrategicChoice) {
|
|
@@ -2795,7 +2795,7 @@ function checkSchemaRef(ref, schema) {
|
|
|
2795
2795
|
}
|
|
2796
2796
|
return { result: "unverifiable", reason: "could not parse schema reference" };
|
|
2797
2797
|
}
|
|
2798
|
-
function formatTrustReport(collection, entryCount, mappings, refs, fixes,
|
|
2798
|
+
function formatTrustReport(collection, entryCount, mappings, refs, fixes, mode2, schemaTableCount, projectRoot) {
|
|
2799
2799
|
const verified = mappings.filter((c) => c.result === "verified").length;
|
|
2800
2800
|
const drifted = mappings.filter((c) => c.result === "drifted").length;
|
|
2801
2801
|
const unverifiable = mappings.filter((c) => c.result === "unverifiable").length;
|
|
@@ -2849,14 +2849,14 @@ function formatTrustReport(collection, entryCount, mappings, refs, fixes, mode,
|
|
|
2849
2849
|
lines.push("");
|
|
2850
2850
|
}
|
|
2851
2851
|
lines.push(`## Trust Score: ${trustScore}% (${totalPassed} of ${totalChecks} checks passed)`);
|
|
2852
|
-
if (
|
|
2852
|
+
if (mode2 === "fix" && fixes.length > 0) {
|
|
2853
2853
|
lines.push(
|
|
2854
2854
|
"",
|
|
2855
2855
|
"## Applied Fixes",
|
|
2856
2856
|
`Updated codeMapping status from \`aligned\` \u2192 \`drifted\` on ${fixes.length} entr${fixes.length === 1 ? "y" : "ies"}:`
|
|
2857
2857
|
);
|
|
2858
2858
|
for (const eid of fixes) lines.push(`- ${eid}`);
|
|
2859
|
-
} else if (
|
|
2859
|
+
} else if (mode2 === "report" && drifted > 0) {
|
|
2860
2860
|
const fixable = mappings.filter((c) => c.result === "drifted" && c.currentStatus === "aligned").length;
|
|
2861
2861
|
if (fixable > 0) {
|
|
2862
2862
|
lines.push("", `_${fixable} mapping(s) marked "aligned" but actually drifted. Run with mode="fix" to update._`);
|
|
@@ -2872,7 +2872,7 @@ var verifySchema = z7.object({
|
|
|
2872
2872
|
var verifyEntrySchema = z7.object({
|
|
2873
2873
|
entryId: z7.string().max(200).describe("Human entry ID (e.g. '<PREFIX>-<n>') to mark as verified")
|
|
2874
2874
|
});
|
|
2875
|
-
async function handleVerifyChain(server, { collection, mode }) {
|
|
2875
|
+
async function handleVerifyChain(server, { collection, mode: mode2 }) {
|
|
2876
2876
|
const projectRoot = resolveProjectRoot();
|
|
2877
2877
|
if (!projectRoot) {
|
|
2878
2878
|
return failureResult(
|
|
@@ -2973,7 +2973,7 @@ async function handleVerifyChain(server, { collection, mode }) {
|
|
|
2973
2973
|
const fixes = [];
|
|
2974
2974
|
const fixUpdateWarnings = [];
|
|
2975
2975
|
const fixRefusals = [];
|
|
2976
|
-
if (
|
|
2976
|
+
if (mode2 === "fix") {
|
|
2977
2977
|
const driftedByEntry = /* @__PURE__ */ new Map();
|
|
2978
2978
|
for (const mc of mappingChecks) {
|
|
2979
2979
|
if (mc.result === "drifted" && mc.currentStatus === "aligned") {
|
|
@@ -3025,7 +3025,7 @@ async function handleVerifyChain(server, { collection, mode }) {
|
|
|
3025
3025
|
mappingChecks,
|
|
3026
3026
|
refChecks,
|
|
3027
3027
|
fixes,
|
|
3028
|
-
|
|
3028
|
+
mode2,
|
|
3029
3029
|
schema.size,
|
|
3030
3030
|
projectRoot
|
|
3031
3031
|
);
|
|
@@ -3067,7 +3067,7 @@ async function handleVerifyChain(server, { collection, mode }) {
|
|
|
3067
3067
|
refsBroken,
|
|
3068
3068
|
fixesApplied: fixes.length,
|
|
3069
3069
|
...fixRefusals.length > 0 ? { fixesRefused: fixRefusals.length, coherencyRefusals: fixRefusals } : {},
|
|
3070
|
-
mode
|
|
3070
|
+
mode: mode2
|
|
3071
3071
|
}
|
|
3072
3072
|
)
|
|
3073
3073
|
};
|
|
@@ -4931,11 +4931,11 @@ function registerContextTools(server) {
|
|
|
4931
4931
|
})
|
|
4932
4932
|
);
|
|
4933
4933
|
}
|
|
4934
|
-
async function handleGather(server, entryId, task,
|
|
4934
|
+
async function handleGather(server, entryId, task, mode2, maxHops, maxResults, strategy = "keyword") {
|
|
4935
4935
|
if (!entryId && !task) {
|
|
4936
4936
|
return validationResult("Provide either entryId (graph traversal) or task (auto-load context).");
|
|
4937
4937
|
}
|
|
4938
|
-
if (entryId &&
|
|
4938
|
+
if (entryId && mode2 === "graph") {
|
|
4939
4939
|
await server.sendLoggingMessage({
|
|
4940
4940
|
level: "info",
|
|
4941
4941
|
data: `Graph traversal for ${entryId} (depth: ${maxHops})...`,
|
|
@@ -6813,8 +6813,8 @@ function computeDelta(before, after) {
|
|
|
6813
6813
|
if (!beforeSet.has(key)) gapsIntroduced++;
|
|
6814
6814
|
}
|
|
6815
6815
|
const netChange = after.totalGaps - before.totalGaps;
|
|
6816
|
-
const
|
|
6817
|
-
return { before, after, gapsFixed, gapsIntroduced, netChange, verdict };
|
|
6816
|
+
const verdict2 = netChange < 0 ? "improved" : netChange > 0 ? "degraded" : "unchanged";
|
|
6817
|
+
return { before, after, gapsFixed, gapsIntroduced, netChange, verdict: verdict2 };
|
|
6818
6818
|
}
|
|
6819
6819
|
function renderCoherenceDelta(delta) {
|
|
6820
6820
|
const lines = ["### Coherence Delta"];
|
|
@@ -7270,13 +7270,13 @@ function getCollectionLabel(canonicalKey, form, workspaceCtx, collectionCtx) {
|
|
|
7270
7270
|
// src/tools/orient.ts
|
|
7271
7271
|
var PURPOSE_GAP_PREFIX = "purpose-gap-";
|
|
7272
7272
|
var READINESS_GAPS_MAX = 50;
|
|
7273
|
-
function formatRetrievalMissLines(
|
|
7273
|
+
function formatRetrievalMissLines(verdict2) {
|
|
7274
7274
|
const lines = [];
|
|
7275
|
-
if (
|
|
7276
|
-
if (
|
|
7277
|
-
lines.push(`\u26A0 RETRIEVAL MISS \u2014 ${
|
|
7275
|
+
if (verdict2.served !== "ok") {
|
|
7276
|
+
if (verdict2.served === "miss") {
|
|
7277
|
+
lines.push(`\u26A0 RETRIEVAL MISS \u2014 ${verdict2.kind ?? "unknown"}`);
|
|
7278
7278
|
} else {
|
|
7279
|
-
lines.push(`\xB7 weak serve (provisional, ${
|
|
7279
|
+
lines.push(`\xB7 weak serve (provisional, ${verdict2.rubricVersion}): ${verdict2.kind ?? "thin"}`);
|
|
7280
7280
|
}
|
|
7281
7281
|
}
|
|
7282
7282
|
return lines;
|
|
@@ -7357,19 +7357,19 @@ function registerOrientTool(server) {
|
|
|
7357
7357
|
thinWrapper(async (args) => {
|
|
7358
7358
|
const parsed = parseOrFail(orientSchema, args);
|
|
7359
7359
|
if (!parsed.ok) return parsed.result;
|
|
7360
|
-
const { action, mode, tier, task, startupSignals, invocationPath } = parsed.data;
|
|
7360
|
+
const { action, mode: mode2, tier, task, startupSignals, invocationPath } = parsed.data;
|
|
7361
7361
|
return runWithToolContext(
|
|
7362
7362
|
{ tool: "orient", action },
|
|
7363
|
-
() => _handleOrient({ mode, tier, task, startupSignals, invocationPath })
|
|
7363
|
+
() => _handleOrient({ mode: mode2, tier, task, startupSignals, invocationPath })
|
|
7364
7364
|
);
|
|
7365
7365
|
})
|
|
7366
7366
|
);
|
|
7367
7367
|
}
|
|
7368
|
-
async function _handleOrient({ mode = "full", tier, task, startupSignals, invocationPath }) {
|
|
7368
|
+
async function _handleOrient({ mode: mode2 = "full", tier, task, startupSignals, invocationPath }) {
|
|
7369
7369
|
const errors = [];
|
|
7370
|
-
const callTier = tier ?? (
|
|
7370
|
+
const callTier = tier ?? (mode2 === "brief" ? "summary" : void 0);
|
|
7371
7371
|
const agentSessionId = getAgentSessionId();
|
|
7372
|
-
if (isSessionOriented() &&
|
|
7372
|
+
if (isSessionOriented() && mode2 === "brief" && !task) {
|
|
7373
7373
|
return buildAlreadyOrientedResult(kernelQuery, agentSessionId, taskGroundingWarningLines());
|
|
7374
7374
|
}
|
|
7375
7375
|
let wsCtx = null;
|
|
@@ -7468,7 +7468,7 @@ async function _handleOrient({ mode = "full", tier, task, startupSignals, invoca
|
|
|
7468
7468
|
lines.push(...retrievalMissLines);
|
|
7469
7469
|
lines.push("");
|
|
7470
7470
|
}
|
|
7471
|
-
if (
|
|
7471
|
+
if (mode2 === "brief") {
|
|
7472
7472
|
lines.push(...buildOrientBriefingLines(orientView));
|
|
7473
7473
|
if (orientView?.strategicContext) {
|
|
7474
7474
|
const sc = orientView.strategicContext;
|
|
@@ -9721,7 +9721,7 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
9721
9721
|
})
|
|
9722
9722
|
};
|
|
9723
9723
|
}
|
|
9724
|
-
const { verdict } = parsed;
|
|
9724
|
+
const { verdict: verdict2 } = parsed;
|
|
9725
9725
|
const lines = ["## Spine Check"];
|
|
9726
9726
|
if (stillRunning) {
|
|
9727
9727
|
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.`, "");
|
|
@@ -9731,7 +9731,7 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
9731
9731
|
if (row.stale) {
|
|
9732
9732
|
lines.push(`> STALE \u2014 ${staleReason}.${stillRunning || scheduleResult.reason === "already-in-flight" ? "" : " Pass refresh:true to recompute."}`, "");
|
|
9733
9733
|
}
|
|
9734
|
-
const s =
|
|
9734
|
+
const s = verdict2.structural;
|
|
9735
9735
|
lines.push(`Structural completeness: ${s.passed ? "PASS" : "FAIL"}`);
|
|
9736
9736
|
if (s.missing.length > 0) lines.push(`Missing: ${s.missing.join(", ")}`);
|
|
9737
9737
|
if (s.duplicateSingletons.purpose) lines.push(`Duplicate purpose singletons (which is canonical?): ${s.duplicateSingletons.purpose.join(", ")}`);
|
|
@@ -9742,19 +9742,19 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
9742
9742
|
}
|
|
9743
9743
|
if (s.visionConnectedToPurpose === false) lines.push("No vision informs any purpose.");
|
|
9744
9744
|
if (s.visionPurposeConnectivityTruncated) lines.push("Vision\u2192purpose connectivity unknown (relation set truncated).");
|
|
9745
|
-
if (
|
|
9746
|
-
if (
|
|
9747
|
-
if (
|
|
9748
|
-
lines.push("", `Per-strategy refusal heuristic (${
|
|
9749
|
-
for (const p of
|
|
9745
|
+
if (verdict2.strategiesTruncated) lines.push("Strategy set truncated \u2014 not every strategy was graded this pass.");
|
|
9746
|
+
if (verdict2.perStrategyEvaluationTruncated) lines.push("Per-strategy evaluation stopped on the wall-clock budget \u2014 not every fetched strategy was graded this pass.");
|
|
9747
|
+
if (verdict2.perStrategy.length > 0) {
|
|
9748
|
+
lines.push("", `Per-strategy refusal heuristic (${verdict2.perStrategy.length}):`);
|
|
9749
|
+
for (const p of verdict2.perStrategy) {
|
|
9750
9750
|
const refusal = p.refusal.hasRefusalSignal ? "names an explicit alternative it declines" : "no explicit refusal language detected";
|
|
9751
9751
|
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";
|
|
9752
9752
|
lines.push(`- ${p.entryId}: ${refusal}${rm}`);
|
|
9753
9753
|
}
|
|
9754
9754
|
}
|
|
9755
|
-
if (
|
|
9756
|
-
lines.push("", `One-story judgment [unvalidated, on-demand]: ${
|
|
9757
|
-
if (!
|
|
9755
|
+
if (verdict2.story) {
|
|
9756
|
+
lines.push("", `One-story judgment [unvalidated, on-demand]: ${verdict2.story.passed ? "PASS" : "FAIL"}`);
|
|
9757
|
+
if (!verdict2.story.passed && verdict2.story.failedCriterion) lines.push(`Failed criterion: ${verdict2.story.failedCriterion}`);
|
|
9758
9758
|
} else if (row.includesStoryPass) {
|
|
9759
9759
|
lines.push("", "One-story judgment: requested, but the LLM pass did not return a usable result (timeout or parse failure). Run quality action=spine-check story=true refresh=true to retry \u2014 a plain re-run will not reschedule it.");
|
|
9760
9760
|
} else {
|
|
@@ -9763,12 +9763,12 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
9763
9763
|
return {
|
|
9764
9764
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
9765
9765
|
structuredContent: success(
|
|
9766
|
-
`Spine check: structural ${s.passed ? "PASS" : "FAIL"}${
|
|
9766
|
+
`Spine check: structural ${s.passed ? "PASS" : "FAIL"}${verdict2.story ? `, story ${verdict2.story.passed ? "PASS" : "FAIL"} [unvalidated]` : ""}${stillRunning ? " (new run still in progress \u2014 showing previous result)" : ""}.`,
|
|
9767
9767
|
{
|
|
9768
9768
|
hasVerdict: true,
|
|
9769
9769
|
noSpineEntries: false,
|
|
9770
9770
|
structuralPassed: s.passed,
|
|
9771
|
-
storyPassed:
|
|
9771
|
+
storyPassed: verdict2.story?.passed ?? null,
|
|
9772
9772
|
...freshnessData,
|
|
9773
9773
|
stillRunning,
|
|
9774
9774
|
scheduled: scheduleResult.scheduled,
|
|
@@ -9897,8 +9897,8 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
9897
9897
|
[{ tool: "search", description: "Find the entry", parameters: { query: entryId } }]
|
|
9898
9898
|
);
|
|
9899
9899
|
}
|
|
9900
|
-
const
|
|
9901
|
-
if (!
|
|
9900
|
+
const verdict2 = res.verdict;
|
|
9901
|
+
if (!verdict2) {
|
|
9902
9902
|
const lines2 = [
|
|
9903
9903
|
`# Quality Check: ${entryId}`,
|
|
9904
9904
|
"",
|
|
@@ -9911,7 +9911,7 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
9911
9911
|
[{ tool: "quality", description: "Evaluate now", parameters: { action: "re-evaluate", entryId } }]
|
|
9912
9912
|
);
|
|
9913
9913
|
}
|
|
9914
|
-
const criteria =
|
|
9914
|
+
const criteria = verdict2.criteria ?? [];
|
|
9915
9915
|
const lines = [`# Quality Check: ${entryId}`, ""];
|
|
9916
9916
|
const staleWhy = res.staleReason === "rubric_removed" ? "this entry type is no longer graded" : res.staleReason === "methodology_changed_since_judged" ? "the rubric changed since it was judged" : "the entry was edited since it was judged";
|
|
9917
9917
|
if (res.stale) {
|
|
@@ -9921,35 +9921,35 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
9921
9921
|
);
|
|
9922
9922
|
}
|
|
9923
9923
|
if (!res.stale) {
|
|
9924
|
-
const verdictSection = formatRubricVerdictSection(
|
|
9925
|
-
lines.push(verdictSection || `Verdict tier \`${
|
|
9924
|
+
const verdictSection = formatRubricVerdictSection(verdict2);
|
|
9925
|
+
lines.push(verdictSection || `Verdict tier \`${verdict2.tier ?? "unknown"}\` for \`${entryId}\` \u2014 no rubric criteria to report.`);
|
|
9926
9926
|
}
|
|
9927
9927
|
try {
|
|
9928
9928
|
const wsForTracking = await getWorkspaceContext();
|
|
9929
9929
|
trackQualityCheck(wsForTracking.workspaceId, {
|
|
9930
9930
|
entry_id: entryId,
|
|
9931
|
-
entry_type:
|
|
9932
|
-
tier:
|
|
9933
|
-
passed:
|
|
9934
|
-
source:
|
|
9935
|
-
llm_status:
|
|
9936
|
-
llm_duration_ms:
|
|
9937
|
-
llm_error:
|
|
9938
|
-
has_roger_martin: !!
|
|
9931
|
+
entry_type: verdict2.canonicalKey ?? "",
|
|
9932
|
+
tier: verdict2.tier ?? "",
|
|
9933
|
+
passed: verdict2.passed ?? false,
|
|
9934
|
+
source: verdict2.source ?? "",
|
|
9935
|
+
llm_status: verdict2.llmStatus,
|
|
9936
|
+
llm_duration_ms: verdict2.llmDurationMs,
|
|
9937
|
+
llm_error: verdict2.llmError,
|
|
9938
|
+
has_roger_martin: !!verdict2.rogerMartin
|
|
9939
9939
|
});
|
|
9940
9940
|
} catch {
|
|
9941
9941
|
}
|
|
9942
9942
|
const passedCount = criteria.filter((c) => c.passed).length;
|
|
9943
9943
|
return successResult(
|
|
9944
9944
|
lines.join("\n"),
|
|
9945
|
-
res.stale ? `Quality check for ${entryId}: STALE \u2014 ${staleWhy}; re-evaluate for a current one.` : `Quality check for ${entryId}: ${
|
|
9945
|
+
res.stale ? `Quality check for ${entryId}: STALE \u2014 ${staleWhy}; re-evaluate for a current one.` : `Quality check for ${entryId}: ${verdict2.tier ?? "unknown"} tier, ${passedCount}/${criteria.length} criteria pass.`,
|
|
9946
9946
|
{
|
|
9947
9947
|
entryId,
|
|
9948
9948
|
entryFound: true,
|
|
9949
9949
|
hasVerdict: true,
|
|
9950
9950
|
stale: res.stale ?? false,
|
|
9951
|
-
tier:
|
|
9952
|
-
...res.stale ? {} : { passed:
|
|
9951
|
+
tier: verdict2.tier,
|
|
9952
|
+
...res.stale ? {} : { passed: verdict2.passed },
|
|
9953
9953
|
// TASK-790 (TEN-4407): a hint is failure coaching, so it is shown for a failed criterion
|
|
9954
9954
|
// only, the same rule the text renders by (lib/rubricFormatting.ts).
|
|
9955
9955
|
criteria: res.stale ? [] : criteria.map((c) => c.passed ? { id: c.id, passed: true } : { id: c.id, passed: false, hint: c.hint })
|
|
@@ -10103,10 +10103,10 @@ async function runWrapupReview() {
|
|
|
10103
10103
|
lines.push("| Entry | Collection | Quality | Connections | Coaching |");
|
|
10104
10104
|
lines.push("|-------|------------|---------|-------------|----------|");
|
|
10105
10105
|
for (const draft of data.drafts) {
|
|
10106
|
-
const
|
|
10106
|
+
const verdict2 = data.qualityVerdicts[draft.entryId];
|
|
10107
10107
|
const conns = data.connectionCounts[draft.entryId] ?? 0;
|
|
10108
|
-
const qualityStr =
|
|
10109
|
-
const weakest =
|
|
10108
|
+
const qualityStr = verdict2 ? `${verdict2.passed ? "Pass" : "Needs work"} (${verdict2.source})` : "\u2014";
|
|
10109
|
+
const weakest = verdict2?.weakest;
|
|
10110
10110
|
const coaching = weakest ? weakest.hint : "\u2014";
|
|
10111
10111
|
lines.push(`| \`${draft.entryId}\` ${draft.name} | ${draft.collection} | ${qualityStr} | ${conns} | ${coaching} |`);
|
|
10112
10112
|
}
|
|
@@ -11819,27 +11819,82 @@ function registerFeedbackTool(server) {
|
|
|
11819
11819
|
}));
|
|
11820
11820
|
}
|
|
11821
11821
|
|
|
11822
|
-
// src/tools/
|
|
11822
|
+
// src/tools/conflicts.ts
|
|
11823
11823
|
import { z as z31 } from "zod/v3";
|
|
11824
|
+
var ACTIONS = ["list", "resolve"];
|
|
11825
|
+
var mode = z31.enum(["actionable", "all"]);
|
|
11826
|
+
var verdict = z31.enum(["real", "noise"]);
|
|
11827
|
+
var conflictsSchema = z31.object({
|
|
11828
|
+
action: z31.enum(ACTIONS),
|
|
11829
|
+
mode: mode.optional().describe("list: all adds judged and snoozed rows."),
|
|
11830
|
+
issueId: z31.string().max(200).optional().describe("resolve: an id from list."),
|
|
11831
|
+
verdict: verdict.optional().describe("resolve: real keeps the row open; noise closes it.")
|
|
11832
|
+
});
|
|
11833
|
+
var conflictsActionUnion = z31.discriminatedUnion("action", [
|
|
11834
|
+
z31.object({ action: z31.literal("list"), mode: mode.optional() }),
|
|
11835
|
+
z31.object({ action: z31.literal("resolve"), issueId: z31.string().min(1).max(200), verdict })
|
|
11836
|
+
]);
|
|
11837
|
+
var CONFLICTS_ACTION_SPECS = {
|
|
11838
|
+
list: { params: ["mode"], description: "mode defaults to actionable." },
|
|
11839
|
+
resolve: { params: ["issueId", "verdict"], description: "issueId and verdict are required." }
|
|
11840
|
+
};
|
|
11841
|
+
async function handleConflictsList(listMode = "actionable") {
|
|
11842
|
+
const now = Math.floor(Date.now() / 6e4) * 6e4;
|
|
11843
|
+
const rows = await kernelCall("conflicts.list", { mode: listMode, now });
|
|
11844
|
+
const side = (name, id) => `${sanitizeSingleLineField(name)} (${id ? sanitizeSingleLineField(id) : "gone"})`;
|
|
11845
|
+
const lines = rows.map((r) => `${r.id} \xB7 ${side(r.entryAName, r.entryAId)} \u2194 ${side(r.entryBName, r.entryBId)} \xB7 ${r.conflictType} ${r.confidence.toFixed(2)} \xB7 ${sanitizeSingleLineField(r.explanation)}` + (r.guidance ? ` \xB7 ${sanitizeSingleLineField(r.guidance)}` : ""));
|
|
11846
|
+
const truncated = rows[0]?.listTruncated === true;
|
|
11847
|
+
const count = `${rows.length}${truncated ? "+" : ""} contradiction${rows.length === 1 && !truncated ? "" : "s"}`;
|
|
11848
|
+
const summary = rows.length === 0 ? listMode === "all" ? "No open contradictions." : "No contradictions await your verdict. mode=all shows judged and snoozed rows." : listMode === "all" ? `${count} open, judged and snoozed included.` : `${count}. Resolve one: conflicts action=resolve issueId=<id> verdict=real|noise`;
|
|
11849
|
+
return successResult([summary, ...lines].join("\n"), summary, { mode: listMode, rows });
|
|
11850
|
+
}
|
|
11851
|
+
async function handleConflictsResolve(issueId, word) {
|
|
11852
|
+
requireWriteAccess();
|
|
11853
|
+
const result2 = await kernelCall("conflicts.resolve", {
|
|
11854
|
+
issueId,
|
|
11855
|
+
verdict: word === "noise" ? "false_alarm" : "real"
|
|
11856
|
+
});
|
|
11857
|
+
const message = word === "real" ? "Verdict recorded: real. The row stays open until one entry changes." : "Verdict recorded: noise. The row is closed.";
|
|
11858
|
+
return successResult(message, message, { issueId, verdict: word, result: result2 });
|
|
11859
|
+
}
|
|
11860
|
+
function registerConflictsTool(server) {
|
|
11861
|
+
const handlers = {
|
|
11862
|
+
list: (data) => handleConflictsList(data.mode),
|
|
11863
|
+
resolve: (data) => handleConflictsResolve(data.issueId, data.verdict)
|
|
11864
|
+
};
|
|
11865
|
+
server.registerTool("conflicts", {
|
|
11866
|
+
title: "Conflicts",
|
|
11867
|
+
description: "Contradictions found between entries: list them, or resolve one as real or noise.",
|
|
11868
|
+
inputSchema: conflictsSchema,
|
|
11869
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
11870
|
+
}, thinWrapper(async (args) => {
|
|
11871
|
+
const parsed = parseOrFail(conflictsSchema, args);
|
|
11872
|
+
if (!parsed.ok) return parsed.result;
|
|
11873
|
+
return runWithToolContext({ tool: "conflicts", action: parsed.data.action }, () => dispatchDiscriminated("conflicts", conflictsActionUnion, parsed.data, CONFLICTS_ACTION_SPECS, handlers));
|
|
11874
|
+
}));
|
|
11875
|
+
}
|
|
11876
|
+
|
|
11877
|
+
// src/tools/question.ts
|
|
11878
|
+
import { z as z32 } from "zod/v3";
|
|
11824
11879
|
var QUESTION_ACTIONS = ["create", "assign", "answer", "force-close", "list"];
|
|
11825
|
-
var questionSchema =
|
|
11826
|
-
action:
|
|
11880
|
+
var questionSchema = z32.object({
|
|
11881
|
+
action: z32.enum(QUESTION_ACTIONS).describe(
|
|
11827
11882
|
"'create': create a new routed question from text \u2014 the dedicated birth action; every question is born escalated to the team lead (generic entry creation and move-and-retype also pair a routing facet). 'assign': reassign ownership to a role (toRole required \u2014 no bare self-confirm, no toPerson). 'answer': close the loop \u2014 writes the answer on the question and moves it to answered; --entry links an existing decision instead. 'force-close': Team-Lead-only escape hatch \u2014 reason required. 'list': the ack-gate read model \u2014 what you owe (or, with mine:true, every question you asked, including closed ones)."
|
|
11828
11883
|
),
|
|
11829
|
-
entryId:
|
|
11830
|
-
text:
|
|
11831
|
-
blocks:
|
|
11832
|
-
toRole:
|
|
11833
|
-
reason:
|
|
11834
|
-
answerEntryId:
|
|
11835
|
-
mine:
|
|
11884
|
+
entryId: z32.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
|
|
11885
|
+
text: z32.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
|
|
11886
|
+
blocks: z32.array(z32.string().max(200)).max(50).optional().describe("'create': entry ids this question is blocking (repeatable)."),
|
|
11887
|
+
toRole: z32.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id) \u2014 required."),
|
|
11888
|
+
reason: z32.string().max(2e3).optional().describe("'force-close': required reason."),
|
|
11889
|
+
answerEntryId: z32.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
|
|
11890
|
+
mine: z32.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
|
|
11836
11891
|
});
|
|
11837
|
-
var createVariant =
|
|
11838
|
-
var assignVariant =
|
|
11839
|
-
var answerVariant =
|
|
11840
|
-
var forceCloseVariant =
|
|
11841
|
-
var listVariant =
|
|
11842
|
-
var questionActionUnion =
|
|
11892
|
+
var createVariant = z32.object({ action: z32.literal("create"), text: z32.string().min(1).max(5e3), blocks: z32.array(z32.string().max(200)).max(50).optional() });
|
|
11893
|
+
var assignVariant = z32.object({ action: z32.literal("assign"), entryId: z32.string().max(200), toRole: z32.string().max(200).min(1) });
|
|
11894
|
+
var answerVariant = z32.object({ action: z32.literal("answer"), entryId: z32.string().max(200), text: z32.string().max(2e3).optional(), answerEntryId: z32.string().max(200).optional() });
|
|
11895
|
+
var forceCloseVariant = z32.object({ action: z32.literal("force-close"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
|
|
11896
|
+
var listVariant = z32.object({ action: z32.literal("list"), mine: z32.boolean().optional() });
|
|
11897
|
+
var questionActionUnion = z32.discriminatedUnion("action", [
|
|
11843
11898
|
createVariant,
|
|
11844
11899
|
assignVariant,
|
|
11845
11900
|
answerVariant,
|
|
@@ -11993,52 +12048,52 @@ function registerQuestionTools(server) {
|
|
|
11993
12048
|
}
|
|
11994
12049
|
|
|
11995
12050
|
// src/tools/role.ts
|
|
11996
|
-
import { z as
|
|
12051
|
+
import { z as z33 } from "zod/v3";
|
|
11997
12052
|
var ROLE_ACTIONS = ["accountable", "seat", "unseat", "release_move", "accept_move", "refuse_move"];
|
|
11998
|
-
var roleSchema =
|
|
11999
|
-
action:
|
|
12053
|
+
var roleSchema = z33.object({
|
|
12054
|
+
action: z33.enum(ROLE_ACTIONS).describe(
|
|
12000
12055
|
"'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage. 'seat': put a person in this role. 'unseat': take this role from a person. 'release_move' / 'accept_move' / 'refuse_move': move a role or team to a team with another Team Lead."
|
|
12001
12056
|
),
|
|
12002
|
-
roleId:
|
|
12003
|
-
canonicalKey:
|
|
12004
|
-
limit:
|
|
12005
|
-
cursor:
|
|
12006
|
-
person:
|
|
12057
|
+
roleId: z33.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id."),
|
|
12058
|
+
canonicalKey: z33.string().max(200).optional().describe("'accountable': narrow to one kind (e.g. objective, key_result, tension)."),
|
|
12059
|
+
limit: z33.number().int().positive().max(200).optional().describe("'accountable': max explicit-list rows per page (default and maximum 200)."),
|
|
12060
|
+
cursor: z33.number().optional().describe("'accountable': continue the explicit list from an earlier truncated page's nextCursor."),
|
|
12061
|
+
person: z33.string().max(200).optional().describe(
|
|
12007
12062
|
"'seat' / 'unseat': an existing person, by person id or exact email. For 'seat', give this or newPersonName, not both."
|
|
12008
12063
|
),
|
|
12009
|
-
newPersonName:
|
|
12064
|
+
newPersonName: z33.string().max(120).optional().describe(
|
|
12010
12065
|
"'seat': the name of a person who has no login yet; a placeholder person is created once and reused for the same exact name. Refused when the name belongs to someone with an account."
|
|
12011
12066
|
),
|
|
12012
|
-
teamId:
|
|
12067
|
+
teamId: z33.string().max(200).optional().describe(
|
|
12013
12068
|
"'seat' / 'unseat': the team, by chain id or entry id. Only needed for Team Lead, which lives in every team; any other role is seated in its home team. 'release_move': the team it moves to."
|
|
12014
12069
|
),
|
|
12015
|
-
childId:
|
|
12016
|
-
why:
|
|
12070
|
+
childId: z33.string().max(200).optional().describe("'release_move' / 'accept_move' / 'refuse_move': the role or team that moves, by chain id or entry id."),
|
|
12071
|
+
why: z33.string().max(1e3).optional().describe("'refuse_move': why the move is refused; it becomes a tension for the old team's Team Lead.")
|
|
12017
12072
|
});
|
|
12018
|
-
var accountableVariant =
|
|
12019
|
-
action:
|
|
12020
|
-
roleId:
|
|
12021
|
-
canonicalKey:
|
|
12022
|
-
limit:
|
|
12023
|
-
cursor:
|
|
12073
|
+
var accountableVariant = z33.object({
|
|
12074
|
+
action: z33.literal("accountable"),
|
|
12075
|
+
roleId: z33.string().max(200),
|
|
12076
|
+
canonicalKey: z33.string().max(200).optional(),
|
|
12077
|
+
limit: z33.number().int().positive().max(200).optional(),
|
|
12078
|
+
cursor: z33.number().optional()
|
|
12024
12079
|
});
|
|
12025
|
-
var seatVariant =
|
|
12026
|
-
action:
|
|
12027
|
-
roleId:
|
|
12028
|
-
person:
|
|
12029
|
-
newPersonName:
|
|
12030
|
-
teamId:
|
|
12080
|
+
var seatVariant = z33.object({
|
|
12081
|
+
action: z33.literal("seat"),
|
|
12082
|
+
roleId: z33.string().max(200),
|
|
12083
|
+
person: z33.string().max(200).optional(),
|
|
12084
|
+
newPersonName: z33.string().max(120).optional(),
|
|
12085
|
+
teamId: z33.string().max(200).optional()
|
|
12031
12086
|
});
|
|
12032
|
-
var unseatVariant =
|
|
12033
|
-
action:
|
|
12034
|
-
roleId:
|
|
12035
|
-
person:
|
|
12036
|
-
teamId:
|
|
12087
|
+
var unseatVariant = z33.object({
|
|
12088
|
+
action: z33.literal("unseat"),
|
|
12089
|
+
roleId: z33.string().max(200),
|
|
12090
|
+
person: z33.string().max(200),
|
|
12091
|
+
teamId: z33.string().max(200).optional()
|
|
12037
12092
|
});
|
|
12038
|
-
var releaseMoveVariant =
|
|
12039
|
-
var acceptMoveVariant =
|
|
12040
|
-
var refuseMoveVariant =
|
|
12041
|
-
var roleActionUnion =
|
|
12093
|
+
var releaseMoveVariant = z33.object({ action: z33.literal("release_move"), childId: z33.string().max(200), teamId: z33.string().max(200) });
|
|
12094
|
+
var acceptMoveVariant = z33.object({ action: z33.literal("accept_move"), childId: z33.string().max(200) });
|
|
12095
|
+
var refuseMoveVariant = z33.object({ action: z33.literal("refuse_move"), childId: z33.string().max(200), why: z33.string().max(1e3) });
|
|
12096
|
+
var roleActionUnion = z33.discriminatedUnion("action", [
|
|
12042
12097
|
accountableVariant,
|
|
12043
12098
|
seatVariant,
|
|
12044
12099
|
unseatVariant,
|
|
@@ -12540,7 +12595,7 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
|
|
|
12540
12595
|
}
|
|
12541
12596
|
|
|
12542
12597
|
// src/prompts/index.ts
|
|
12543
|
-
import { z as
|
|
12598
|
+
import { z as z34 } from "zod/v3";
|
|
12544
12599
|
async function buildReviewAgainstRulesPrompt(domain) {
|
|
12545
12600
|
const entries = await kernelQuery("chain.listEntries", { collectionSlug: "rules" });
|
|
12546
12601
|
const rules = entries.slice(0, 50);
|
|
@@ -12622,27 +12677,27 @@ function registerPrompts(server) {
|
|
|
12622
12677
|
server.prompt(
|
|
12623
12678
|
"review-against-rules",
|
|
12624
12679
|
"Review code or a design decision against the rules, focused on a given area. Fetches the rules and asks you to do a structured compliance review.",
|
|
12625
|
-
{ domain:
|
|
12680
|
+
{ domain: z34.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
|
|
12626
12681
|
async ({ domain }) => buildReviewAgainstRulesPrompt(domain)
|
|
12627
12682
|
);
|
|
12628
12683
|
server.prompt(
|
|
12629
12684
|
"name-check",
|
|
12630
12685
|
"Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
|
|
12631
|
-
{ names:
|
|
12686
|
+
{ names: z34.string().describe("Comma-separated list of names to check (e.g. 'user_id, workspace_id, entry_status')") },
|
|
12632
12687
|
async ({ names }) => buildNameCheckPrompt(names)
|
|
12633
12688
|
);
|
|
12634
12689
|
server.prompt(
|
|
12635
12690
|
"draft-decision-record",
|
|
12636
12691
|
"Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
|
|
12637
|
-
{ context:
|
|
12692
|
+
{ context: z34.string().describe("Description of the decision (e.g. 'We decided to keep one body field per entry because...')") },
|
|
12638
12693
|
async ({ context }) => buildDraftDecisionRecordPrompt(context)
|
|
12639
12694
|
);
|
|
12640
12695
|
server.prompt(
|
|
12641
12696
|
"draft-rule-from-context",
|
|
12642
12697
|
"Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
|
|
12643
12698
|
{
|
|
12644
|
-
observation:
|
|
12645
|
-
domain:
|
|
12699
|
+
observation: z34.string().describe("What you observed or discovered (e.g. 'A workspace can have more than one owner role')"),
|
|
12700
|
+
domain: z34.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
|
|
12646
12701
|
},
|
|
12647
12702
|
async ({ observation, domain }) => buildDraftRuleFromContextPrompt(observation, domain)
|
|
12648
12703
|
);
|
|
@@ -12738,7 +12793,7 @@ var INSTRUCTIONS = [
|
|
|
12738
12793
|
"Finish with `session action=wrapup-review`, then `session action=close`.",
|
|
12739
12794
|
"If the work falls outside the active work packages, name the gap and ask the user before designing it."
|
|
12740
12795
|
].join("\n");
|
|
12741
|
-
var sessionIdArg =
|
|
12796
|
+
var sessionIdArg = z35.string().max(200).optional().describe(
|
|
12742
12797
|
"The sessionId `session action=start` returned. Pass it on every write: the connection does not remember your session between calls."
|
|
12743
12798
|
);
|
|
12744
12799
|
function acceptSessionIdOnEveryTool(server) {
|
|
@@ -12792,6 +12847,7 @@ function createProductBrainServer() {
|
|
|
12792
12847
|
registerWorkflowTools(server);
|
|
12793
12848
|
registerWorkspaceTools(server);
|
|
12794
12849
|
registerFeedbackTool(server);
|
|
12850
|
+
registerConflictsTool(server);
|
|
12795
12851
|
registerQuestionTools(server);
|
|
12796
12852
|
registerRoleTools(server);
|
|
12797
12853
|
registerPeopleTools(server);
|
|
@@ -12832,4 +12888,4 @@ export {
|
|
|
12832
12888
|
createProductBrainServer,
|
|
12833
12889
|
initFeatureFlags
|
|
12834
12890
|
};
|
|
12835
|
-
//# sourceMappingURL=chunk-
|
|
12891
|
+
//# sourceMappingURL=chunk-WD7EUIF4.js.map
|