@productbrain/mcp 0.0.1-beta.5912 → 0.0.1-beta.5946
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.
|
@@ -1554,6 +1554,8 @@ var groundingGovernanceEntrySchema = z4.object({
|
|
|
1554
1554
|
collectionSlug: z4.string()
|
|
1555
1555
|
});
|
|
1556
1556
|
var groundingReportSchema = z4.object({
|
|
1557
|
+
// TASK-156: null preserves uncertainty for unavailable checks or an older server response.
|
|
1558
|
+
scanTruncated: z4.boolean().nullable(),
|
|
1557
1559
|
related: z4.array(groundingRelatedEntrySchema),
|
|
1558
1560
|
duplicates: z4.array(groundingDuplicateEntrySchema),
|
|
1559
1561
|
governance: z4.array(groundingGovernanceEntrySchema)
|
|
@@ -1680,6 +1682,9 @@ function reconcileBatchPreviewDuplicate(tracker, args) {
|
|
|
1680
1682
|
return null;
|
|
1681
1683
|
}
|
|
1682
1684
|
|
|
1685
|
+
// src/lib/readinessNotices.ts
|
|
1686
|
+
var READINESS_PROVISIONAL_LINE = "Readiness is provisional: its sample omitted accepted entries. Counts are lower bounds, apparent gaps may already be covered, and the overall stage is unverified.";
|
|
1687
|
+
|
|
1683
1688
|
// src/tools/smart-capture.ts
|
|
1684
1689
|
var GENERIC_PROFILE = {
|
|
1685
1690
|
governedDraft: false,
|
|
@@ -2265,9 +2270,8 @@ async function handleCapture(server, { collection, name, description, context, e
|
|
|
2265
2270
|
};
|
|
2266
2271
|
}
|
|
2267
2272
|
const profile = await getProfile(resolvedCollection);
|
|
2268
|
-
const skipAutoDiscoveryEarly = links && links.length > 0;
|
|
2269
2273
|
const groundingStart = Date.now();
|
|
2270
|
-
const groundingPromise =
|
|
2274
|
+
const groundingPromise = name || description ? kernelQuery("chain.suggestLinksForCapture", {
|
|
2271
2275
|
entries: [{ name, description: description ?? "", collectionHint: resolvedCollection }],
|
|
2272
2276
|
limit: 5,
|
|
2273
2277
|
threshold: 2
|
|
@@ -2286,6 +2290,8 @@ async function handleCapture(server, { collection, name, description, context, e
|
|
|
2286
2290
|
collectionSlug: g.collectionSlug
|
|
2287
2291
|
}));
|
|
2288
2292
|
const groundingReport = {
|
|
2293
|
+
// null preserves uncertainty for unavailable checks or an older server response.
|
|
2294
|
+
scanTruncated: typeof groundingEntry?.scanTruncated === "boolean" ? groundingEntry.scanTruncated : null,
|
|
2289
2295
|
related: groundingRelated.map((r) => ({
|
|
2290
2296
|
entryId: r.entryId,
|
|
2291
2297
|
name: r.name,
|
|
@@ -2303,9 +2309,10 @@ async function handleCapture(server, { collection, name, description, context, e
|
|
|
2303
2309
|
})),
|
|
2304
2310
|
governance: groundingGovernance
|
|
2305
2311
|
};
|
|
2312
|
+
const groundingCaveat = !groundingEntry ? "Grounding could not be completed; duplicate presence is unknown. Review existing entries before creating another." : groundingReport.scanTruncated !== false ? "Grounding covered a bounded sample; completeness is unverified and additional matching entries may exist. Review existing entries before creating another." : "";
|
|
2306
2313
|
if (suggestOnly) {
|
|
2307
2314
|
const hasSuggestions = groundingReport.related.length > 0 || groundingReport.duplicates.length > 0;
|
|
2308
|
-
const summaryText = hasSuggestions ? `Grounding preview for "${name}": ${groundingReport.related.length} related, ${groundingReport.duplicates.length} possible duplicates. No entry created.` : `Grounding preview for "${name}": no similar entries found.
|
|
2315
|
+
const summaryText = hasSuggestions ? `Grounding preview for "${name}": ${groundingReport.related.length} related, ${groundingReport.duplicates.length} possible duplicates. No entry created.` : groundingEntry ? `Grounding preview for "${name}": no similar entries found in this check. No entry created.` : `Grounding preview for "${name}": duplicate check unavailable. No entry created.`;
|
|
2309
2316
|
return {
|
|
2310
2317
|
content: [{
|
|
2311
2318
|
type: "text",
|
|
@@ -2313,7 +2320,9 @@ async function handleCapture(server, { collection, name, description, context, e
|
|
|
2313
2320
|
|
|
2314
2321
|
${summaryText}
|
|
2315
2322
|
|
|
2316
|
-
` + (
|
|
2323
|
+
` + (groundingCaveat ? `${groundingCaveat}
|
|
2324
|
+
|
|
2325
|
+
` : "") + (groundingReport.related.length > 0 ? `## Related entries
|
|
2317
2326
|
${groundingReport.related.map((r) => `- **${r.entryId}** ${r.name} [${r.collectionSlug}] \u2014 ${r.reasoning}`).join("\n")}
|
|
2318
2327
|
|
|
2319
2328
|
` : "") + (groundingReport.duplicates.length > 0 ? `## Possible duplicates
|
|
@@ -2569,7 +2578,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2569
2578
|
const linksSuggested = [];
|
|
2570
2579
|
const userLinkResults = [];
|
|
2571
2580
|
const advisedLinks = [];
|
|
2572
|
-
|
|
2581
|
+
const conflictCandidates = groundingReport.duplicates.filter((candidate) => candidate.matchType === "possible_duplicate" && candidate.entryId !== finalEntryId).slice(0, 3).map((candidate) => ({ entryId: candidate.entryId, name: candidate.name, collection: candidate.collectionSlug }));
|
|
2573
2582
|
const pendingRelations = [];
|
|
2574
2583
|
const skipAutoDiscovery = links && links.length > 0;
|
|
2575
2584
|
const searchQuery = extractSearchTerms(name, description);
|
|
@@ -2646,7 +2655,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2646
2655
|
const reason = c.confidence >= AUTO_LINK_CONFIDENCE_THRESHOLD ? "high relevance (already linked)" : `"${c.name.toLowerCase().split(/\s+/).filter((w) => `${name} ${description}`.toLowerCase().includes(w) && w.length > 3).slice(0, 2).join('", "')}" appears in content`;
|
|
2647
2656
|
linksSuggested.push({ entryId: c.entryId, name: c.name, collection: c.collSlug, reason, preview: c.preview });
|
|
2648
2657
|
}
|
|
2649
|
-
conflictCandidates = candidates.filter((c) => c.entryId && c.entryId !== finalEntryId).slice(0, 3).map((c) => ({ entryId: c.entryId, name: c.name, collection: c.collSlug }));
|
|
2650
2658
|
}
|
|
2651
2659
|
if (links && links.length > 0 && finalEntryId) {
|
|
2652
2660
|
for (const link of links) {
|
|
@@ -2678,8 +2686,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2678
2686
|
entryWarnings.push(`Auto-linking partially failed: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
2679
2687
|
return null;
|
|
2680
2688
|
}) : Promise.resolve(null);
|
|
2681
|
-
const cardinalityPromise = resolvedCK ? kernelQuery("chain.checkCardinalityWarning", { canonicalKey: resolvedCK }).catch(() => null) : Promise.resolve(null);
|
|
2682
|
-
const contradictionPromise = runContradictionCheck(name, description);
|
|
2689
|
+
const cardinalityPromise = resolvedCK ? kernelQuery("chain.checkCardinalityWarning", { canonicalKey: resolvedCK, excludeEntryId: internalId }).catch(() => null) : Promise.resolve(null);
|
|
2690
|
+
const contradictionPromise = runContradictionCheck(name, description, finalEntryId, internalId);
|
|
2683
2691
|
const coachingPromise = coachingFromCreate !== void 0 ? Promise.resolve(coachingFromCreate) : kernelMutation("quality.evaluateHeuristicAndSchedule", {
|
|
2684
2692
|
entryId: finalEntryId,
|
|
2685
2693
|
context: "capture"
|
|
@@ -2769,13 +2777,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2769
2777
|
}
|
|
2770
2778
|
const freshGovernanceMode = await refreshWorkspaceGovernanceMode();
|
|
2771
2779
|
const shouldAutoCommit = shouldAutoCommitCapture(autoCommit, freshGovernanceMode);
|
|
2772
|
-
if (shouldAutoCommit && finalEntryId && conflictCandidates.length === 0) {
|
|
2773
|
-
try {
|
|
2774
|
-
const conflictResults = await kernelQuery("chain.searchEntries", { query: name });
|
|
2775
|
-
conflictCandidates = (conflictResults ?? []).filter((r) => r.entryId && r.entryId !== finalEntryId && r._id !== internalId).slice(0, 3).map((r) => ({ entryId: r.entryId, name: r.name, collection: r.collectionSlug ?? "unknown" }));
|
|
2776
|
-
} catch {
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
2780
|
let finalStatus = "draft";
|
|
2780
2781
|
let commitError = null;
|
|
2781
2782
|
let commitRefusal = null;
|
|
@@ -2925,6 +2926,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2925
2926
|
lines.push(`${i + 1}. **${s.entryId ?? "(no ID)"}**: ${s.name} [${s.collection}]${preview2}`);
|
|
2926
2927
|
}
|
|
2927
2928
|
}
|
|
2929
|
+
if (groundingCaveat) lines.push("", groundingCaveat);
|
|
2928
2930
|
if (format === "human") {
|
|
2929
2931
|
const hasDuplicates = groundingReport.duplicates.length > 0;
|
|
2930
2932
|
const hasRelated = groundingReport.related.length > 0;
|
|
@@ -2962,11 +2964,11 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2962
2964
|
for (const rs of relationSuggestionsFromCreate) {
|
|
2963
2965
|
lines.push(`- **${rs.relationType}** \u2192 ${rs.targetEntryId} "${rs.targetName}" (confidence: ${rs.confidence})`);
|
|
2964
2966
|
}
|
|
2965
|
-
lines.push(`_Create with: \`relations action=create
|
|
2967
|
+
lines.push(`_Create with: \`relations action=create from="${finalEntryId}" to="TARGET-ID" type="TYPE"\`_`);
|
|
2966
2968
|
}
|
|
2967
2969
|
if (authorityDomainFromCreate) {
|
|
2968
2970
|
lines.push("");
|
|
2969
|
-
lines.push(`domain: ${authorityDomainFromCreate.slug} (proposal pending \u2014
|
|
2971
|
+
lines.push(`domain: ${authorityDomainFromCreate.slug} (proposal pending \u2014 closing the session does not ratify it. A workspace owner or admin can review it with \`pb authority-domains review\` before deciding whether to ratify.)`);
|
|
2970
2972
|
}
|
|
2971
2973
|
if (formativeHints.length > 0) {
|
|
2972
2974
|
trackCaptureQualityHints(wsCtx.workspaceId, {
|
|
@@ -3001,11 +3003,11 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3001
3003
|
}
|
|
3002
3004
|
if (contradictionWarnings.length > 0) {
|
|
3003
3005
|
lines.push("");
|
|
3004
|
-
lines.push("\u26A0 Contradiction check:
|
|
3006
|
+
lines.push("\u26A0 Contradiction check: captured entry matched existing governance entries:");
|
|
3005
3007
|
for (const w of contradictionWarnings) {
|
|
3006
3008
|
lines.push(`- ${w.name} (${w.collection}, ${w.entryId}) \u2014 has 'governs' relation to ${w.governsCount} entries`);
|
|
3007
3009
|
}
|
|
3008
|
-
lines.push("
|
|
3010
|
+
lines.push("Review the matched governance entries and reconcile any actual conflict.");
|
|
3009
3011
|
}
|
|
3010
3012
|
if (entryWarnings.length > 0) {
|
|
3011
3013
|
lines.push("");
|
|
@@ -3043,7 +3045,9 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3043
3045
|
}
|
|
3044
3046
|
try {
|
|
3045
3047
|
const readiness = await kernelQuery("chain.workspaceReadiness");
|
|
3046
|
-
if (readiness
|
|
3048
|
+
if (readiness?.sampleTruncated) {
|
|
3049
|
+
lines.push("", `## Workspace Readiness: ${readiness.score}% (provisional sample)`, READINESS_PROVISIONAL_LINE);
|
|
3050
|
+
} else if (readiness && readiness.gaps && readiness.gaps.length > 0) {
|
|
3047
3051
|
const topGaps = readiness.gaps.slice(0, 2);
|
|
3048
3052
|
lines.push("");
|
|
3049
3053
|
lines.push(`## Workspace Readiness: ${readiness.score}%`);
|
|
@@ -3694,10 +3698,10 @@ _Use \`entries action=move\` to correct any misclassified entries._`);
|
|
|
3694
3698
|
const entriesWithOverlaps = created.filter((r) => r.overlapCount && r.overlapCount > 0);
|
|
3695
3699
|
if (entriesWithOverlaps.length > 0) {
|
|
3696
3700
|
lines.push("");
|
|
3697
|
-
lines.push("##
|
|
3698
|
-
lines.push(`${entriesWithOverlaps.length} ${entriesWithOverlaps.length === 1 ? "entry has" : "entries have"}
|
|
3701
|
+
lines.push("## Related-entry Suggestions");
|
|
3702
|
+
lines.push(`${entriesWithOverlaps.length} ${entriesWithOverlaps.length === 1 ? "entry has" : "entries have"} link candidates. Review their relevance before linking:`);
|
|
3699
3703
|
for (const r of entriesWithOverlaps) {
|
|
3700
|
-
lines.push(`- **${r.entryId}**: ${r.name} \u2014 ${r.overlapCount}
|
|
3704
|
+
lines.push(`- **${r.entryId}**: ${r.name} \u2014 ${r.overlapCount} link ${r.overlapCount === 1 ? "candidate" : "candidates"}`);
|
|
3701
3705
|
}
|
|
3702
3706
|
}
|
|
3703
3707
|
}
|
|
@@ -3865,7 +3869,7 @@ async function recordCommitFailure({
|
|
|
3865
3869
|
}
|
|
3866
3870
|
}
|
|
3867
3871
|
}
|
|
3868
|
-
async function runContradictionCheck(name, description) {
|
|
3872
|
+
async function runContradictionCheck(name, description, excludeEntryId, excludeDocId) {
|
|
3869
3873
|
const warnings = [];
|
|
3870
3874
|
try {
|
|
3871
3875
|
const keyTerms = tokenizeText(`${name} ${description}`).filter((w) => w.length >= 4 && !STOP_WORDS.has(w)).slice(0, 8);
|
|
@@ -3875,7 +3879,13 @@ async function runContradictionCheck(name, description) {
|
|
|
3875
3879
|
kernelQuery("chain.searchEntries", { query: searchQuery, collectionSlug: "business-rules" }),
|
|
3876
3880
|
kernelQuery("chain.searchEntries", { query: searchQuery, collectionSlug: "architecture" })
|
|
3877
3881
|
]);
|
|
3878
|
-
const
|
|
3882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3883
|
+
const allGov = [...govResults ?? [], ...archResults ?? []].filter((entry) => {
|
|
3884
|
+
const id = entry.entryId ?? entry._id;
|
|
3885
|
+
if (!id || excludeEntryId && entry.entryId === excludeEntryId || excludeDocId && entry._id === excludeDocId || seen.has(id)) return false;
|
|
3886
|
+
seen.add(id);
|
|
3887
|
+
return true;
|
|
3888
|
+
}).slice(0, 5);
|
|
3879
3889
|
for (const entry of allGov) {
|
|
3880
3890
|
const entryTokens = new Set(tokenizeText(`${entry.name} ${entry.data?.description ?? ""}`));
|
|
3881
3891
|
const matched = keyTerms.filter((t) => entryTokens.has(t));
|
|
@@ -4859,7 +4869,7 @@ var entriesSchema = z10.object({
|
|
|
4859
4869
|
entryIds: z10.array(z10.string().max(200)).min(1).max(20).optional().describe("Entry IDs for 'batch', e.g. ['TYPE-strategy', 'STR-jljeg7']"),
|
|
4860
4870
|
collection: z10.string().max(200).optional().describe("Collection slug \u2014 for 'list'/'search': scope filter, e.g. 'glossary', 'tracking-events'. For 'preflight': the collection the entry would be composed into (required)."),
|
|
4861
4871
|
status: entriesStatusFilterSchema.optional().describe(
|
|
4862
|
-
"For 'list':
|
|
4872
|
+
"For 'list': defaults to accepted entries (active/accepted); use status='draft' to discover drafts. One lifecycle value (draft|active|accepted|deprecated|archived). For 'search': one value OR an array of up to 5 (e.g. ['active','accepted']). For 'update': draft|active|deprecated|archived ONLY \u2014 'accepted' is set via 'commit', never 'update'. Workflow values use `workflowStatus` instead."
|
|
4863
4873
|
),
|
|
4864
4874
|
tag: z10.string().max(200).optional().describe("For 'list': filter by internal tag."),
|
|
4865
4875
|
label: z10.string().max(200).optional().describe("For 'list': filter by label slug \u2014 matches entries across all collections."),
|
|
@@ -4943,7 +4953,7 @@ var entriesActionUnion = z10.discriminatedUnion("action", [
|
|
|
4943
4953
|
]);
|
|
4944
4954
|
var ENTRIES_ACTION_SPECS = {
|
|
4945
4955
|
preflight: { params: ["collection", "canonicalKey", "links"], description: "Inspect structure before composition; no writes or permission grant." },
|
|
4946
|
-
list: { params: ["collection", "status", "tag", "label"], description: "Browse entries;
|
|
4956
|
+
list: { params: ["collection", "status", "tag", "label"], description: "Browse accepted entries by default; status='draft' lists drafts. All params optional." },
|
|
4947
4957
|
get: { params: ["entryId"], description: "Fetch one entry by ID." },
|
|
4948
4958
|
batch: { params: ["entryIds"], description: "Fetch up to 20 entries by ID." },
|
|
4949
4959
|
search: { params: ["query", "collection", "status"], description: "Full-text search; query is required (min 2 chars)." },
|
|
@@ -5337,7 +5347,7 @@ async function handleBatch(entryIds) {
|
|
|
5337
5347
|
async function handleList(collection, status2, tag, label) {
|
|
5338
5348
|
let entries;
|
|
5339
5349
|
if (label) {
|
|
5340
|
-
entries = await kernelQuery("chain.listEntriesByLabel", { labelSlug: label });
|
|
5350
|
+
entries = await kernelQuery("chain.listEntriesByLabel", { labelSlug: label, acceptedOnly: !status2 });
|
|
5341
5351
|
if (status2) {
|
|
5342
5352
|
entries = entries.filter((e) => e.status === status2);
|
|
5343
5353
|
}
|
|
@@ -5349,10 +5359,11 @@ async function handleList(collection, status2, tag, label) {
|
|
|
5349
5359
|
});
|
|
5350
5360
|
}
|
|
5351
5361
|
if (entries.length === 0) {
|
|
5362
|
+
const emptyMessage = `No entries match the given filters.${!status2 ? " Default list scope is accepted entries (active/accepted); use status='draft' to discover drafts." : ` Status filter: ${status2}.`}`;
|
|
5352
5363
|
return {
|
|
5353
|
-
content: [{ type: "text", text:
|
|
5364
|
+
content: [{ type: "text", text: emptyMessage }],
|
|
5354
5365
|
structuredContent: success(
|
|
5355
|
-
|
|
5366
|
+
emptyMessage,
|
|
5356
5367
|
{ entries: [], total: 0 },
|
|
5357
5368
|
[{ tool: "collections", description: "List collections", parameters: { action: "list" } }]
|
|
5358
5369
|
)
|
|
@@ -7210,8 +7221,9 @@ function formatSteeringBrief(result2) {
|
|
|
7210
7221
|
lines.push(`${delta.newEntries.length} new entries since last steering brief${delta.truncated ? " (TRUNCATED)" : ""}`);
|
|
7211
7222
|
lines.push("");
|
|
7212
7223
|
lines.push("## Workspace Readiness");
|
|
7213
|
-
lines.push(`Score: **${readiness.score}
|
|
7214
|
-
if (readiness.
|
|
7224
|
+
lines.push(`Score: **${readiness.score}%**${readiness.sampleTruncated ? " (provisional sample)" : ""} (${readiness.passedChecks}/${readiness.totalChecks} checks passed)`);
|
|
7225
|
+
if (readiness.sampleTruncated) lines.push(READINESS_PROVISIONAL_LINE);
|
|
7226
|
+
if (!readiness.sampleTruncated && readiness.gaps.length > 0) {
|
|
7215
7227
|
lines.push("");
|
|
7216
7228
|
lines.push("**Gaps:**");
|
|
7217
7229
|
for (const gap of readiness.gaps.slice(0, 5)) {
|
|
@@ -7228,7 +7240,7 @@ function formatSteeringBrief(result2) {
|
|
|
7228
7240
|
return {
|
|
7229
7241
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
7230
7242
|
structuredContent: success(
|
|
7231
|
-
`Steering brief: ${changes.entries.length} changes, ${delta.newEntries.length} new entries, ${readiness.score}% readiness.`,
|
|
7243
|
+
`Steering brief: ${changes.entries.length} changes, ${delta.newEntries.length} new entries, ${readiness.score}% readiness${readiness.sampleTruncated ? " (provisional sample; gaps unverified)" : ""}.`,
|
|
7232
7244
|
{ ...result2, trustFilterActive: true }
|
|
7233
7245
|
)
|
|
7234
7246
|
};
|
|
@@ -8745,19 +8757,30 @@ function sanitizeSingleLineField(str) {
|
|
|
8745
8757
|
}
|
|
8746
8758
|
|
|
8747
8759
|
// src/tools/orient-rituals.ts
|
|
8760
|
+
var SPINE_UNAVAILABLE_LINE = "Spine check: unavailable \u2014 orient fetch failed, retry `orient`";
|
|
8748
8761
|
function buildRitualsSurfaceLines(view, opts) {
|
|
8749
8762
|
const lines = [""];
|
|
8750
8763
|
if (opts?.fetchFailed) {
|
|
8751
8764
|
lines.push("Direction: unavailable \u2014 orient fetch failed, retry `orient`");
|
|
8752
|
-
lines.push(
|
|
8765
|
+
lines.push(SPINE_UNAVAILABLE_LINE);
|
|
8753
8766
|
return lines;
|
|
8754
8767
|
}
|
|
8755
8768
|
lines.push(view?.directionLine ?? "Direction: not computed yet \u2014 `pb direction refresh`");
|
|
8756
8769
|
if (view?.verdictAskLine) lines.push(view.verdictAskLine);
|
|
8757
|
-
lines.push(view
|
|
8770
|
+
if (view?.spineLine) lines.push(view.spineLine);
|
|
8758
8771
|
if (view?.atCapLine) lines.push(view.atCapLine);
|
|
8759
8772
|
return lines;
|
|
8760
8773
|
}
|
|
8774
|
+
async function fetchAndRenderSetupSpineLine(kernelQuery2) {
|
|
8775
|
+
try {
|
|
8776
|
+
const view = await kernelQuery2("chain.getRitualsSurface", {
|
|
8777
|
+
now: Math.floor(Date.now() / 6e4) * 6e4
|
|
8778
|
+
});
|
|
8779
|
+
return view?.spineLine ? [view.spineLine] : [];
|
|
8780
|
+
} catch {
|
|
8781
|
+
return [SPINE_UNAVAILABLE_LINE];
|
|
8782
|
+
}
|
|
8783
|
+
}
|
|
8761
8784
|
function toSafeCodeSpan(text) {
|
|
8762
8785
|
const longestBacktickRun = (text.match(/`+/g) ?? []).reduce((max, run) => Math.max(max, run.length), 0);
|
|
8763
8786
|
const fence = "`".repeat(longestBacktickRun + 1);
|
|
@@ -9096,15 +9119,18 @@ function pickNextTensionPrompt(openTensions) {
|
|
|
9096
9119
|
cta: "Want to discuss this tension or capture a decision about it?"
|
|
9097
9120
|
};
|
|
9098
9121
|
}
|
|
9099
|
-
async function buildSetupResponse(agentSessionId) {
|
|
9122
|
+
async function buildSetupResponse(agentSessionId, sampleTruncated) {
|
|
9100
9123
|
await kernelMutation(
|
|
9101
9124
|
"setup.stampMcpOnlySurface",
|
|
9102
9125
|
{}
|
|
9103
9126
|
);
|
|
9104
9127
|
const skill = await loadSkillBody(PB_SETUP_SKILL_ENTRY_ID);
|
|
9105
9128
|
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
9129
|
+
const spineLines = await fetchAndRenderSetupSpineLine(kernelQuery);
|
|
9106
9130
|
return {
|
|
9107
|
-
text: skill.body
|
|
9131
|
+
text: `${skill.body}${spineLines.length > 0 ? `
|
|
9132
|
+
${spineLines.join("\n")}` : ""}${sampleTruncated ? `
|
|
9133
|
+
${READINESS_PROVISIONAL_LINE} Reuse existing knowledge when following setup.` : ""}`,
|
|
9108
9134
|
structuredContent: success(
|
|
9109
9135
|
`pb-setup ready. Delivering SKILL-pb-setup body (${skill.body.length} chars).`,
|
|
9110
9136
|
{
|
|
@@ -9112,9 +9138,11 @@ async function buildSetupResponse(agentSessionId) {
|
|
|
9112
9138
|
skillEntryId: skill.entryId,
|
|
9113
9139
|
skillBody: skill.body,
|
|
9114
9140
|
stage: "setup",
|
|
9141
|
+
sampleTruncated,
|
|
9115
9142
|
oriented,
|
|
9116
9143
|
orientationStatus,
|
|
9117
9144
|
...agentSessionId ? { sessionId: agentSessionId } : {},
|
|
9145
|
+
...spineLines[0] !== void 0 ? { spineLine: spineLines[0] } : {},
|
|
9118
9146
|
instructions: "Read skillBody and follow it; use entries/orient MCP tools per PAT-227."
|
|
9119
9147
|
}
|
|
9120
9148
|
)
|
|
@@ -9149,8 +9177,9 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
9149
9177
|
errors.push(`Readiness: ${e instanceof Error ? e.message : String(e)}`);
|
|
9150
9178
|
}
|
|
9151
9179
|
const lines = [];
|
|
9152
|
-
const isLowReadiness = readiness !== null && readiness.score < 50;
|
|
9153
|
-
|
|
9180
|
+
const isLowReadiness = readiness !== null && !readiness.sampleTruncated && readiness.score < 50;
|
|
9181
|
+
if (readiness?.sampleTruncated) lines.push(READINESS_PROVISIONAL_LINE, "");
|
|
9182
|
+
const showExistingContext = readiness !== null && (readiness.sampleTruncated || readiness.score >= 50);
|
|
9154
9183
|
const stage = readiness?.stage ?? null;
|
|
9155
9184
|
const captureBehaviorNote = wsCtx.governanceMode === "open" ? "_In Open mode, user-authored captures are accepted immediately unless you ask me to keep them as drafts._" : "_Everything I capture stays pending until you confirm._";
|
|
9156
9185
|
const coherence = await buildCoherenceSection();
|
|
@@ -9208,9 +9237,9 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
9208
9237
|
lines.push("");
|
|
9209
9238
|
}
|
|
9210
9239
|
}
|
|
9211
|
-
lines.push("_Need a collection that doesn't exist yet?
|
|
9240
|
+
lines.push("_Need a collection that doesn't exist yet? Creating one is not available through this tool. Ask a platform admin to provision it, or enable the module ontology pack that owns it._");
|
|
9212
9241
|
lines.push("");
|
|
9213
|
-
} else if (
|
|
9242
|
+
} else if (showExistingContext) {
|
|
9214
9243
|
let activeBets = [];
|
|
9215
9244
|
try {
|
|
9216
9245
|
const betsEntries = await kernelQuery("chain.listEntries", { collectionSlug: "work-packages" });
|
|
@@ -9276,7 +9305,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
9276
9305
|
const committedCollections = new Set(committed.map((e) => e.collectionId));
|
|
9277
9306
|
if (committed.length >= 10 && committedCollections.size >= 3) {
|
|
9278
9307
|
lines.push("");
|
|
9279
|
-
lines.push(`
|
|
9308
|
+
lines.push(`_Context sample: ${committed.length} accepted/active entries across ${committedCollections.size} collections._`);
|
|
9280
9309
|
}
|
|
9281
9310
|
} catch {
|
|
9282
9311
|
}
|
|
@@ -9328,13 +9357,13 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
9328
9357
|
lines.push("");
|
|
9329
9358
|
}
|
|
9330
9359
|
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId, coherence?.snapshot ?? void 0);
|
|
9331
|
-
const stageLabel = stage ?? "active"
|
|
9360
|
+
const stageLabel = `${stage ?? "active"}${readiness?.sampleTruncated ? " (provisional sample)" : ""}`;
|
|
9332
9361
|
const gapCount = readiness?.gaps?.length ?? 0;
|
|
9333
9362
|
const summaryParts = [`Product Brain ready. Stage: ${stageLabel}.`];
|
|
9334
|
-
if (gapCount > 0) summaryParts.push(`${gapCount} gap${gapCount === 1 ? "" : "s"}.`);
|
|
9363
|
+
if (gapCount > 0) summaryParts.push(`${gapCount} ${readiness?.sampleTruncated ? "potential " : ""}gap${gapCount === 1 ? "" : "s"}${readiness?.sampleTruncated ? " in sampled entries; absence unverified" : ""}.`);
|
|
9335
9364
|
if (openTensions.length > 0) summaryParts.push(`${openTensions.length} open tension${openTensions.length === 1 ? "" : "s"}.`);
|
|
9336
9365
|
const next = [];
|
|
9337
|
-
if (gapCount > 0) {
|
|
9366
|
+
if (gapCount > 0 && !readiness?.sampleTruncated) {
|
|
9338
9367
|
next.push({ tool: "capture", description: "Fill a gap", parameters: {} });
|
|
9339
9368
|
}
|
|
9340
9369
|
if (openTensions.length > 0) {
|
|
@@ -9346,6 +9375,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
9346
9375
|
summaryParts.join(" "),
|
|
9347
9376
|
{
|
|
9348
9377
|
stage,
|
|
9378
|
+
sampleTruncated: readiness?.sampleTruncated ?? null,
|
|
9349
9379
|
readinessScore: readiness?.score ?? null,
|
|
9350
9380
|
totalGaps: gapCount,
|
|
9351
9381
|
openTensions: openTensions.length,
|
|
@@ -9381,14 +9411,16 @@ async function handleStartPb({ task }) {
|
|
|
9381
9411
|
};
|
|
9382
9412
|
}
|
|
9383
9413
|
let stage = null;
|
|
9414
|
+
let sampleTruncated = false;
|
|
9384
9415
|
try {
|
|
9385
9416
|
const readiness = await kernelQuery("chain.workspaceReadiness");
|
|
9386
9417
|
stage = readiness?.stage ?? "blank";
|
|
9418
|
+
sampleTruncated = readiness?.sampleTruncated === true;
|
|
9387
9419
|
} catch {
|
|
9388
9420
|
errors.push("Readiness check unavailable \u2014 showing workspace summary.");
|
|
9389
9421
|
}
|
|
9390
9422
|
if (stage === "blank" || stage === "seeded") {
|
|
9391
|
-
const result2 = await buildSetupResponse(agentSessionId);
|
|
9423
|
+
const result2 = await buildSetupResponse(agentSessionId, sampleTruncated);
|
|
9392
9424
|
return {
|
|
9393
9425
|
content: textContent(`${result2.text}
|
|
9394
9426
|
|
|
@@ -9396,7 +9428,7 @@ ${FEEDBACK_SUBMIT_HINT}`),
|
|
|
9396
9428
|
structuredContent: result2.structuredContent
|
|
9397
9429
|
};
|
|
9398
9430
|
}
|
|
9399
|
-
if (stage === "grounded" || stage === "connected") {
|
|
9431
|
+
if (!sampleTruncated && (stage === "grounded" || stage === "connected")) {
|
|
9400
9432
|
void kernelMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
9401
9433
|
});
|
|
9402
9434
|
}
|
|
@@ -9701,16 +9733,17 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9701
9733
|
} catch (e) {
|
|
9702
9734
|
errors.push(`Readiness: ${e instanceof Error ? e.message : String(e)}`);
|
|
9703
9735
|
}
|
|
9704
|
-
|
|
9736
|
+
const sampleTruncated = readiness?.sampleTruncated === true || orientView?.sampleTruncated === true;
|
|
9737
|
+
if (readiness?.stage === "blank" && !sampleTruncated) {
|
|
9705
9738
|
const scanLines = [
|
|
9706
9739
|
`# Welcome to ${wsCtx?.workspaceName ?? "your workspace"}`,
|
|
9707
9740
|
"",
|
|
9708
|
-
"
|
|
9741
|
+
"No accepted user-authored entries were found; drafts may already exist.",
|
|
9709
9742
|
"",
|
|
9710
|
-
"
|
|
9711
|
-
"
|
|
9743
|
+
"For your current task, follow the spine guidance below: discover and reuse existing knowledge before creating anything.",
|
|
9744
|
+
"Capture only what the task needs, inspect its returned status, and read it back. Accept it only if it remains a draft; already accepted or committed entries need no extra acceptance.",
|
|
9712
9745
|
"",
|
|
9713
|
-
"
|
|
9746
|
+
"Full onboarding is optional: call `orient action=start` if you want guided workspace setup. If you already ran setup, continue your task without restarting it."
|
|
9714
9747
|
];
|
|
9715
9748
|
scanLines.push(...buildRitualsSurfaceLines(orientView, { fetchFailed: orientFetchFailed || orientView?.fetchFailed === true }));
|
|
9716
9749
|
let oriented = false;
|
|
@@ -9728,16 +9761,17 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9728
9761
|
const blankResult = {
|
|
9729
9762
|
content: [{ type: "text", text: scanLines.join("\n") }],
|
|
9730
9763
|
structuredContent: success(
|
|
9731
|
-
"
|
|
9732
|
-
{ stage: "blank", readinessScore: readiness?.score ?? 0, oriented, orientationStatus: orientationStatus2, redirectHint: "
|
|
9733
|
-
[{ tool: "orient", description: "
|
|
9764
|
+
"No accepted user-authored entries found. Follow the spine guidance for your task; full onboarding is optional.",
|
|
9765
|
+
{ stage: "blank", readinessScore: readiness?.score ?? 0, oriented, orientationStatus: orientationStatus2, redirectHint: "Optional full onboarding: `orient action=start`. Continue an existing task using the spine guidance." },
|
|
9766
|
+
task ? [] : [{ tool: "orient", description: "Optional full workspace onboarding", parameters: { action: "start" } }]
|
|
9734
9767
|
)
|
|
9735
9768
|
};
|
|
9736
9769
|
reportOrientWrapperBytes(blankResult, false, resolvedTier, taskMeaningful);
|
|
9737
9770
|
return blankResult;
|
|
9738
9771
|
}
|
|
9739
|
-
const lines = [];
|
|
9740
|
-
const isLowReadiness = readiness && readiness.score < 50;
|
|
9772
|
+
const lines = sampleTruncated ? [READINESS_PROVISIONAL_LINE, ""] : [];
|
|
9773
|
+
const isLowReadiness = !sampleTruncated && readiness && readiness.score < 50;
|
|
9774
|
+
const stageQualifier = sampleTruncated ? " (provisional sample)" : "";
|
|
9741
9775
|
if (wsCtx) {
|
|
9742
9776
|
lines.push(`# ${wsCtx.workspaceName}`);
|
|
9743
9777
|
lines.push(`_Workspace \`${wsCtx.workspaceSlug}\` \u2014 Product Brain is healthy._`);
|
|
@@ -9757,7 +9791,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9757
9791
|
}
|
|
9758
9792
|
if (mode === "brief") {
|
|
9759
9793
|
const briefStage = readiness?.stage ?? (readiness?.score != null ? readiness.score < 50 ? "seeded" : "grounded" : "unknown");
|
|
9760
|
-
lines.push(`Brain stage: ${briefStage}`);
|
|
9794
|
+
lines.push(`Brain stage: ${briefStage}${stageQualifier}`);
|
|
9761
9795
|
lines.push(...buildDirectionLines(orientView));
|
|
9762
9796
|
if (orientView?.strategicContext) {
|
|
9763
9797
|
const sc = orientView.strategicContext;
|
|
@@ -9821,12 +9855,12 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9821
9855
|
lines.push("");
|
|
9822
9856
|
lines.push(...briefWna);
|
|
9823
9857
|
}
|
|
9824
|
-
const briefGaps = readiness?.gaps ?? [];
|
|
9858
|
+
const briefGaps = sampleTruncated ? [] : readiness?.gaps ?? [];
|
|
9825
9859
|
if (briefGaps.length > 0) {
|
|
9826
9860
|
const oneLiner = formatGapOneLiner(briefGaps[0]);
|
|
9827
9861
|
if (oneLiner) lines.push(oneLiner);
|
|
9828
9862
|
}
|
|
9829
|
-
const briefClassificationGaps =
|
|
9863
|
+
const briefClassificationGaps = (readiness?.gaps ?? []).filter((g) => g.id?.startsWith?.(PURPOSE_GAP_PREFIX));
|
|
9830
9864
|
if (briefClassificationGaps.length > 0) {
|
|
9831
9865
|
lines.push(`${briefClassificationGaps.length} collection(s) with weak purpose \u2014 classification may be inaccurate.`);
|
|
9832
9866
|
}
|
|
@@ -9912,10 +9946,11 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9912
9946
|
const briefResult = {
|
|
9913
9947
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
9914
9948
|
structuredContent: success(
|
|
9915
|
-
`Oriented (brief). Stage: ${readiness?.stage ?? "unknown"}.`,
|
|
9949
|
+
`Oriented (brief). Stage: ${readiness?.stage ?? "unknown"}${stageQualifier}.`,
|
|
9916
9950
|
{
|
|
9917
9951
|
mode: "brief",
|
|
9918
9952
|
stage: readiness?.stage ?? "unknown",
|
|
9953
|
+
sampleTruncated,
|
|
9919
9954
|
oriented: hasTaskGrounding ? isSessionOriented() : false,
|
|
9920
9955
|
sessionId: agentSessionId,
|
|
9921
9956
|
taskGroundingStatus: hasTaskGrounding ? "task_scoped" : task ? "missing_task_context" : "workspace_only",
|
|
@@ -9955,7 +9990,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9955
9990
|
if (isLowReadiness && wsCtx?.createdAt) {
|
|
9956
9991
|
const ageDays = Math.floor((Date.now() - wsCtx.createdAt) / (1e3 * 60 * 60 * 24));
|
|
9957
9992
|
if (ageDays >= 30) {
|
|
9958
|
-
lines.push(`Your workspace has been around for ${ageDays} days and is still at the **${orientStage}** stage.`);
|
|
9993
|
+
lines.push(`Your workspace has been around for ${ageDays} days and is still at the **${orientStage}${stageQualifier}** stage.`);
|
|
9959
9994
|
lines.push("Let's close the gaps \u2014 or if the current structure doesn't fit, we can reshape it.");
|
|
9960
9995
|
lines.push("");
|
|
9961
9996
|
}
|
|
@@ -9986,7 +10021,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9986
10021
|
lines.push("");
|
|
9987
10022
|
}
|
|
9988
10023
|
}
|
|
9989
|
-
lines.push("_Need a collection that doesn't exist yet?
|
|
10024
|
+
lines.push("_Need a collection that doesn't exist yet? Creating one is not available through this tool. Ask a platform admin to provision it, or enable the module ontology pack that owns it._");
|
|
9990
10025
|
lines.push("");
|
|
9991
10026
|
} else if (readiness) {
|
|
9992
10027
|
const fmt = (e) => {
|
|
@@ -10006,7 +10041,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
10006
10041
|
fullCoherenceSnapshot = fullCoherence.snapshot ?? void 0;
|
|
10007
10042
|
}
|
|
10008
10043
|
if (task) {
|
|
10009
|
-
lines.push(`**Brain stage: ${orientStage}.** Working on: **${task}**`);
|
|
10044
|
+
lines.push(`**Brain stage: ${orientStage}${stageQualifier}.** Working on: **${task}**`);
|
|
10010
10045
|
lines.push("");
|
|
10011
10046
|
lines.push(...buildDirectionLines(orientView));
|
|
10012
10047
|
if (orientView?.strategicContext) {
|
|
@@ -10233,7 +10268,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
10233
10268
|
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
10234
10269
|
}
|
|
10235
10270
|
} else {
|
|
10236
|
-
lines.push(`**Brain stage: ${orientStage}.**`);
|
|
10271
|
+
lines.push(`**Brain stage: ${orientStage}${stageQualifier}.**`);
|
|
10237
10272
|
lines.push("");
|
|
10238
10273
|
const scNoTaskLines = buildDirectionLines(orientView);
|
|
10239
10274
|
if (scNoTaskLines.length > 0) lines.push(...scNoTaskLines, "");
|
|
@@ -10349,10 +10384,11 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
10349
10384
|
const fullResult = {
|
|
10350
10385
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
10351
10386
|
structuredContent: success(
|
|
10352
|
-
`Oriented (full). Stage: ${orientStage}. ${isLowReadiness ? "Low readiness \u2014 gaps remain." : "Ready."}`,
|
|
10387
|
+
`Oriented (full). Stage: ${orientStage}${stageQualifier}. ${sampleTruncated ? "Provisional readiness sample." : isLowReadiness ? "Low readiness \u2014 gaps remain." : "Ready."}`,
|
|
10353
10388
|
{
|
|
10354
10389
|
mode: "full",
|
|
10355
10390
|
stage: orientStage,
|
|
10391
|
+
sampleTruncated,
|
|
10356
10392
|
oriented: hasTaskGrounding ? isSessionOriented() : false,
|
|
10357
10393
|
sessionId: agentSessionId,
|
|
10358
10394
|
taskGroundingStatus: hasTaskGrounding ? "task_scoped" : task ? "missing_task_context" : "workspace_only",
|
|
@@ -12194,6 +12230,51 @@ async function handleAuditRun(entryId, phase) {
|
|
|
12194
12230
|
};
|
|
12195
12231
|
}
|
|
12196
12232
|
|
|
12233
|
+
// src/lib/spineVerdictRead.ts
|
|
12234
|
+
function isRecord(value) {
|
|
12235
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12236
|
+
}
|
|
12237
|
+
function isStringArray(value) {
|
|
12238
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
12239
|
+
}
|
|
12240
|
+
function isStructural(value) {
|
|
12241
|
+
if (!isRecord(value) || typeof value.passed !== "boolean" || !isStringArray(value.missing) || !isRecord(value.duplicateSingletons) || !isStringArray(value.unconnectedStrategyIds) || value.visionConnectedToPurpose !== null && typeof value.visionConnectedToPurpose !== "boolean") return false;
|
|
12242
|
+
for (const key of ["purpose", "vision"]) {
|
|
12243
|
+
const ids = value.duplicateSingletons[key];
|
|
12244
|
+
if (ids !== null && !isStringArray(ids)) return false;
|
|
12245
|
+
}
|
|
12246
|
+
for (const key of [
|
|
12247
|
+
"strategiesWithUnknownConnectivity",
|
|
12248
|
+
"unconnectedObjectiveIds",
|
|
12249
|
+
"objectivesWithUnknownConnectivity",
|
|
12250
|
+
"unconnectedKeyResultIds",
|
|
12251
|
+
"keyResultsWithUnknownConnectivity"
|
|
12252
|
+
]) {
|
|
12253
|
+
if (value[key] != null && !isStringArray(value[key])) return false;
|
|
12254
|
+
}
|
|
12255
|
+
return value.visionPurposeConnectivityTruncated === void 0 || typeof value.visionPurposeConnectivityTruncated === "boolean";
|
|
12256
|
+
}
|
|
12257
|
+
function isStrategy(value) {
|
|
12258
|
+
if (!isRecord(value) || typeof value.entryId !== "string" || !isRecord(value.refusal) || typeof value.refusal.hasRefusalSignal !== "boolean") return false;
|
|
12259
|
+
return value.rogerMartin === null || isRecord(value.rogerMartin) && typeof value.rogerMartin.isStrategicChoice === "boolean" && typeof value.rogerMartin.reasoning === "string";
|
|
12260
|
+
}
|
|
12261
|
+
function isStory(value) {
|
|
12262
|
+
return value === null || isRecord(value) && typeof value.passed === "boolean" && (value.failedCriterion === null || typeof value.failedCriterion === "string") && Array.isArray(value.criteria) && value.criteria.every((item) => isRecord(item) && typeof item.id === "string" && typeof item.passed === "boolean" && typeof item.reasoning === "string");
|
|
12263
|
+
}
|
|
12264
|
+
function readSpineVerdict(value) {
|
|
12265
|
+
if (!isRecord(value) || typeof value.noSpineEntries !== "boolean") return { kind: "malformed" };
|
|
12266
|
+
if (value.noSpineEntries) return { kind: "empty" };
|
|
12267
|
+
if (!isStructural(value.structural) || !Array.isArray(value.perStrategy) || !value.perStrategy.every(isStrategy) || !isStory(value.story)) return { kind: "malformed" };
|
|
12268
|
+
for (const key of ["strategiesTruncated", "perStrategyEvaluationTruncated", "objectivesTruncated", "keyResultsTruncated"]) {
|
|
12269
|
+
if (value[key] !== void 0 && typeof value[key] !== "boolean") return { kind: "malformed" };
|
|
12270
|
+
}
|
|
12271
|
+
return { kind: "graded", verdict: value };
|
|
12272
|
+
}
|
|
12273
|
+
function describeSpineStaleness(changedMemberCount, membershipTruncated = false) {
|
|
12274
|
+
const reason = changedMemberCount > 0 ? `${membershipTruncated ? "at least " : ""}${changedMemberCount} entr${changedMemberCount === 1 ? "y" : "ies"} changed or joined/left the spine since this was computed` : "freshness could not be verified; the changed-entry count is unknown";
|
|
12275
|
+
return reason + (membershipTruncated ? "; membership scan incomplete" : "");
|
|
12276
|
+
}
|
|
12277
|
+
|
|
12197
12278
|
// src/tools/spine-check.ts
|
|
12198
12279
|
async function handleSpineCheck(story, refresh = false) {
|
|
12199
12280
|
const scheduleResult = await kernelMutation(
|
|
@@ -12203,26 +12284,59 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
12203
12284
|
const row = await kernelQuery("quality.getLatestSpineVerdict", {});
|
|
12204
12285
|
const stillRunning = scheduleResult.scheduled;
|
|
12205
12286
|
if (!row) {
|
|
12206
|
-
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.";
|
|
12287
|
+
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." : scheduleResult.reason === "already-in-flight" ? "A spine check is already running \u2014 no prior result exists yet. Re-run quality action=spine-check in a few seconds to read its result." : "No spine check has been run yet, and none is currently running. Pass refresh:true to start one.";
|
|
12207
12288
|
return {
|
|
12208
12289
|
content: [{ type: "text", text }],
|
|
12209
|
-
structuredContent: success(text, {
|
|
12290
|
+
structuredContent: success(text, {
|
|
12291
|
+
hasVerdict: false,
|
|
12292
|
+
checkStarted: stillRunning,
|
|
12293
|
+
stillRunning: stillRunning || scheduleResult.reason === "already-in-flight"
|
|
12294
|
+
})
|
|
12210
12295
|
};
|
|
12211
12296
|
}
|
|
12212
|
-
const
|
|
12213
|
-
|
|
12297
|
+
const parsed = readSpineVerdict(row.verdict);
|
|
12298
|
+
const freshnessData = { stale: row.stale, staleMemberIds: row.staleMemberIds, membershipTruncated: row.membershipTruncated ?? false };
|
|
12299
|
+
const staleReason = describeSpineStaleness(row.staleMemberIds.length, row.membershipTruncated);
|
|
12300
|
+
if (parsed.kind === "empty") {
|
|
12301
|
+
const pending = stillRunning || scheduleResult.reason === "already-in-flight";
|
|
12302
|
+
const freshness = (row.stale ? ` Stale: ${staleReason}.` : "") + (pending ? " A check is running; re-run quality action=spine-check in a few seconds to read its result." : row.stale ? " Run quality action=spine-check refresh=true to refresh it." : "");
|
|
12303
|
+
const text = `The last spine check found no accepted purpose, vision or strategy.${freshness}`;
|
|
12214
12304
|
return {
|
|
12215
|
-
content: [{ type: "text", text
|
|
12216
|
-
structuredContent: success(
|
|
12305
|
+
content: [{ type: "text", text }],
|
|
12306
|
+
structuredContent: success(text, {
|
|
12307
|
+
hasVerdict: true,
|
|
12308
|
+
noSpineEntries: true,
|
|
12309
|
+
...freshnessData,
|
|
12310
|
+
stillRunning: pending,
|
|
12311
|
+
scheduled: scheduleResult.scheduled,
|
|
12312
|
+
scheduleReason: scheduleResult.reason
|
|
12313
|
+
})
|
|
12217
12314
|
};
|
|
12218
12315
|
}
|
|
12316
|
+
if (parsed.kind === "malformed") {
|
|
12317
|
+
const pending = stillRunning || scheduleResult.reason === "already-in-flight";
|
|
12318
|
+
const text = `Stored spine verdict is malformed \u2014 ${pending ? "a check is running; re-run quality action=spine-check in a few seconds to read its result." : "run quality action=spine-check refresh=true to replace it."}`;
|
|
12319
|
+
return {
|
|
12320
|
+
content: [{ type: "text", text }],
|
|
12321
|
+
structuredContent: success(text, {
|
|
12322
|
+
hasVerdict: true,
|
|
12323
|
+
malformed: true,
|
|
12324
|
+
...freshnessData,
|
|
12325
|
+
scheduled: scheduleResult.scheduled,
|
|
12326
|
+
stillRunning: pending,
|
|
12327
|
+
scheduleReason: scheduleResult.reason
|
|
12328
|
+
})
|
|
12329
|
+
};
|
|
12330
|
+
}
|
|
12331
|
+
const { verdict } = parsed;
|
|
12219
12332
|
const lines = ["## Spine Check"];
|
|
12220
12333
|
if (stillRunning) {
|
|
12221
12334
|
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.`, "");
|
|
12222
12335
|
} else if (scheduleResult.reason === "already-in-flight") {
|
|
12223
12336
|
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.`, "");
|
|
12224
|
-
}
|
|
12225
|
-
|
|
12337
|
+
}
|
|
12338
|
+
if (row.stale) {
|
|
12339
|
+
lines.push(`> STALE \u2014 ${staleReason}.${stillRunning || scheduleResult.reason === "already-in-flight" ? "" : " Pass refresh:true to recompute."}`, "");
|
|
12226
12340
|
}
|
|
12227
12341
|
const s = verdict.structural;
|
|
12228
12342
|
lines.push(`Structural completeness: ${s.passed ? "PASS" : "FAIL"}`);
|
|
@@ -12249,7 +12363,7 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
12249
12363
|
lines.push("", `One-story judgment [unvalidated, on-demand]: ${verdict.story.passed ? "PASS" : "FAIL"}`);
|
|
12250
12364
|
if (!verdict.story.passed && verdict.story.failedCriterion) lines.push(`Failed criterion: ${verdict.story.failedCriterion}`);
|
|
12251
12365
|
} else if (row.includesStoryPass) {
|
|
12252
|
-
lines.push("", "One-story judgment: requested, but the LLM pass did not return a usable result (timeout or parse failure).
|
|
12366
|
+
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.");
|
|
12253
12367
|
} else {
|
|
12254
12368
|
lines.push("", "One-story judgment: not run this pass \u2014 pass story=true to run it.");
|
|
12255
12369
|
}
|
|
@@ -12262,7 +12376,7 @@ async function handleSpineCheck(story, refresh = false) {
|
|
|
12262
12376
|
noSpineEntries: false,
|
|
12263
12377
|
structuralPassed: s.passed,
|
|
12264
12378
|
storyPassed: verdict.story?.passed ?? null,
|
|
12265
|
-
|
|
12379
|
+
...freshnessData,
|
|
12266
12380
|
stillRunning,
|
|
12267
12381
|
scheduled: scheduleResult.scheduled,
|
|
12268
12382
|
scheduleReason: scheduleResult.reason
|
|
@@ -12351,7 +12465,12 @@ var qualitySpineCheckOutputSchema = z24.object({
|
|
|
12351
12465
|
noSpineEntries: z24.boolean().optional(),
|
|
12352
12466
|
structuralPassed: z24.boolean().optional(),
|
|
12353
12467
|
storyPassed: z24.boolean().nullable().optional(),
|
|
12354
|
-
stale: z24.boolean().optional()
|
|
12468
|
+
stale: z24.boolean().optional(),
|
|
12469
|
+
// TASK-156: a capped membership scan must stay distinguishable from a complete one, and a
|
|
12470
|
+
// malformed verdict needs its own discriminator — both were silently stripped before this.
|
|
12471
|
+
staleMemberIds: z24.array(z24.string()).optional(),
|
|
12472
|
+
membershipTruncated: z24.boolean().optional(),
|
|
12473
|
+
malformed: z24.boolean().optional()
|
|
12355
12474
|
});
|
|
12356
12475
|
function registerQualityTools(server) {
|
|
12357
12476
|
const qualityHandlers = {
|
|
@@ -14629,8 +14748,8 @@ async function handleWorkspaceStatus() {
|
|
|
14629
14748
|
const { score, totalChecks, passedChecks, checks, gaps, stats, governanceMode } = result2;
|
|
14630
14749
|
const scoringVersion = result2.scoringVersion ?? "v1";
|
|
14631
14750
|
const stage = result2.stage ?? "seeded";
|
|
14632
|
-
const stageLabel = STAGE_LABELS[stage] ?? stage
|
|
14633
|
-
const stageDescription = STAGE_DESCRIPTIONS[stage] ?? "";
|
|
14751
|
+
const stageLabel = `${STAGE_LABELS[stage] ?? stage}${result2.sampleTruncated ? " (provisional sample)" : ""}`;
|
|
14752
|
+
const stageDescription = result2.sampleTruncated ? READINESS_PROVISIONAL_LINE : STAGE_DESCRIPTIONS[stage] ?? "";
|
|
14634
14753
|
const scoreBar = "\u2588".repeat(Math.round(score / 10)) + "\u2591".repeat(10 - Math.round(score / 10));
|
|
14635
14754
|
const lines = [
|
|
14636
14755
|
`# Brain Status: ${stageLabel}`,
|
|
@@ -14639,14 +14758,14 @@ async function handleWorkspaceStatus() {
|
|
|
14639
14758
|
`${scoreBar} ${stageLabel} \xB7 ${score}%`,
|
|
14640
14759
|
`**Governance:** ${governanceMode ?? "open"}${(governanceMode ?? "open") !== "open" ? " (accepts require proposal)" : ""}`,
|
|
14641
14760
|
"",
|
|
14642
|
-
"##
|
|
14643
|
-
`- **Entries:** ${stats.totalEntries} (${stats.activeCount} active, ${stats.draftCount} draft)`,
|
|
14761
|
+
"## Sampled counts",
|
|
14762
|
+
`- **Entries:** ${stats.totalEntries} (${stats.activeCount} accepted/active, ${stats.draftCount} draft)`,
|
|
14644
14763
|
`- **Relations:** ${stats.totalRelations}`,
|
|
14645
14764
|
`- **Collections:** ${stats.collectionCount}`,
|
|
14646
14765
|
`- **Orphaned:** ${stats.orphanedCount} committed entries with no relations`,
|
|
14647
14766
|
""
|
|
14648
14767
|
];
|
|
14649
|
-
if (gaps.length > 0) {
|
|
14768
|
+
if (gaps.length > 0 && !result2.sampleTruncated) {
|
|
14650
14769
|
lines.push("## Gaps");
|
|
14651
14770
|
for (const gap of gaps) {
|
|
14652
14771
|
const action = gap.capabilityGuidance ?? gap.guidance;
|
|
@@ -14657,7 +14776,7 @@ async function handleWorkspaceStatus() {
|
|
|
14657
14776
|
}
|
|
14658
14777
|
const passed = checks.filter((c) => c.passed);
|
|
14659
14778
|
if (passed.length > 0) {
|
|
14660
|
-
lines.push("## Passing checks");
|
|
14779
|
+
lines.push(result2.sampleTruncated ? "## Checks observed in the provisional sample" : "## Passing checks");
|
|
14661
14780
|
for (const check of passed) {
|
|
14662
14781
|
lines.push(`- [x] ${check.label} (${check.current}/${check.required})`);
|
|
14663
14782
|
}
|
|
@@ -14671,6 +14790,7 @@ async function handleWorkspaceStatus() {
|
|
|
14671
14790
|
}
|
|
14672
14791
|
const statusData = {
|
|
14673
14792
|
stage,
|
|
14793
|
+
sampleTruncated: result2.sampleTruncated ?? null,
|
|
14674
14794
|
scoringVersion,
|
|
14675
14795
|
readinessScore: score,
|
|
14676
14796
|
activeEntries: stats.activeCount,
|
|
@@ -14679,14 +14799,14 @@ async function handleWorkspaceStatus() {
|
|
|
14679
14799
|
gaps: gaps.map((g) => ({
|
|
14680
14800
|
id: g.id,
|
|
14681
14801
|
label: g.label,
|
|
14682
|
-
guidance: g.capabilityGuidance ?? g.guidance
|
|
14802
|
+
guidance: result2.sampleTruncated ? "Unverified gap in sampled entries. Review existing knowledge before adding more." : g.capabilityGuidance ?? g.guidance
|
|
14683
14803
|
})),
|
|
14684
14804
|
...orgHealth && { organisationHealth: orgHealth }
|
|
14685
14805
|
};
|
|
14686
14806
|
return {
|
|
14687
14807
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
14688
14808
|
structuredContent: success(
|
|
14689
|
-
`Brain: ${stageLabel} (${score}%). ${stats.activeCount} active entries, ${gaps.length} gap(s).`,
|
|
14809
|
+
`Brain: ${stageLabel} (${score}%). ${stats.activeCount} accepted/active entries in the sample, ${gaps.length} ${result2.sampleTruncated ? "potential gap(s); absence unverified" : "gap(s)"}.`,
|
|
14690
14810
|
statusData
|
|
14691
14811
|
)
|
|
14692
14812
|
};
|
|
@@ -14747,6 +14867,7 @@ var organisationHealthSchema = z29.object({
|
|
|
14747
14867
|
});
|
|
14748
14868
|
var healthStatusOutputSchema = z29.object({
|
|
14749
14869
|
stage: z29.enum(["blank", "seeded", "grounded", "connected"]).optional().default("seeded"),
|
|
14870
|
+
sampleTruncated: z29.boolean().nullable().optional(),
|
|
14750
14871
|
scoringVersion: z29.enum(["v1", "v2"]).optional().default("v1"),
|
|
14751
14872
|
readinessScore: z29.number(),
|
|
14752
14873
|
activeEntries: z29.number(),
|
|
@@ -16941,4 +17062,4 @@ export {
|
|
|
16941
17062
|
createProductBrainServer,
|
|
16942
17063
|
initFeatureFlags
|
|
16943
17064
|
};
|
|
16944
|
-
//# sourceMappingURL=chunk-
|
|
17065
|
+
//# sourceMappingURL=chunk-7T4HSTNG.js.map
|