@productbrain/mcp 0.0.1-beta.204 → 0.0.1-beta.206
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.
|
@@ -478,6 +478,13 @@ async function getWorkspaceContext() {
|
|
|
478
478
|
governanceMode: s.workspaceGovernanceMode ?? "open"
|
|
479
479
|
};
|
|
480
480
|
}
|
|
481
|
+
async function refreshWorkspaceGovernanceMode() {
|
|
482
|
+
const workspace = await kernelCall("resolveWorkspace", {});
|
|
483
|
+
const mode = workspace?.governanceMode ?? "open";
|
|
484
|
+
const s = state();
|
|
485
|
+
s.workspaceGovernanceMode = mode;
|
|
486
|
+
return mode;
|
|
487
|
+
}
|
|
481
488
|
async function kernelQuery(fn, args = {}) {
|
|
482
489
|
const workspaceId = await getWorkspaceId();
|
|
483
490
|
return kernelCall(fn, { ...args, workspaceId });
|
|
@@ -2425,19 +2432,19 @@ ${groundingReport.governance.map((g) => `- **${g.entryId}** ${g.name} [${g.colle
|
|
|
2425
2432
|
}
|
|
2426
2433
|
}
|
|
2427
2434
|
canonicalizeSelects(data, col.fields ?? [], isBetCapture);
|
|
2428
|
-
const status = "draft";
|
|
2429
2435
|
const agentId = getAgentSessionId();
|
|
2430
2436
|
let finalEntryId;
|
|
2431
2437
|
let internalId;
|
|
2432
2438
|
let normalizationMeta;
|
|
2433
2439
|
let formativeHints = [];
|
|
2434
2440
|
let relationSuggestionsFromCreate = [];
|
|
2441
|
+
let wasAutoCommittedServerSide = false;
|
|
2435
2442
|
try {
|
|
2436
2443
|
const result = await kernelMutation("chain.createEntry", {
|
|
2437
2444
|
collectionSlug: resolvedCollection,
|
|
2438
2445
|
entryId: entryId ?? void 0,
|
|
2439
2446
|
name,
|
|
2440
|
-
status,
|
|
2447
|
+
// DEC-896 / BR-76: omit status — server resolves from governanceMode (Open→active, else draft).
|
|
2441
2448
|
data,
|
|
2442
2449
|
canonicalKey,
|
|
2443
2450
|
createdBy: agentId ? `agent:${agentId}` : "capture",
|
|
@@ -2486,6 +2493,9 @@ No DB writes \u2014 call without \`preview:true\` to capture for real.${result.w
|
|
|
2486
2493
|
relationSuggestionsFromCreate = result.relationSuggestions ?? [];
|
|
2487
2494
|
formativeHints = result.qualityHints ?? [];
|
|
2488
2495
|
resolveGapsForEntry(name, result.entryId);
|
|
2496
|
+
if (result.status === "active") {
|
|
2497
|
+
wasAutoCommittedServerSide = true;
|
|
2498
|
+
}
|
|
2489
2499
|
} catch (error) {
|
|
2490
2500
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2491
2501
|
if (msg.includes("Duplicate") || msg.includes("already exists")) {
|
|
@@ -2682,7 +2692,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2682
2692
|
} catch {
|
|
2683
2693
|
}
|
|
2684
2694
|
}
|
|
2685
|
-
const
|
|
2695
|
+
const freshGovernanceMode = await refreshWorkspaceGovernanceMode();
|
|
2696
|
+
const shouldAutoCommit = shouldAutoCommitCapture(autoCommit, freshGovernanceMode);
|
|
2686
2697
|
if (shouldAutoCommit && finalEntryId && conflictCandidates.length === 0) {
|
|
2687
2698
|
try {
|
|
2688
2699
|
const conflictResults = await kernelQuery("chain.searchEntries", { query: name });
|
|
@@ -2692,7 +2703,16 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2692
2703
|
}
|
|
2693
2704
|
let finalStatus = "draft";
|
|
2694
2705
|
let commitError = null;
|
|
2695
|
-
if (
|
|
2706
|
+
if (wasAutoCommittedServerSide) {
|
|
2707
|
+
finalStatus = "committed";
|
|
2708
|
+
await recordSessionActivity({ entryModified: internalId });
|
|
2709
|
+
trackChainEntryCommitted(wsCtx.workspaceId, {
|
|
2710
|
+
entry_id: finalEntryId,
|
|
2711
|
+
collection: resolvedCollection,
|
|
2712
|
+
commit_method: "auto",
|
|
2713
|
+
surface: "mcp_capture"
|
|
2714
|
+
});
|
|
2715
|
+
} else if (shouldAutoCommit && finalEntryId) {
|
|
2696
2716
|
try {
|
|
2697
2717
|
const semanticConflicts = await runSemanticConflictPreflight(name, description, resolvedCollection);
|
|
2698
2718
|
const blockingConflicts = semanticConflicts.filter(
|
|
@@ -2719,6 +2739,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2719
2739
|
}
|
|
2720
2740
|
} catch (e) {
|
|
2721
2741
|
commitError = e instanceof Error ? e.message : "unknown error";
|
|
2742
|
+
finalStatus = "draft_on_failure";
|
|
2743
|
+
await recordCommitFailure({ entryId: finalEntryId, error: e, sessionId: agentId, server });
|
|
2722
2744
|
}
|
|
2723
2745
|
}
|
|
2724
2746
|
const lines = [
|
|
@@ -2770,7 +2792,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2770
2792
|
for (const r of userLinkResults) lines.push(`- ${r.label}`);
|
|
2771
2793
|
}
|
|
2772
2794
|
if (finalStatus === "committed") {
|
|
2773
|
-
const wasAutoCommitted = autoCommit === void 0 &&
|
|
2795
|
+
const wasAutoCommitted = autoCommit === void 0 && freshGovernanceMode === "open";
|
|
2774
2796
|
lines.push("");
|
|
2775
2797
|
lines.push(`## Committed: ${finalEntryId}`);
|
|
2776
2798
|
if (wasAutoCommitted) {
|
|
@@ -3026,7 +3048,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3026
3048
|
const agentId = getAgentSessionId();
|
|
3027
3049
|
const createdBy = agentId ? `agent:${agentId}` : "capture";
|
|
3028
3050
|
const wsCtx = await getWorkspaceContext();
|
|
3029
|
-
const
|
|
3051
|
+
const freshGovernanceModeForBatch = await refreshWorkspaceGovernanceMode();
|
|
3052
|
+
const autoCommitApplied = shouldAutoCommitCapture(autoCommit, freshGovernanceModeForBatch);
|
|
3030
3053
|
const needsClassification = entries.map((e, i) => ({ ...e, index: i })).filter((e) => !e.collection);
|
|
3031
3054
|
let classificationMap = /* @__PURE__ */ new Map();
|
|
3032
3055
|
if (needsClassification.length > 0) {
|
|
@@ -3194,11 +3217,13 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3194
3217
|
collectionSlug: resolvedSlug,
|
|
3195
3218
|
entryId: entry.entryId ?? void 0,
|
|
3196
3219
|
name: entry.name,
|
|
3197
|
-
|
|
3220
|
+
// DEC-896 / BR-76: omit status — server resolves from governanceMode (Open→active, else draft).
|
|
3198
3221
|
data,
|
|
3199
3222
|
createdBy,
|
|
3200
3223
|
sessionId: agentId ?? void 0,
|
|
3201
3224
|
canonicalKey: entry.canonicalKey ?? void 0,
|
|
3225
|
+
// BR-76 S2: Distinguish batch provenance in retrospective metrics (path a).
|
|
3226
|
+
originDetailOverride: "mcp-batch-capture",
|
|
3202
3227
|
...entry.sourceRef ? { sourceRef: entry.sourceRef } : {},
|
|
3203
3228
|
...entry.sourceExcerpt ? { sourceExcerpt: entry.sourceExcerpt } : {},
|
|
3204
3229
|
...preview ? { preview: true } : {}
|
|
@@ -3210,8 +3235,18 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3210
3235
|
...result.warnings ?? []
|
|
3211
3236
|
];
|
|
3212
3237
|
resolveGapsForEntry(entry.name, result.entryId);
|
|
3213
|
-
|
|
3238
|
+
const batchWasAutoCommittedServerSide = result.status === "active";
|
|
3239
|
+
let finalStatus = batchWasAutoCommittedServerSide ? "committed" : "draft";
|
|
3214
3240
|
let commitError;
|
|
3241
|
+
if (batchWasAutoCommittedServerSide) {
|
|
3242
|
+
await recordSessionActivity({ entryModified: internalId });
|
|
3243
|
+
trackChainEntryCommitted(wsCtx.workspaceId, {
|
|
3244
|
+
entry_id: finalEntryId,
|
|
3245
|
+
collection: resolvedSlug ?? void 0,
|
|
3246
|
+
commit_method: "auto",
|
|
3247
|
+
surface: "mcp_capture"
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3215
3250
|
let autoLinkCount = 0;
|
|
3216
3251
|
let entryOverlapCount = 0;
|
|
3217
3252
|
const searchQuery = extractSearchTerms(entry.name, entry.description);
|
|
@@ -3241,7 +3276,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3241
3276
|
} catch {
|
|
3242
3277
|
}
|
|
3243
3278
|
}
|
|
3244
|
-
if (autoCommitApplied) {
|
|
3279
|
+
if (autoCommitApplied && !batchWasAutoCommittedServerSide) {
|
|
3245
3280
|
try {
|
|
3246
3281
|
const semanticConflicts = await runSemanticConflictPreflight(entry.name, entry.description, resolvedSlug);
|
|
3247
3282
|
const blockingConflicts = semanticConflicts.filter(
|
|
@@ -3268,6 +3303,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3268
3303
|
}
|
|
3269
3304
|
} catch (error) {
|
|
3270
3305
|
commitError = error instanceof Error ? error.message : String(error);
|
|
3306
|
+
finalStatus = "draft_on_failure";
|
|
3307
|
+
await recordCommitFailure({ entryId: finalEntryId, error, sessionId: agentId, server });
|
|
3271
3308
|
}
|
|
3272
3309
|
}
|
|
3273
3310
|
const entryNorm = result.normalization;
|
|
@@ -3310,7 +3347,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3310
3347
|
const committed = created.filter((r) => r.status === "committed");
|
|
3311
3348
|
const proposed = created.filter((r) => r.status === "proposed");
|
|
3312
3349
|
const drafts = created.filter((r) => r.status === "draft");
|
|
3313
|
-
const
|
|
3350
|
+
const commitFailed = created.filter((r) => r.status === "draft_on_failure");
|
|
3314
3351
|
const classifiedCount = created.filter((r) => r.classifiedBy && r.classifiedBy !== "explicit").length;
|
|
3315
3352
|
await server.sendLoggingMessage({
|
|
3316
3353
|
level: "info",
|
|
@@ -3333,8 +3370,9 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
3333
3370
|
lines.push("");
|
|
3334
3371
|
}
|
|
3335
3372
|
if (created.length > 0) {
|
|
3373
|
+
const commitFailedSegment = commitFailed.length > 0 ? `, ${commitFailed.length} commit-failed` : "";
|
|
3336
3374
|
lines.push(
|
|
3337
|
-
`**Statuses:** ${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft.`
|
|
3375
|
+
`**Statuses:** ${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft${commitFailedSegment}.`
|
|
3338
3376
|
);
|
|
3339
3377
|
lines.push("");
|
|
3340
3378
|
}
|
|
@@ -3378,10 +3416,10 @@ _Use \`move-entry\` to correct any misclassified entries._`);
|
|
|
3378
3416
|
lines.push("");
|
|
3379
3417
|
lines.push("_Re-capture these with an explicit `collection` or use the suggested collection._");
|
|
3380
3418
|
}
|
|
3381
|
-
if (
|
|
3419
|
+
if (commitFailed.length > 0) {
|
|
3382
3420
|
lines.push("");
|
|
3383
3421
|
lines.push("## Saved as draft");
|
|
3384
|
-
for (const r of
|
|
3422
|
+
for (const r of commitFailed) {
|
|
3385
3423
|
lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 ${r.commitError}`);
|
|
3386
3424
|
}
|
|
3387
3425
|
}
|
|
@@ -3432,7 +3470,8 @@ _Use \`move-entry\` to correct any misclassified entries._`);
|
|
|
3432
3470
|
}
|
|
3433
3471
|
const skippedNote = skippedLowConfidence.length > 0 ? `, ${skippedLowConfidence.length} skipped (low confidence)` : "";
|
|
3434
3472
|
const classifiedNote = classifiedCount > 0 ? `, ${classifiedCount} auto-classified` : "";
|
|
3435
|
-
const
|
|
3473
|
+
const commitFailedNote = commitFailed.length > 0 ? `, ${commitFailed.length} commit-failed` : "";
|
|
3474
|
+
const summary = failed.length > 0 || skippedLowConfidence.length > 0 || commitFailed.length > 0 ? `Batch captured ${created.length}/${entries.length} entries (${failed.length} failed${skippedNote}, ${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft${commitFailedNote}${classifiedNote}).` : `Batch captured ${created.length} entries successfully (${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft${classifiedNote}).`;
|
|
3436
3475
|
const firstDraft = drafts[0];
|
|
3437
3476
|
const next = [];
|
|
3438
3477
|
if (created.length > 0) {
|
|
@@ -3464,13 +3503,15 @@ _Use \`move-entry\` to correct any misclassified entries._`);
|
|
|
3464
3503
|
...r.confidence != null ? { confidence: r.confidence } : {},
|
|
3465
3504
|
...r.confidenceTier ? { confidenceTier: r.confidenceTier } : {},
|
|
3466
3505
|
...r.warnings?.length ? { warnings: r.warnings } : {},
|
|
3467
|
-
...r.normalization ? { normalization: r.normalization } : {}
|
|
3506
|
+
...r.normalization ? { normalization: r.normalization } : {},
|
|
3507
|
+
...r.commitError ? { commitError: r.commitError } : {}
|
|
3468
3508
|
})),
|
|
3469
3509
|
total: created.length,
|
|
3470
3510
|
failed: failed.length,
|
|
3471
3511
|
committed: committed.length,
|
|
3472
3512
|
proposed: proposed.length,
|
|
3473
3513
|
drafts: drafts.length,
|
|
3514
|
+
...commitFailed.length > 0 ? { commitFailed: commitFailed.length } : {},
|
|
3474
3515
|
...classifiedCount > 0 ? { classified: classifiedCount } : {},
|
|
3475
3516
|
autoCommitApplied,
|
|
3476
3517
|
...skippedLowConfidence.length > 0 && {
|
|
@@ -3500,6 +3541,35 @@ _Use \`move-entry\` to correct any misclassified entries._`);
|
|
|
3500
3541
|
);
|
|
3501
3542
|
trackWriteTool(batchCaptureTool);
|
|
3502
3543
|
}
|
|
3544
|
+
async function recordCommitFailure({
|
|
3545
|
+
entryId,
|
|
3546
|
+
error,
|
|
3547
|
+
sessionId,
|
|
3548
|
+
server
|
|
3549
|
+
}) {
|
|
3550
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3551
|
+
const logLine = `commitEntry failed for ${entryId}: ${errorMessage}${sessionId ? ` (session: ${sessionId})` : ""}`;
|
|
3552
|
+
try {
|
|
3553
|
+
await server.sendLoggingMessage({ level: "error", data: logLine, logger: "product-brain" });
|
|
3554
|
+
} catch {
|
|
3555
|
+
}
|
|
3556
|
+
if (sessionId) {
|
|
3557
|
+
try {
|
|
3558
|
+
const tenName = `commitEntry failed for ${entryId} \u2014 ${errorMessage}`.slice(0, 250);
|
|
3559
|
+
await kernelMutation("chain.createEntry", {
|
|
3560
|
+
collectionSlug: "tensions",
|
|
3561
|
+
name: tenName,
|
|
3562
|
+
// Failure audit TENs intentionally stay draft — they need explicit human review,
|
|
3563
|
+
// not auto-commit, even in Open mode.
|
|
3564
|
+
status: "draft",
|
|
3565
|
+
data: {},
|
|
3566
|
+
createdBy: `agent:${sessionId}`,
|
|
3567
|
+
sessionId
|
|
3568
|
+
});
|
|
3569
|
+
} catch {
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3503
3573
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
3504
3574
|
"the",
|
|
3505
3575
|
"and",
|
|
@@ -10436,128 +10506,41 @@ function registerVerifyTools(server) {
|
|
|
10436
10506
|
trackWriteTool(verifyEntryTool);
|
|
10437
10507
|
}
|
|
10438
10508
|
|
|
10439
|
-
// src/tools/
|
|
10509
|
+
// src/tools/start_pb.ts
|
|
10440
10510
|
import { z as z17 } from "zod";
|
|
10441
10511
|
|
|
10442
|
-
// src/
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
{
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
{ key: "rationale", label: "Rationale", type: "string", searchable: true },
|
|
10475
|
-
{ key: "domain", label: "Domain", type: "string" },
|
|
10476
|
-
{ key: "severity", label: "Severity", type: "select", options: ["low", "medium", "high", "critical"] },
|
|
10477
|
-
{ key: "platforms", label: "Platforms", type: "array" }
|
|
10478
|
-
]
|
|
10479
|
-
}
|
|
10480
|
-
];
|
|
10481
|
-
var COLLECTION_PRESETS = [
|
|
10482
|
-
{
|
|
10483
|
-
id: "software-product",
|
|
10484
|
-
name: "Software Product",
|
|
10485
|
-
description: "For teams building software products \u2014 glossary, features, architecture, tech debt, and API endpoints",
|
|
10486
|
-
collections: [
|
|
10487
|
-
...GOVERNANCE_CORE,
|
|
10488
|
-
{ slug: "glossary", name: "Glossary", description: "Canonical terminology for the product domain", idPrefix: "GLO", fields: [{ key: "canonical", label: "Canonical", type: "string", required: true, searchable: true }, { key: "category", label: "Category", type: "select", options: ["Platform & Architecture", "Knowledge Management", "AI & Developer Tools", "Governance & Process"] }, { key: "confusedWith", label: "Confused With", type: "array" }] },
|
|
10489
|
-
{ slug: "features", name: "Features", description: "Product features and capabilities", idPrefix: "FEAT", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "scope", label: "Scope", type: "string" }, { key: "area", label: "Area", type: "string" }, { key: "status", label: "Status", type: "select", options: ["proposed", "in-progress", "shipped", "deprecated"] }] },
|
|
10490
|
-
{ slug: "architecture", name: "Architecture", description: "System architecture layers and components", idPrefix: "ARCH", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "layer", label: "Layer", type: "string" }, { key: "dependencies", label: "Dependencies", type: "array" }] },
|
|
10491
|
-
{ slug: "tech-debt", name: "Tech Debt", description: "Technical debt items to track and address", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "severity", label: "Severity", type: "select", options: ["low", "medium", "high", "critical"] }, { key: "area", label: "Area", type: "string" }, { key: "effort", label: "Effort", type: "select", options: ["small", "medium", "large"] }] },
|
|
10492
|
-
{ slug: "api-endpoints", name: "API Endpoints", description: "REST/GraphQL endpoints and their contracts", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "method", label: "Method", type: "select", options: ["GET", "POST", "PUT", "PATCH", "DELETE"] }, { key: "path", label: "Path", type: "string" }, { key: "auth", label: "Auth Required", type: "select", options: ["none", "api-key", "bearer", "session"] }] },
|
|
10493
|
-
{ slug: "decisions", name: "Decisions", description: "Significant decisions with rationale and context", idPrefix: "DEC", fields: [{ key: "rationale", label: "Rationale", type: "string", required: true, searchable: true }, { key: "date", label: "Date", type: "string" }, { key: "decidedBy", label: "Decided By", type: "string" }, { key: "alternatives", label: "Alternatives", type: "string" }] },
|
|
10494
|
-
{ slug: "tensions", name: "Tensions", description: "Friction points, pain points, and unmet needs", idPrefix: "TEN", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "priority", label: "Priority", type: "select", options: ["low", "medium", "high", "critical"] }, { key: "severity", label: "Severity", type: "select", options: ["low", "medium", "high", "critical"] }] }
|
|
10495
|
-
]
|
|
10496
|
-
},
|
|
10497
|
-
{
|
|
10498
|
-
id: "content-business",
|
|
10499
|
-
name: "Content Business",
|
|
10500
|
-
description: "For content creators, publishers, and media companies \u2014 topics, audience segments, content calendar, and brand voice",
|
|
10501
|
-
collections: [
|
|
10502
|
-
...GOVERNANCE_CORE,
|
|
10503
|
-
{ slug: "glossary", name: "Glossary", description: "Industry terminology and brand language", fields: [{ key: "canonical", label: "Canonical", type: "string", required: true, searchable: true }, { key: "category", label: "Category", type: "string" }] },
|
|
10504
|
-
{ slug: "topics", name: "Topics", description: "Content topics and themes", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "pillar", label: "Content Pillar", type: "string" }, { key: "stage", label: "Stage", type: "select", options: ["idea", "researching", "drafting", "published", "evergreen"] }] },
|
|
10505
|
-
{ slug: "audience-segments", name: "Audience Segments", description: "Target reader/viewer personas", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "painPoints", label: "Pain Points", type: "string" }, { key: "channels", label: "Channels", type: "array" }] },
|
|
10506
|
-
{ slug: "brand-voice", name: "Brand Voice", description: "Tone, style, and voice guidelines", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "doThis", label: "Do This", type: "string" }, { key: "notThis", label: "Not This", type: "string" }] },
|
|
10507
|
-
{ slug: "decisions", name: "Decisions", description: "Editorial and strategic decisions", fields: [{ key: "rationale", label: "Rationale", type: "string", required: true, searchable: true }, { key: "date", label: "Date", type: "string" }, { key: "decidedBy", label: "Decided By", type: "string" }] },
|
|
10508
|
-
{ slug: "tensions", name: "Tensions", description: "Content gaps, audience friction, and unmet needs", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "priority", label: "Priority", type: "select", options: ["low", "medium", "high", "critical"] }] }
|
|
10509
|
-
]
|
|
10510
|
-
},
|
|
10511
|
-
{
|
|
10512
|
-
id: "agency",
|
|
10513
|
-
name: "Agency",
|
|
10514
|
-
description: "For agencies managing multiple clients \u2014 client profiles, project scopes, deliverables, and processes",
|
|
10515
|
-
collections: [
|
|
10516
|
-
...GOVERNANCE_CORE,
|
|
10517
|
-
{ slug: "glossary", name: "Glossary", description: "Agency and client terminology", fields: [{ key: "canonical", label: "Canonical", type: "string", required: true, searchable: true }, { key: "category", label: "Category", type: "string" }] },
|
|
10518
|
-
{ slug: "clients", name: "Clients", description: "Client profiles and relationship context", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "industry", label: "Industry", type: "string" }, { key: "contact", label: "Primary Contact", type: "string" }] },
|
|
10519
|
-
{ slug: "deliverables", name: "Deliverables", description: "Standard deliverable types and templates", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "type", label: "Type", type: "string" }, { key: "estimatedHours", label: "Estimated Hours", type: "string" }] },
|
|
10520
|
-
{ slug: "processes", name: "Processes", description: "Standard operating procedures and workflows", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "steps", label: "Steps", type: "string" }, { key: "owner", label: "Owner", type: "string" }] },
|
|
10521
|
-
{ slug: "decisions", name: "Decisions", description: "Strategic and operational decisions", fields: [{ key: "rationale", label: "Rationale", type: "string", required: true, searchable: true }, { key: "date", label: "Date", type: "string" }] },
|
|
10522
|
-
{ slug: "tensions", name: "Tensions", description: "Process bottlenecks and client friction", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "priority", label: "Priority", type: "select", options: ["low", "medium", "high", "critical"] }] }
|
|
10523
|
-
]
|
|
10524
|
-
},
|
|
10525
|
-
{
|
|
10526
|
-
id: "saas-api",
|
|
10527
|
-
name: "SaaS API",
|
|
10528
|
-
description: "For API-first SaaS products \u2014 endpoints, schemas, rate limits, changelog, and integration guides",
|
|
10529
|
-
collections: [
|
|
10530
|
-
...GOVERNANCE_CORE,
|
|
10531
|
-
{ slug: "glossary", name: "Glossary", description: "API and domain terminology", fields: [{ key: "canonical", label: "Canonical", type: "string", required: true, searchable: true }, { key: "category", label: "Category", type: "string" }] },
|
|
10532
|
-
{ slug: "api-endpoints", name: "API Endpoints", description: "API routes and contracts", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "method", label: "Method", type: "select", options: ["GET", "POST", "PUT", "PATCH", "DELETE"] }, { key: "path", label: "Path", type: "string" }, { key: "auth", label: "Auth", type: "select", options: ["none", "api-key", "bearer", "oauth"] }] },
|
|
10533
|
-
{ slug: "schemas", name: "Schemas", description: "Data models and API schemas", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "format", label: "Format", type: "select", options: ["json", "protobuf", "graphql", "openapi"] }, { key: "version", label: "Version", type: "string" }] },
|
|
10534
|
-
{ slug: "rate-limits", name: "Rate Limits", description: "Rate limiting policies and tiers", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "tier", label: "Tier", type: "string" }, { key: "limit", label: "Limit", type: "string" }] },
|
|
10535
|
-
{ slug: "changelog", name: "Changelog", description: "API version history and breaking changes", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "version", label: "Version", type: "string" }, { key: "date", label: "Date", type: "string" }, { key: "breaking", label: "Breaking", type: "select", options: ["yes", "no"] }] },
|
|
10536
|
-
{ slug: "decisions", name: "Decisions", description: "API design decisions and rationale", fields: [{ key: "rationale", label: "Rationale", type: "string", required: true, searchable: true }, { key: "date", label: "Date", type: "string" }] }
|
|
10537
|
-
]
|
|
10538
|
-
},
|
|
10539
|
-
{
|
|
10540
|
-
id: "general",
|
|
10541
|
-
name: "General",
|
|
10542
|
-
description: "A minimal starter set \u2014 glossary, decisions, tensions, and governance. Add more collections as you need them.",
|
|
10543
|
-
collections: [
|
|
10544
|
-
...GOVERNANCE_CORE,
|
|
10545
|
-
{ slug: "glossary", name: "Glossary", description: "Canonical terminology", fields: [{ key: "canonical", label: "Canonical", type: "string", required: true, searchable: true }, { key: "category", label: "Category", type: "string" }] },
|
|
10546
|
-
{ slug: "decisions", name: "Decisions", description: "Decisions with rationale", fields: [{ key: "rationale", label: "Rationale", type: "string", required: true, searchable: true }, { key: "date", label: "Date", type: "string" }, { key: "decidedBy", label: "Decided By", type: "string" }] },
|
|
10547
|
-
{ slug: "tensions", name: "Tensions", description: "Friction points and unmet needs", fields: [{ key: "description", label: "Description", type: "string", required: true, searchable: true }, { key: "priority", label: "Priority", type: "select", options: ["low", "medium", "high", "critical"] }] }
|
|
10548
|
-
]
|
|
10549
|
-
}
|
|
10550
|
-
];
|
|
10551
|
-
function getPreset(id) {
|
|
10552
|
-
return COLLECTION_PRESETS.find((p) => p.id === id);
|
|
10553
|
-
}
|
|
10554
|
-
function listPresets() {
|
|
10555
|
-
return COLLECTION_PRESETS.map((p) => ({
|
|
10556
|
-
id: p.id,
|
|
10557
|
-
name: p.name,
|
|
10558
|
-
description: p.description,
|
|
10559
|
-
collectionCount: p.collections.length
|
|
10560
|
-
}));
|
|
10512
|
+
// src/tools/skills.ts
|
|
10513
|
+
import { z as z16 } from "zod";
|
|
10514
|
+
var skillsSchema = z16.object({
|
|
10515
|
+
entryId: z16.string().min(1).describe(
|
|
10516
|
+
"Workspace-scoped skill entry id (e.g. 'SKILL-pb-setup'). The tool refuses non-skill entries (canonicalKey !== 'skill') with INVALID_KIND."
|
|
10517
|
+
)
|
|
10518
|
+
});
|
|
10519
|
+
async function loadSkillBody(entryId) {
|
|
10520
|
+
const workspaceId = await getWorkspaceId();
|
|
10521
|
+
return await kernelQuery("setup.getSkillBody", {
|
|
10522
|
+
workspaceId,
|
|
10523
|
+
entryId
|
|
10524
|
+
});
|
|
10525
|
+
}
|
|
10526
|
+
function registerSkillsTools(server) {
|
|
10527
|
+
server.registerTool(
|
|
10528
|
+
"skills",
|
|
10529
|
+
{
|
|
10530
|
+
title: "Load workspace skill body",
|
|
10531
|
+
description: "Loads the markdown body of a SKILL-* entry from the current workspace. Generic loader: works for ANY skill in your workspace \u2014 no allow-list, no pb-setup-specific behavior. Use this when an agent or MCP client needs the canonical body of a skill (e.g. SKILL-pb-setup, SKILL-shape, SKILL-review). Returns markdown plus name + retrievedAt for caching.",
|
|
10532
|
+
inputSchema: skillsSchema,
|
|
10533
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
10534
|
+
},
|
|
10535
|
+
thinWrapper(async ({ entryId }) => {
|
|
10536
|
+
const result = await loadSkillBody(entryId);
|
|
10537
|
+
const summary = `Loaded ${result.entryId} (${result.body.length} chars).`;
|
|
10538
|
+
return {
|
|
10539
|
+
content: textContent(result.body),
|
|
10540
|
+
structuredContent: success(summary, result)
|
|
10541
|
+
};
|
|
10542
|
+
})
|
|
10543
|
+
);
|
|
10561
10544
|
}
|
|
10562
10545
|
|
|
10563
10546
|
// src/tools/planned-work.ts
|
|
@@ -11015,75 +10998,6 @@ function formatWhatNeedsAttentionBrief(wna) {
|
|
|
11015
10998
|
return lines;
|
|
11016
10999
|
}
|
|
11017
11000
|
|
|
11018
|
-
// src/tools/start-interview.ts
|
|
11019
|
-
import { z as z16 } from "zod";
|
|
11020
|
-
var interviewExtractionSchema = z16.object({
|
|
11021
|
-
vision: z16.string().min(10).describe("What they're building \u2014 concise product purpose statement"),
|
|
11022
|
-
audience: z16.string().optional().describe("Who it's for \u2014 primary user or customer segment"),
|
|
11023
|
-
techStack: z16.array(z16.string()).optional().describe("Key technologies, frameworks, or platforms (e.g. ['SvelteKit', 'Convex', 'PostgreSQL'])"),
|
|
11024
|
-
keyTerms: z16.array(z16.string()).optional().describe("Domain-specific terms that belong in the glossary"),
|
|
11025
|
-
keyDecisions: z16.array(z16.string()).optional().describe("Recent significant decisions made (each as a concise statement)"),
|
|
11026
|
-
tensions: z16.array(z16.string()).optional().describe("Pain points or friction the product is solving")
|
|
11027
|
-
});
|
|
11028
|
-
function getInterviewInstructions(workspaceName) {
|
|
11029
|
-
return {
|
|
11030
|
-
systemPrompt: `You are activating the **${workspaceName}** Product Brain. Ask 1\u20132 focused questions, then extract structured knowledge and batch-capture it. Let the user talk naturally \u2014 you do the structuring. In Open governance mode, entries commit automatically. In consensus/role mode, they stay as drafts for review.`,
|
|
11031
|
-
question1: `**What are you building, and who is it for?** A sentence or two is plenty \u2014 I'll pull out the structure.`,
|
|
11032
|
-
question2: `**What's one word or phrase that would trip someone up if they didn't know your world?** (That becomes your first glossary term \u2014 the one that saves explanations later.)`,
|
|
11033
|
-
extractionGuidance: `After the user answers, extract:
|
|
11034
|
-
- vision: the core product purpose (required)
|
|
11035
|
-
- audience: who it's for (optional)
|
|
11036
|
-
- techStack: technologies mentioned (optional, array)
|
|
11037
|
-
- keyTerms: domain terms \u2014 Q2 answer goes here (optional, array)
|
|
11038
|
-
- keyDecisions: any decisions stated (optional, array)
|
|
11039
|
-
- tensions: any pain points stated (optional, array)
|
|
11040
|
-
|
|
11041
|
-
Map to batch-capture entries:
|
|
11042
|
-
- vision \u2192 strategy ("Product Vision")
|
|
11043
|
-
- audience \u2192 audiences (1 entry)
|
|
11044
|
-
- techStack \u2192 architecture ("Tech Stack") + top 3 terms \u2192 glossary
|
|
11045
|
-
- keyTerms \u2192 glossary (1 per term)
|
|
11046
|
-
- keyDecisions \u2192 decisions (1 per decision)
|
|
11047
|
-
- tensions \u2192 tensions (1 per tension)`,
|
|
11048
|
-
captureInstructions: `Call batch-capture with the extracted entries. Omit \`autoCommit\` to follow workspace governance automatically, or pass \`autoCommit: false\` if the user wants review-first. Keep descriptions concise (1\u20132 sentences each). Prefer 8 good entries over 15 mediocre ones \u2014 quality over volume. If batch-capture returns failedEntries, tell the user and retry individually.`,
|
|
11049
|
-
qualityNote: (
|
|
11050
|
-
// FEAT-149: Retrieval-First Proof Moment.
|
|
11051
|
-
// The aha is friction elimination ("I'll never have to explain this again"),
|
|
11052
|
-
// not recognition ("it remembered me"). Simulate a FUTURE session, not a recap.
|
|
11053
|
-
`After capturing, silently call graph action=suggest on the strategy or architecture entry to build connections.
|
|
11054
|
-
|
|
11055
|
-
**Then demonstrate the proof moment \u2014 this is the most important step:**
|
|
11056
|
-
Based on what the user told you, invent a specific, plausible development task they might actually do next (e.g. "add user authentication" for a SaaS app, "build the recommendation engine" for a marketplace, "set up the API layer" for a developer tool). Call \`context action=gather task="<your invented task>"\` to load their captured knowledge.
|
|
11057
|
-
|
|
11058
|
-
Present with the "tomorrow" framing \u2014 simulate a future session, not a recap:
|
|
11059
|
-
"Okay \u2014 let me show you what just changed. Tomorrow, if you open a new tab and ask me to help with **<task>**, I'd already know:
|
|
11060
|
-
- [vision applied to that task \u2014 one sentence]
|
|
11061
|
-
- [ICP applied \u2014 who this feature is for in their context]
|
|
11062
|
-
- [any key term that matters for this task]
|
|
11063
|
-
|
|
11064
|
-
That's context you won't have to explain again."
|
|
11065
|
-
|
|
11066
|
-
End with: "**Try it right now** \u2014 ask me something you'd normally have to re-explain first. I'll answer like I've known your product for months."`
|
|
11067
|
-
),
|
|
11068
|
-
scanOffer: `After the proof moment, offer the codebase scan:
|
|
11069
|
-
"Want me to learn more? I can read your project files \u2014 README, package.json, source structure \u2014 and pick up technical decisions, conventions, and architecture I missed. Takes about two minutes."`
|
|
11070
|
-
};
|
|
11071
|
-
}
|
|
11072
|
-
function buildInterviewResponse(workspaceName) {
|
|
11073
|
-
const instructions = getInterviewInstructions(workspaceName);
|
|
11074
|
-
return [
|
|
11075
|
-
"## Let's get to know your product",
|
|
11076
|
-
"",
|
|
11077
|
-
instructions.systemPrompt,
|
|
11078
|
-
"",
|
|
11079
|
-
"I'll ask you one or two questions. Your answers become the foundation of your Brain.",
|
|
11080
|
-
"",
|
|
11081
|
-
instructions.question1,
|
|
11082
|
-
"",
|
|
11083
|
-
"_Take your time \u2014 I'll pull out the structure from whatever you say._"
|
|
11084
|
-
].join("\n");
|
|
11085
|
-
}
|
|
11086
|
-
|
|
11087
11001
|
// src/lib/gapToPrompt.ts
|
|
11088
11002
|
function cleanLabel(label) {
|
|
11089
11003
|
return label.replace(/ has entries$/i, "").replace(/ coverage$/i, "").replace(/^Strategy — /i, "");
|
|
@@ -11111,28 +11025,19 @@ function formatTopGapPrompt(gap, remaining, ctx) {
|
|
|
11111
11025
|
];
|
|
11112
11026
|
return lines.join("\n");
|
|
11113
11027
|
}
|
|
11114
|
-
function formatGapList(gaps, limit = 3) {
|
|
11115
|
-
const topGaps = gaps.slice(0, limit);
|
|
11116
|
-
if (topGaps.length === 0) return [];
|
|
11117
|
-
const lines = [
|
|
11118
|
-
"Here's where your Brain would benefit most:",
|
|
11119
|
-
""
|
|
11120
|
-
];
|
|
11121
|
-
for (let i = 0; i < topGaps.length; i++) {
|
|
11122
|
-
const gap = topGaps[i];
|
|
11123
|
-
const prompt = gap.capabilityGuidance ?? gap.guidance;
|
|
11124
|
-
const label = cleanLabel(gap.label);
|
|
11125
|
-
lines.push(`${i + 1}. **${label}** \u2014 ${prompt}`);
|
|
11126
|
-
}
|
|
11127
|
-
return lines;
|
|
11128
|
-
}
|
|
11129
11028
|
function formatGapOneLiner(gap) {
|
|
11130
11029
|
if (!gap) return null;
|
|
11131
11030
|
const prompt = gap.capabilityGuidance ?? gap.guidance;
|
|
11132
11031
|
return `Next gap: ${prompt}`;
|
|
11133
11032
|
}
|
|
11134
11033
|
|
|
11135
|
-
// src/tools/
|
|
11034
|
+
// src/tools/start_pb.ts
|
|
11035
|
+
var startPbSchema = z17.object({
|
|
11036
|
+
task: z17.string().optional().describe(
|
|
11037
|
+
"What you're about to work on (e.g. 'implementing auth middleware'). Grounded/connected workspaces: filters governance to show relevant principles, standards, and business rules. Blank/seeded workspaces: ignored (setup flow takes over)."
|
|
11038
|
+
)
|
|
11039
|
+
});
|
|
11040
|
+
var PB_SETUP_SKILL_ENTRY_ID = "SKILL-pb-setup";
|
|
11136
11041
|
async function tryMarkOriented(agentSessionId, coherenceSnapshot) {
|
|
11137
11042
|
if (!agentSessionId) return { oriented: false, orientationStatus: "no_session" };
|
|
11138
11043
|
try {
|
|
@@ -11155,312 +11060,16 @@ async function tryMarkOriented(agentSessionId, coherenceSnapshot) {
|
|
|
11155
11060
|
}
|
|
11156
11061
|
}
|
|
11157
11062
|
}
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
task: z17.string().optional().describe(
|
|
11163
|
-
"What you're about to work on (e.g. 'implementing auth middleware', 'refactoring the API layer'). Filters governance to show only relevant principles, standards, and business rules."
|
|
11164
|
-
)
|
|
11165
|
-
});
|
|
11166
|
-
function registerStartTools(server) {
|
|
11167
|
-
server.registerTool(
|
|
11168
|
-
"start",
|
|
11169
|
-
{
|
|
11170
|
-
title: "Start Product Brain",
|
|
11171
|
-
description: "The zero-friction entry point. Say 'start PB' to begin.\n\n- **Fresh workspace (blank)**: asks what you're building, captures the essentials, and can scan your codebase if useful.\n- **Early workspace (seeded)**: picks up where you left off with the top gaps to fill.\n- **Active workspace (grounded/connected)**: standup briefing with recent activity and open items.\n\nUse this as your first call. Replaces the need to call orient or health separately.",
|
|
11172
|
-
inputSchema: startSchema,
|
|
11173
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
11174
|
-
},
|
|
11175
|
-
thinWrapper(async ({ preset, task }) => {
|
|
11176
|
-
const errors = [];
|
|
11177
|
-
const agentSessionId = getAgentSessionId();
|
|
11178
|
-
let wsCtx = null;
|
|
11179
|
-
try {
|
|
11180
|
-
wsCtx = await getWorkspaceContext();
|
|
11181
|
-
} catch (e) {
|
|
11182
|
-
errors.push(`Workspace: ${e instanceof Error ? e.message : String(e)}`);
|
|
11183
|
-
}
|
|
11184
|
-
if (!wsCtx) {
|
|
11185
|
-
const text = "# Could not connect to Product Brain\n\n" + (errors.length > 0 ? errors.map((e) => `- ${e}`).join("\n") : "Check your API key and CONVEX_SITE_URL.");
|
|
11186
|
-
return {
|
|
11187
|
-
content: [{ type: "text", text }],
|
|
11188
|
-
structuredContent: failure(
|
|
11189
|
-
"BACKEND_UNAVAILABLE",
|
|
11190
|
-
"Could not connect to Product Brain.",
|
|
11191
|
-
"Check your API key and CONVEX_SITE_URL."
|
|
11192
|
-
)
|
|
11193
|
-
};
|
|
11194
|
-
}
|
|
11195
|
-
let stage = null;
|
|
11196
|
-
let readiness = null;
|
|
11197
|
-
try {
|
|
11198
|
-
readiness = await kernelQuery("chain.workspaceReadiness");
|
|
11199
|
-
stage = readiness?.stage ?? "blank";
|
|
11200
|
-
} catch {
|
|
11201
|
-
errors.push("Readiness check unavailable \u2014 showing workspace summary.");
|
|
11202
|
-
}
|
|
11203
|
-
if (stage === "blank" && preset) {
|
|
11204
|
-
const result = await seedPreset(wsCtx, preset, agentSessionId);
|
|
11205
|
-
return {
|
|
11206
|
-
content: [{ type: "text", text: result.text }],
|
|
11207
|
-
structuredContent: result.structuredContent
|
|
11208
|
-
};
|
|
11209
|
-
}
|
|
11210
|
-
if (stage === "blank") {
|
|
11211
|
-
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
11212
|
-
const blankResult = buildBlankResponse(wsCtx, {
|
|
11213
|
-
oriented,
|
|
11214
|
-
orientationStatus,
|
|
11215
|
-
...agentSessionId ? { sessionId: agentSessionId } : {}
|
|
11216
|
-
});
|
|
11217
|
-
return {
|
|
11218
|
-
content: [{ type: "text", text: blankResult.text }],
|
|
11219
|
-
structuredContent: blankResult.structuredContent
|
|
11220
|
-
};
|
|
11221
|
-
}
|
|
11222
|
-
if (stage === "seeded") {
|
|
11223
|
-
const result = await buildSeededResponse(wsCtx, readiness, agentSessionId);
|
|
11224
|
-
void kernelMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
11225
|
-
});
|
|
11226
|
-
return {
|
|
11227
|
-
content: [{ type: "text", text: result.text }],
|
|
11228
|
-
structuredContent: result.structuredContent
|
|
11229
|
-
};
|
|
11230
|
-
}
|
|
11231
|
-
if (stage === "grounded" || stage === "connected") {
|
|
11232
|
-
void kernelMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
11233
|
-
});
|
|
11234
|
-
}
|
|
11235
|
-
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors, task);
|
|
11236
|
-
return {
|
|
11237
|
-
content: [{ type: "text", text: orientResult.text }],
|
|
11238
|
-
structuredContent: orientResult.structuredContent
|
|
11239
|
-
};
|
|
11240
|
-
})
|
|
11241
|
-
);
|
|
11242
|
-
}
|
|
11243
|
-
function buildBlankResponse(wsCtx, sessionCtx) {
|
|
11244
|
-
const instructions = getInterviewInstructions(wsCtx.workspaceName);
|
|
11245
|
-
const presetList = listPresets();
|
|
11246
|
-
const presets = presetList.map((p) => p.id).join(", ");
|
|
11247
|
-
const text = [
|
|
11248
|
-
// ── User-facing welcome — present this to the user ─────────────────
|
|
11249
|
-
`# Welcome to ${wsCtx.workspaceName}`,
|
|
11250
|
-
"",
|
|
11251
|
-
"Your workspace is fresh. Let's bring it to life.",
|
|
11252
|
-
"",
|
|
11253
|
-
"Tell me what you're building \u2014 paste a doc, describe it in a sentence, or say **scan my codebase**.",
|
|
11254
|
-
"",
|
|
11255
|
-
"_Powered by [Product Brain](https://productbrain.io) \u2014 your AI's product context layer._",
|
|
11256
|
-
"",
|
|
11257
|
-
"---",
|
|
11258
|
-
"",
|
|
11259
|
-
// ── Agent instructions — follow these exactly, do not present them ──
|
|
11260
|
-
"## Agent Instructions",
|
|
11261
|
-
"",
|
|
11262
|
-
"### Step 1 \u2014 Open with their name (FLO-1)",
|
|
11263
|
-
`Ask: _"What should I call you?"_ before asking about the product. Use their name throughout.`,
|
|
11264
|
-
"",
|
|
11265
|
-
"### Step 2 \u2014 Detect path from their first response (no menu, no numbered options)",
|
|
11266
|
-
"",
|
|
11267
|
-
"Read signals from the user's first substantive message and branch silently:",
|
|
11268
|
-
"",
|
|
11269
|
-
"**document** \u2014 paste >200 words, markdown headers, or says 'here's my doc/brief/spec'",
|
|
11270
|
-
"\u2192 Extract vision, audience, keyTerms, keyDecisions, tensions from the document.",
|
|
11271
|
-
"\u2192 Present a numbered confirmation list before capturing. Capture only confirmed entries.",
|
|
11272
|
-
"\u2192 Fallback: if sparse or unclear, switch to interview path.",
|
|
11273
|
-
"",
|
|
11274
|
-
"**codebase** \u2014 mentions 'scan', 'codebase', README, or provides a file path",
|
|
11275
|
-
`\u2192 First ask: _"Tell me one thing about this product that's not obvious from the code \u2014 what's it actually for?"_`,
|
|
11276
|
-
"\u2192 Then read: README.md, package.json / pyproject.toml / Cargo.toml, top-level source dirs.",
|
|
11277
|
-
"\u2192 Infer 5\u20138 entries. Present numbered list for confirmation. Capture only confirmed entries.",
|
|
11278
|
-
"\u2192 Fallback: if no README or empty project, immediately switch to interview path.",
|
|
11279
|
-
"",
|
|
11280
|
-
`**preset** \u2014 mentions a domain keyword (SaaS, agency, content business, API)`,
|
|
11281
|
-
`\u2192 Confirm which preset fits. Available: ${presets}.`,
|
|
11282
|
-
"\u2192 Call `start` with `preset: '<chosen-preset>'`.",
|
|
11283
|
-
"\u2192 Fallback: if no preset matches or seeding fails, switch to interview path.",
|
|
11284
|
-
"",
|
|
11285
|
-
"**interview** (default \u2014 1\u20135 sentence description or anything else)",
|
|
11286
|
-
`\u2192 ${instructions.systemPrompt}`,
|
|
11287
|
-
`\u2192 Q1: ${instructions.question1}`,
|
|
11288
|
-
`\u2192 Q2 (optional): _"What's one word or phrase that would trip someone up if they didn't know your context?"_ (This becomes their first glossary term.)`,
|
|
11289
|
-
instructions.extractionGuidance,
|
|
11290
|
-
`\u2192 ${instructions.captureInstructions}`,
|
|
11291
|
-
"\u2192 Omit `autoCommit` \u2014 workspace governance applies automatically.",
|
|
11292
|
-
"",
|
|
11293
|
-
"### Step 3 \u2014 Proof moment (most important \u2014 run after every path)",
|
|
11294
|
-
"",
|
|
11295
|
-
instructions.qualityNote,
|
|
11296
|
-
"",
|
|
11297
|
-
"### Step 4 \u2014 Deepen (offer after proof moment)",
|
|
11298
|
-
"",
|
|
11299
|
-
instructions.scanOffer,
|
|
11300
|
-
"If they say yes, scan README.md, package.json/pyproject.toml/Cargo.toml, and top-level source dirs.",
|
|
11301
|
-
"Infer entries with the product context you now have. Present as a numbered list for confirmation.",
|
|
11302
|
-
"Capture only confirmed entries."
|
|
11303
|
-
].join("\n");
|
|
11304
|
-
return {
|
|
11305
|
-
text,
|
|
11306
|
-
structuredContent: success(
|
|
11307
|
-
"Workspace is blank. Ready for activation \u2014 interview, document, codebase scan, or preset.",
|
|
11308
|
-
{
|
|
11309
|
-
stage: "blank",
|
|
11310
|
-
interviewSchema: {
|
|
11311
|
-
fields: ["vision", "audience", "techStack", "keyTerms", "keyDecisions", "tensions"],
|
|
11312
|
-
mapping: {
|
|
11313
|
-
vision: "strategy",
|
|
11314
|
-
audience: "audiences",
|
|
11315
|
-
techStack: "architecture + top 3 terms \u2192 glossary",
|
|
11316
|
-
keyTerms: "glossary",
|
|
11317
|
-
keyDecisions: "decisions",
|
|
11318
|
-
tensions: "tensions"
|
|
11319
|
-
}
|
|
11320
|
-
},
|
|
11321
|
-
availablePresets: presetList.map((p) => ({ id: p.id, name: p.name })),
|
|
11322
|
-
...sessionCtx
|
|
11323
|
-
},
|
|
11324
|
-
[
|
|
11325
|
-
{ tool: "capture", description: "Capture product knowledge", parameters: {} },
|
|
11326
|
-
{ tool: "start", description: "Seed with a preset", parameters: { preset: "software-product" } }
|
|
11327
|
-
]
|
|
11328
|
-
)
|
|
11329
|
-
};
|
|
11330
|
-
}
|
|
11331
|
-
async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
|
|
11332
|
-
const stage = readiness?.stage ?? "seeded";
|
|
11333
|
-
const score = readiness?.score ?? null;
|
|
11334
|
-
const gaps = readiness?.gaps ?? [];
|
|
11335
|
-
const lines = [
|
|
11336
|
-
`# ${wsCtx.workspaceName}`,
|
|
11337
|
-
"_Picking up where you left off._",
|
|
11338
|
-
""
|
|
11339
|
-
];
|
|
11340
|
-
if (gaps.length > 0) {
|
|
11341
|
-
const gapLines = formatGapList(gaps, 3);
|
|
11342
|
-
lines.push(...gapLines);
|
|
11343
|
-
lines.push("");
|
|
11344
|
-
lines.push("Pick any to start \u2014 or begin with **#1** and I'll guide you through it.");
|
|
11345
|
-
} else {
|
|
11346
|
-
lines.push("No gaps detected \u2014 your workspace is filling up nicely. What would you like to work on?");
|
|
11347
|
-
}
|
|
11348
|
-
const knowledgeGaps = getTopGaps(3);
|
|
11349
|
-
if (knowledgeGaps.length > 0) {
|
|
11350
|
-
lines.push("");
|
|
11351
|
-
lines.push("### Learning Opportunities");
|
|
11352
|
-
lines.push("_Topics agents recently looked for but aren't on the Chain yet:_");
|
|
11353
|
-
lines.push("");
|
|
11354
|
-
for (const gap of knowledgeGaps) {
|
|
11355
|
-
const freq = gap.count > 1 ? ` _(${gap.count}x)_` : "";
|
|
11356
|
-
lines.push(`- **${gap.query}**${freq}`);
|
|
11357
|
-
}
|
|
11358
|
-
lines.push("");
|
|
11359
|
-
lines.push("_Capturing these will make future sessions more effective._");
|
|
11360
|
-
}
|
|
11361
|
-
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
11362
|
-
const next = gaps.length > 0 ? [{ tool: "capture", description: `Fill gap: ${gaps[0].label}`, parameters: {} }] : [];
|
|
11363
|
-
return {
|
|
11364
|
-
text: lines.join("\n"),
|
|
11365
|
-
structuredContent: success(
|
|
11366
|
-
gaps.length > 0 ? `Workspace seeded. ${gaps.length} gap${gaps.length === 1 ? "" : "s"} to fill. Top: ${gaps[0].label}.` : `Workspace seeded. No gaps detected.`,
|
|
11367
|
-
{
|
|
11368
|
-
stage,
|
|
11369
|
-
readinessScore: score ?? null,
|
|
11370
|
-
totalGaps: gaps.length,
|
|
11371
|
-
topGapLabels: gaps.slice(0, 3).map((g) => g.label),
|
|
11372
|
-
oriented,
|
|
11373
|
-
orientationStatus,
|
|
11374
|
-
...agentSessionId ? { sessionId: agentSessionId } : {}
|
|
11375
|
-
},
|
|
11376
|
-
next.length > 0 ? next : void 0
|
|
11377
|
-
)
|
|
11378
|
-
};
|
|
11379
|
-
}
|
|
11380
|
-
async function seedPreset(wsCtx, presetId, agentSessionId) {
|
|
11381
|
-
const preset = getPreset(presetId);
|
|
11382
|
-
if (!preset) {
|
|
11383
|
-
return {
|
|
11384
|
-
text: `Preset "${presetId}" not found.
|
|
11385
|
-
|
|
11386
|
-
Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`,
|
|
11387
|
-
structuredContent: failure(
|
|
11388
|
-
"NOT_FOUND",
|
|
11389
|
-
`Preset '${presetId}' not found.`,
|
|
11390
|
-
`Available presets: ${listPresets().map((p) => p.id).join(", ")}.`,
|
|
11391
|
-
[{ tool: "start", description: "Start with a preset", parameters: { preset: "software-product" } }]
|
|
11392
|
-
)
|
|
11393
|
-
};
|
|
11394
|
-
}
|
|
11395
|
-
const seeded = [];
|
|
11396
|
-
const skipped = [];
|
|
11397
|
-
for (const col of preset.collections) {
|
|
11398
|
-
try {
|
|
11399
|
-
const purpose = col.purpose ?? col.description;
|
|
11400
|
-
await kernelCall("chain.createCollection", {
|
|
11401
|
-
slug: col.slug,
|
|
11402
|
-
name: col.name,
|
|
11403
|
-
description: col.description,
|
|
11404
|
-
purpose,
|
|
11405
|
-
icon: col.icon,
|
|
11406
|
-
idPrefix: col.idPrefix,
|
|
11407
|
-
fields: col.fields,
|
|
11408
|
-
createdBy: "preset:" + presetId
|
|
11409
|
-
});
|
|
11410
|
-
seeded.push(col.name);
|
|
11411
|
-
} catch {
|
|
11412
|
-
skipped.push(`${col.name} (already exists)`);
|
|
11413
|
-
}
|
|
11414
|
-
}
|
|
11415
|
-
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
11416
|
-
const lines = [
|
|
11417
|
-
`# ${wsCtx.workspaceName} is ready`,
|
|
11418
|
-
"",
|
|
11419
|
-
`Seeded **${preset.name}** preset with ${seeded.length} collection${seeded.length === 1 ? "" : "s"}:`,
|
|
11420
|
-
""
|
|
11421
|
-
];
|
|
11422
|
-
for (const name of seeded) {
|
|
11423
|
-
lines.push(`- ${name}`);
|
|
11424
|
-
}
|
|
11425
|
-
if (skipped.length > 0) {
|
|
11426
|
-
lines.push("", "Skipped (already exist):");
|
|
11427
|
-
for (const name of skipped) {
|
|
11428
|
-
lines.push(`- ${name}`);
|
|
11429
|
-
}
|
|
11430
|
-
}
|
|
11431
|
-
lines.push(
|
|
11432
|
-
"",
|
|
11433
|
-
buildInterviewResponse(wsCtx.workspaceName),
|
|
11434
|
-
"",
|
|
11435
|
-
"_You can also customize your collections anytime \u2014 just ask._"
|
|
11436
|
-
);
|
|
11437
|
-
return {
|
|
11438
|
-
text: lines.join("\n"),
|
|
11439
|
-
structuredContent: success(
|
|
11440
|
-
`Seeded '${preset.name}' preset with ${seeded.length} collection${seeded.length === 1 ? "" : "s"}.`,
|
|
11441
|
-
{
|
|
11442
|
-
stage: "blank",
|
|
11443
|
-
preset: presetId,
|
|
11444
|
-
seededCollections: seeded,
|
|
11445
|
-
skippedCollections: skipped,
|
|
11446
|
-
oriented,
|
|
11447
|
-
orientationStatus,
|
|
11448
|
-
...agentSessionId ? { sessionId: agentSessionId } : {}
|
|
11449
|
-
},
|
|
11450
|
-
[{ tool: "capture", description: "Capture your first entry", parameters: {} }]
|
|
11451
|
-
)
|
|
11452
|
-
};
|
|
11063
|
+
function isActiveNowBet(e) {
|
|
11064
|
+
const status = e.status ?? e.data?.status;
|
|
11065
|
+
const horizon = e.data?.horizon;
|
|
11066
|
+
return status === "active" && horizon === "now";
|
|
11453
11067
|
}
|
|
11454
11068
|
function computeWorkspaceAge(createdAt) {
|
|
11455
11069
|
if (!createdAt) return { ageDays: 0, isNeglected: false };
|
|
11456
11070
|
const ageDays = Math.floor((Date.now() - createdAt) / (1e3 * 60 * 60 * 24));
|
|
11457
11071
|
return { ageDays, isNeglected: ageDays >= 30 };
|
|
11458
11072
|
}
|
|
11459
|
-
function isActiveNowBet(e) {
|
|
11460
|
-
const status = e.status ?? e.data?.status;
|
|
11461
|
-
const horizon = e.data?.horizon;
|
|
11462
|
-
return status === "active" && horizon === "now";
|
|
11463
|
-
}
|
|
11464
11073
|
function pickNextTensionPrompt(openTensions) {
|
|
11465
11074
|
if (openTensions.length === 0) return null;
|
|
11466
11075
|
const t = openTensions[0];
|
|
@@ -11469,9 +11078,32 @@ function pickNextTensionPrompt(openTensions) {
|
|
|
11469
11078
|
cta: "Want to discuss this tension or capture a decision about it?"
|
|
11470
11079
|
};
|
|
11471
11080
|
}
|
|
11081
|
+
async function buildSetupResponse(agentSessionId) {
|
|
11082
|
+
await kernelMutation(
|
|
11083
|
+
"setup.stampMcpOnlySurface",
|
|
11084
|
+
{}
|
|
11085
|
+
);
|
|
11086
|
+
const skill = await loadSkillBody(PB_SETUP_SKILL_ENTRY_ID);
|
|
11087
|
+
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
11088
|
+
return {
|
|
11089
|
+
text: skill.body,
|
|
11090
|
+
structuredContent: success(
|
|
11091
|
+
`pb-setup ready. Delivering SKILL-pb-setup body (${skill.body.length} chars).`,
|
|
11092
|
+
{
|
|
11093
|
+
bootstrap: "pb-setup",
|
|
11094
|
+
skillEntryId: skill.entryId,
|
|
11095
|
+
skillBody: skill.body,
|
|
11096
|
+
stage: "setup",
|
|
11097
|
+
oriented,
|
|
11098
|
+
orientationStatus,
|
|
11099
|
+
...agentSessionId ? { sessionId: agentSessionId } : {},
|
|
11100
|
+
instructions: "Read skillBody and follow it; use commit-entry/entries/orient MCP tools per PAT-227."
|
|
11101
|
+
}
|
|
11102
|
+
)
|
|
11103
|
+
};
|
|
11104
|
+
}
|
|
11472
11105
|
async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
11473
|
-
const
|
|
11474
|
-
const { ageDays, isNeglected } = computeWorkspaceAge(wsFullCtx.createdAt);
|
|
11106
|
+
const { ageDays, isNeglected } = computeWorkspaceAge(wsCtx.createdAt ?? null);
|
|
11475
11107
|
let priorSessions = [];
|
|
11476
11108
|
let recoveryBlock = null;
|
|
11477
11109
|
try {
|
|
@@ -11496,7 +11128,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
11496
11128
|
const isLowReadiness = readiness !== null && readiness.score < 50;
|
|
11497
11129
|
const isHighReadiness = readiness !== null && readiness.score >= 50;
|
|
11498
11130
|
const stage = readiness?.stage ?? null;
|
|
11499
|
-
const captureBehaviorNote =
|
|
11131
|
+
const captureBehaviorNote = wsCtx.governanceMode === "open" ? "_In Open mode, user-authored captures commit immediately unless you ask me to keep them as drafts._" : "_Everything I capture stays pending until you confirm._";
|
|
11500
11132
|
const coherence = buildCoherenceSection();
|
|
11501
11133
|
let allCollections = [];
|
|
11502
11134
|
try {
|
|
@@ -11695,6 +11327,61 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
11695
11327
|
)
|
|
11696
11328
|
};
|
|
11697
11329
|
}
|
|
11330
|
+
function registerStartPbTools(server) {
|
|
11331
|
+
server.registerTool(
|
|
11332
|
+
"start_pb",
|
|
11333
|
+
{
|
|
11334
|
+
title: "Start Product Brain",
|
|
11335
|
+
description: "Universal session opener \u2014 say 'Start PB' to begin every session (PAT-227).\n\nStage-aware delivery:\n- **Fresh workspace (blank/seeded)**: returns the canonical SKILL-pb-setup body verbatim. The skill drives the install-to-activation flow.\n- **Active workspace (grounded/connected)**: standup briefing with active bets, governance, and recent activity.\n\nAlways call this first. It starts a session, stamps the surface, and loads the right guidance for your stage. Replaces both the former `start` tool and the former chat-only `start_pb` bootstrap.",
|
|
11336
|
+
inputSchema: startPbSchema,
|
|
11337
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
11338
|
+
},
|
|
11339
|
+
thinWrapper(async ({ task }) => {
|
|
11340
|
+
const errors = [];
|
|
11341
|
+
const agentSessionId = getAgentSessionId();
|
|
11342
|
+
let wsCtx = null;
|
|
11343
|
+
try {
|
|
11344
|
+
wsCtx = await getWorkspaceContext();
|
|
11345
|
+
} catch (e) {
|
|
11346
|
+
errors.push(`Workspace: ${e instanceof Error ? e.message : String(e)}`);
|
|
11347
|
+
}
|
|
11348
|
+
if (!wsCtx) {
|
|
11349
|
+
const text = "# Could not connect to Product Brain\n\n" + (errors.length > 0 ? errors.map((e) => `- ${e}`).join("\n") : "Check your API key and CONVEX_SITE_URL.");
|
|
11350
|
+
return {
|
|
11351
|
+
content: [{ type: "text", text }],
|
|
11352
|
+
structuredContent: failure(
|
|
11353
|
+
"BACKEND_UNAVAILABLE",
|
|
11354
|
+
"Could not connect to Product Brain.",
|
|
11355
|
+
"Check your API key and CONVEX_SITE_URL."
|
|
11356
|
+
)
|
|
11357
|
+
};
|
|
11358
|
+
}
|
|
11359
|
+
let stage = null;
|
|
11360
|
+
try {
|
|
11361
|
+
const readiness = await kernelQuery("chain.workspaceReadiness");
|
|
11362
|
+
stage = readiness?.stage ?? "blank";
|
|
11363
|
+
} catch {
|
|
11364
|
+
errors.push("Readiness check unavailable \u2014 showing workspace summary.");
|
|
11365
|
+
}
|
|
11366
|
+
if (stage === "blank" || stage === "seeded") {
|
|
11367
|
+
const result = await buildSetupResponse(agentSessionId);
|
|
11368
|
+
return {
|
|
11369
|
+
content: textContent(result.text),
|
|
11370
|
+
structuredContent: result.structuredContent
|
|
11371
|
+
};
|
|
11372
|
+
}
|
|
11373
|
+
if (stage === "grounded" || stage === "connected") {
|
|
11374
|
+
void kernelMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
11375
|
+
});
|
|
11376
|
+
}
|
|
11377
|
+
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors, task);
|
|
11378
|
+
return {
|
|
11379
|
+
content: [{ type: "text", text: orientResult.text }],
|
|
11380
|
+
structuredContent: orientResult.structuredContent
|
|
11381
|
+
};
|
|
11382
|
+
})
|
|
11383
|
+
);
|
|
11384
|
+
}
|
|
11698
11385
|
|
|
11699
11386
|
// src/tools/usage.ts
|
|
11700
11387
|
import { z as z18 } from "zod";
|
|
@@ -13062,139 +12749,141 @@ ${nodeDetail}${flowLines}`
|
|
|
13062
12749
|
return unknownAction(action, ["show", "explore", "flow"]);
|
|
13063
12750
|
})
|
|
13064
12751
|
);
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
12752
|
+
if (process.env.PB_INTERNAL === "true") {
|
|
12753
|
+
const archAdminTool = server.registerTool(
|
|
12754
|
+
"architecture-admin",
|
|
12755
|
+
{
|
|
12756
|
+
title: "Architecture Admin",
|
|
12757
|
+
description: "Architecture maintenance \u2014 seed the default architecture data or run a dependency health check.\n\nActions:\n- `seed`: Populate the architecture collection with the default Product OS map. Safe to re-run.\n- `check`: Scan the codebase for dependency direction violations against layer rules.",
|
|
12758
|
+
inputSchema: architectureAdminSchema,
|
|
12759
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
12760
|
+
},
|
|
12761
|
+
thinWrapper(async ({ action }) => {
|
|
12762
|
+
if (action === "seed") {
|
|
12763
|
+
await ensureCollection();
|
|
12764
|
+
const existing = await listArchEntries();
|
|
12765
|
+
const existingIds = new Set(existing.map((e) => e.entryId));
|
|
12766
|
+
let created = 0;
|
|
12767
|
+
let updated = 0;
|
|
12768
|
+
let unchanged = 0;
|
|
12769
|
+
const allWarnings = [];
|
|
12770
|
+
const allSeeds = [
|
|
12771
|
+
{ ...SEED_TEMPLATE, order: 0, status: "active" },
|
|
12772
|
+
...SEED_LAYERS.map((l) => ({ ...l, status: "active" })),
|
|
12773
|
+
...SEED_NODES.map((n) => ({ ...n, status: "active" })),
|
|
12774
|
+
...SEED_FLOWS.map((f) => ({ ...f, status: "active" }))
|
|
12775
|
+
];
|
|
12776
|
+
for (const seed of allSeeds) {
|
|
12777
|
+
if (existingIds.has(seed.entryId)) {
|
|
12778
|
+
const existingEntry = existing.find((e) => e.entryId === seed.entryId);
|
|
12779
|
+
const existingData = existingEntry?.data ?? {};
|
|
12780
|
+
const seedData = seed.data;
|
|
12781
|
+
const hasChanges = Object.keys(seedData).some(
|
|
12782
|
+
(k) => seedData[k] !== void 0 && existingData[k] !== seedData[k]
|
|
12783
|
+
);
|
|
12784
|
+
if (hasChanges) {
|
|
12785
|
+
const mergedData = { ...existingData, ...seedData };
|
|
12786
|
+
const updateResult = await kernelMutation("chain.updateEntry", { entryId: seed.entryId, data: mergedData });
|
|
12787
|
+
if (updateResult.normalizationWarnings.length > 0 || updateResult.validationWarnings.length > 0) {
|
|
12788
|
+
allWarnings.push({
|
|
12789
|
+
entryId: seed.entryId,
|
|
12790
|
+
normalizationWarnings: updateResult.normalizationWarnings,
|
|
12791
|
+
validationWarnings: updateResult.validationWarnings
|
|
12792
|
+
});
|
|
12793
|
+
}
|
|
12794
|
+
updated++;
|
|
12795
|
+
} else {
|
|
12796
|
+
unchanged++;
|
|
13105
12797
|
}
|
|
13106
|
-
|
|
13107
|
-
} else {
|
|
13108
|
-
unchanged++;
|
|
12798
|
+
continue;
|
|
13109
12799
|
}
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
const createResult = await kernelMutation("chain.createEntry", {
|
|
13113
|
-
collectionSlug: COLLECTION_SLUG,
|
|
13114
|
-
entryId: seed.entryId,
|
|
13115
|
-
name: seed.name,
|
|
13116
|
-
status: seed.status,
|
|
13117
|
-
data: seed.data,
|
|
13118
|
-
order: seed.order ?? 0
|
|
13119
|
-
});
|
|
13120
|
-
if (createResult.normalizationWarnings.length > 0 || createResult.validationWarnings.length > 0) {
|
|
13121
|
-
allWarnings.push({
|
|
12800
|
+
const createResult = await kernelMutation("chain.createEntry", {
|
|
12801
|
+
collectionSlug: COLLECTION_SLUG,
|
|
13122
12802
|
entryId: seed.entryId,
|
|
13123
|
-
|
|
13124
|
-
|
|
12803
|
+
name: seed.name,
|
|
12804
|
+
status: seed.status,
|
|
12805
|
+
data: seed.data,
|
|
12806
|
+
order: seed.order ?? 0
|
|
13125
12807
|
});
|
|
12808
|
+
if (createResult.normalizationWarnings.length > 0 || createResult.validationWarnings.length > 0) {
|
|
12809
|
+
allWarnings.push({
|
|
12810
|
+
entryId: seed.entryId,
|
|
12811
|
+
normalizationWarnings: createResult.normalizationWarnings,
|
|
12812
|
+
validationWarnings: createResult.validationWarnings
|
|
12813
|
+
});
|
|
12814
|
+
}
|
|
12815
|
+
created++;
|
|
13126
12816
|
}
|
|
13127
|
-
|
|
13128
|
-
}
|
|
13129
|
-
let responseText = `# Architecture Seeded
|
|
12817
|
+
let responseText = `# Architecture Seeded
|
|
13130
12818
|
|
|
13131
12819
|
**Created:** ${created} entries
|
|
13132
12820
|
**Updated:** ${updated} (merged new fields)
|
|
13133
12821
|
**Unchanged:** ${unchanged}
|
|
13134
12822
|
|
|
13135
12823
|
Use \`architecture action=show\` to view the map.`;
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
12824
|
+
if (allWarnings.length > 0) {
|
|
12825
|
+
responseText += "\n\n---\n\n\u26A0\uFE0F **Seed warnings:**";
|
|
12826
|
+
for (const w of allWarnings) {
|
|
12827
|
+
for (const nw of w.normalizationWarnings) {
|
|
12828
|
+
responseText += `
|
|
13141
12829
|
- **${w.entryId}** (normalization): ${nw}`;
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
12830
|
+
}
|
|
12831
|
+
for (const vw of w.validationWarnings) {
|
|
12832
|
+
responseText += `
|
|
13145
12833
|
- **${w.entryId}** (validation): ${vw}`;
|
|
12834
|
+
}
|
|
13146
12835
|
}
|
|
13147
12836
|
}
|
|
13148
|
-
}
|
|
13149
|
-
return {
|
|
13150
|
-
content: [{
|
|
13151
|
-
type: "text",
|
|
13152
|
-
text: responseText
|
|
13153
|
-
}],
|
|
13154
|
-
structuredContent: success(
|
|
13155
|
-
`Architecture seeded: ${created} created, ${updated} updated, ${unchanged} unchanged.`,
|
|
13156
|
-
{ created, updated, unchanged },
|
|
13157
|
-
[{ tool: "architecture", description: "View the map", parameters: { action: "show" } }]
|
|
13158
|
-
)
|
|
13159
|
-
};
|
|
13160
|
-
}
|
|
13161
|
-
if (action === "check") {
|
|
13162
|
-
const projectRoot = resolveProjectRoot();
|
|
13163
|
-
if (!projectRoot) {
|
|
13164
12837
|
return {
|
|
13165
12838
|
content: [{
|
|
13166
12839
|
type: "text",
|
|
13167
|
-
text:
|
|
12840
|
+
text: responseText
|
|
13168
12841
|
}],
|
|
13169
|
-
structuredContent:
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
12842
|
+
structuredContent: success(
|
|
12843
|
+
`Architecture seeded: ${created} created, ${updated} updated, ${unchanged} unchanged.`,
|
|
12844
|
+
{ created, updated, unchanged },
|
|
12845
|
+
[{ tool: "architecture", description: "View the map", parameters: { action: "show" } }]
|
|
13173
12846
|
)
|
|
13174
12847
|
};
|
|
13175
12848
|
}
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
)
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
12849
|
+
if (action === "check") {
|
|
12850
|
+
const projectRoot = resolveProjectRoot();
|
|
12851
|
+
if (!projectRoot) {
|
|
12852
|
+
return {
|
|
12853
|
+
content: [{
|
|
12854
|
+
type: "text",
|
|
12855
|
+
text: "# Scan Failed\n\nCannot find project root (looked for `convex/schema.ts` in cwd and parent). Set `WORKSPACE_PATH` env var to the absolute path of the Product OS project root."
|
|
12856
|
+
}],
|
|
12857
|
+
structuredContent: failure(
|
|
12858
|
+
"VALIDATION_ERROR",
|
|
12859
|
+
"Cannot find project root.",
|
|
12860
|
+
"Set WORKSPACE_PATH env var to the absolute path of the Product OS project root."
|
|
12861
|
+
)
|
|
12862
|
+
};
|
|
12863
|
+
}
|
|
12864
|
+
await ensureCollection();
|
|
12865
|
+
const all = await listArchEntries();
|
|
12866
|
+
const layers = byTag(all, "layer");
|
|
12867
|
+
const nodes = byTag(all, "node");
|
|
12868
|
+
const result = scanDependencies(projectRoot, layers, nodes);
|
|
12869
|
+
return {
|
|
12870
|
+
content: [{ type: "text", text: formatScanReport(result) }],
|
|
12871
|
+
structuredContent: success(
|
|
12872
|
+
result.violations.length === 0 ? `Health check passed: 0 violations across ${result.filesScanned} files.` : `Health check found ${result.violations.length} violation(s) across ${result.filesScanned} files.`,
|
|
12873
|
+
{
|
|
12874
|
+
violations: result.violations.length,
|
|
12875
|
+
filesScanned: result.filesScanned,
|
|
12876
|
+
importsChecked: result.importsChecked,
|
|
12877
|
+
unmappedImports: result.unmappedImports
|
|
12878
|
+
}
|
|
12879
|
+
)
|
|
12880
|
+
};
|
|
12881
|
+
}
|
|
12882
|
+
return unknownAction(action, ["seed", "check"]);
|
|
12883
|
+
})
|
|
12884
|
+
);
|
|
12885
|
+
trackWriteTool(archAdminTool);
|
|
12886
|
+
}
|
|
13198
12887
|
}
|
|
13199
12888
|
function scanDependencies(projectRoot, layers, nodes) {
|
|
13200
12889
|
const layerMap = /* @__PURE__ */ new Map();
|
|
@@ -13429,29 +13118,6 @@ async function markOrientedWithSnapshotFallback(agentSessionId, coherenceSnapsho
|
|
|
13429
13118
|
}
|
|
13430
13119
|
}
|
|
13431
13120
|
}
|
|
13432
|
-
function extractSessionEntryIds(priorSessions) {
|
|
13433
|
-
const allSeen = /* @__PURE__ */ new Set();
|
|
13434
|
-
const all = [];
|
|
13435
|
-
let lastSessionOnly = [];
|
|
13436
|
-
for (let i = 0; i < priorSessions.length; i++) {
|
|
13437
|
-
const s = priorSessions[i];
|
|
13438
|
-
const created = Array.isArray(s.entriesCreated) ? s.entriesCreated : [];
|
|
13439
|
-
const modified = Array.isArray(s.entriesModified) ? s.entriesModified : [];
|
|
13440
|
-
const ids = [...created, ...modified].filter((id) => typeof id === "string" && id.length > 0);
|
|
13441
|
-
if (i === 0) {
|
|
13442
|
-
lastSessionOnly = [...new Set(ids)].slice(0, 5);
|
|
13443
|
-
}
|
|
13444
|
-
for (const id of ids) {
|
|
13445
|
-
if (!allSeen.has(id)) {
|
|
13446
|
-
allSeen.add(id);
|
|
13447
|
-
all.push(id);
|
|
13448
|
-
if (all.length >= 10) break;
|
|
13449
|
-
}
|
|
13450
|
-
}
|
|
13451
|
-
if (all.length >= 10) break;
|
|
13452
|
-
}
|
|
13453
|
-
return { all, lastSessionOnly };
|
|
13454
|
-
}
|
|
13455
13121
|
var VALID_TASK_DOMAINS = [
|
|
13456
13122
|
"auth",
|
|
13457
13123
|
"strategy",
|
|
@@ -13547,29 +13213,22 @@ function registerOrientTool(server) {
|
|
|
13547
13213
|
}
|
|
13548
13214
|
let priorSessions = [];
|
|
13549
13215
|
let recoveryBlock = null;
|
|
13550
|
-
|
|
13551
|
-
try {
|
|
13552
|
-
const sessionsResult = await kernelQuery("agent.recentSessions", { limit: 3 });
|
|
13553
|
-
priorSessions = sessionsResult?.sessions ?? [];
|
|
13554
|
-
recoveryBlock = sessionsResult?.recoveryBlock ?? null;
|
|
13555
|
-
} catch {
|
|
13556
|
-
}
|
|
13557
|
-
}
|
|
13558
|
-
const { all: sessionEntryIds, lastSessionOnly } = extractSessionEntryIds(priorSessions);
|
|
13559
|
-
let orientEntries = null;
|
|
13216
|
+
let orientView = null;
|
|
13560
13217
|
try {
|
|
13561
13218
|
const orientArgs = {};
|
|
13562
13219
|
if (task) orientArgs.task = task;
|
|
13563
13220
|
if (scope) orientArgs.scope = scope;
|
|
13564
|
-
if (sessionEntryIds.length > 0) orientArgs.sessionEntryIds = sessionEntryIds;
|
|
13565
|
-
if (lastSessionOnly.length > 0) orientArgs.lastSessionEntryIds = lastSessionOnly;
|
|
13566
13221
|
orientArgs.tier = effectiveTier;
|
|
13567
|
-
|
|
13222
|
+
orientView = await kernelQuery("chain.getOrientView", orientArgs);
|
|
13568
13223
|
} catch {
|
|
13569
13224
|
}
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13225
|
+
if (orientView) {
|
|
13226
|
+
priorSessions = orientView.priorSessions ?? [];
|
|
13227
|
+
recoveryBlock = orientView.recoveryBlock ?? null;
|
|
13228
|
+
}
|
|
13229
|
+
const hasTaskGrounding = Boolean(task && orientView?.taskContext?.context?.length > 0);
|
|
13230
|
+
if (wsCtx && hasTaskGrounding && orientView?.writeBackHints) {
|
|
13231
|
+
const hints = Array.isArray(orientView.writeBackHints) ? orientView.writeBackHints : [];
|
|
13573
13232
|
if (hints.length > 0) {
|
|
13574
13233
|
trackWriteBackHintServed(wsCtx.workspaceId, {
|
|
13575
13234
|
hint_count: hints.length,
|
|
@@ -13620,9 +13279,9 @@ function registerOrientTool(server) {
|
|
|
13620
13279
|
const blankResult = {
|
|
13621
13280
|
content: [{ type: "text", text: scanLines.join("\n") }],
|
|
13622
13281
|
structuredContent: success(
|
|
13623
|
-
"Workspace is blank. Use the
|
|
13624
|
-
{ stage: "blank", readinessScore: readiness?.score ?? 0, oriented, orientationStatus: orientationStatus2, redirectHint: "Use the `
|
|
13625
|
-
[{ tool: "
|
|
13282
|
+
"Workspace is blank. Use the start_pb tool for guided setup.",
|
|
13283
|
+
{ stage: "blank", readinessScore: readiness?.score ?? 0, oriented, orientationStatus: orientationStatus2, redirectHint: "Use the `start_pb` tool for the full guided setup experience." },
|
|
13284
|
+
[{ tool: "start_pb", description: "Guided workspace setup", parameters: {} }]
|
|
13626
13285
|
)
|
|
13627
13286
|
};
|
|
13628
13287
|
reportOrientWrapperBytes(blankResult, false);
|
|
@@ -13642,8 +13301,8 @@ function registerOrientTool(server) {
|
|
|
13642
13301
|
if (mode === "brief") {
|
|
13643
13302
|
const briefStage = readiness?.stage ?? (readiness?.score != null ? readiness.score < 50 ? "seeded" : "grounded" : "unknown");
|
|
13644
13303
|
lines.push(`Brain stage: ${briefStage}`);
|
|
13645
|
-
if (
|
|
13646
|
-
const sc =
|
|
13304
|
+
if (orientView?.strategicContext) {
|
|
13305
|
+
const sc = orientView.strategicContext;
|
|
13647
13306
|
if (sc.vision) lines.push(`Vision: ${sc.vision}`);
|
|
13648
13307
|
if (sc.purpose) lines.push(`Purpose: ${sc.purpose}`);
|
|
13649
13308
|
if (sc.productAreaCount != null && sc.productAreaCount > 0) {
|
|
@@ -13654,11 +13313,11 @@ function registerOrientTool(server) {
|
|
|
13654
13313
|
}
|
|
13655
13314
|
lines.push(`${sc.activeBetCount} active bet(s), ${sc.activeTensionCount} tension(s).`);
|
|
13656
13315
|
}
|
|
13657
|
-
if (
|
|
13658
|
-
lines.push(`Task context: ${
|
|
13316
|
+
if (orientView?.taskContext && orientView.taskContext.context.length > 0) {
|
|
13317
|
+
lines.push(`Task context: ${orientView.taskContext.totalFound} relevant entries (${orientView.taskContext.confidence} confidence).`);
|
|
13659
13318
|
}
|
|
13660
|
-
if (
|
|
13661
|
-
const retrieval =
|
|
13319
|
+
if (orientView?.startup?.domainRetrieval) {
|
|
13320
|
+
const retrieval = orientView.startup.domainRetrieval;
|
|
13662
13321
|
const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
|
|
13663
13322
|
lines.push(`Domain retrieval: ${retrieval.state}${scopedTo}`);
|
|
13664
13323
|
if (retrieval.activeSource) lines.push(`Active source: ${retrieval.activeSource}`);
|
|
@@ -13669,29 +13328,29 @@ function registerOrientTool(server) {
|
|
|
13669
13328
|
lines.push(`Domain relation evidence: ${retrieval.directRatifiedCount ?? 0} direct, ${retrieval.inheritedRatifiedCount ?? 0} inherited, ${retrieval.orgFallbackCount ?? 0} org fallback`);
|
|
13670
13329
|
}
|
|
13671
13330
|
}
|
|
13672
|
-
if (
|
|
13331
|
+
if (orientView?.writeBackHints && orientView.writeBackHints.length > 0) {
|
|
13673
13332
|
lines.push("");
|
|
13674
13333
|
lines.push("Write-back hints (what to capture this session):");
|
|
13675
|
-
for (const h of
|
|
13334
|
+
for (const h of orientView.writeBackHints) {
|
|
13676
13335
|
lines.push(` ${h.collectionSlug}: ${h.hint}`);
|
|
13677
13336
|
}
|
|
13678
13337
|
}
|
|
13679
|
-
if (
|
|
13680
|
-
for (const e of
|
|
13338
|
+
if (orientView?.activeBets?.length > 0) {
|
|
13339
|
+
for (const e of orientView.activeBets) {
|
|
13681
13340
|
const tensions = e.linkedTensions;
|
|
13682
13341
|
const tensionPart = tensions?.length ? ` \u2014 ${tensions.map((t) => `${t.entryId ?? t.name} (${t.severity ?? "\u2014"})`).join(", ")}` : "";
|
|
13683
13342
|
const originPart = e.origin ? ` (origin: ${e.origin})` : "";
|
|
13684
13343
|
lines.push(`- \`${e.entryId ?? e._id}\` ${e.name}${originPart}${tensionPart}`);
|
|
13685
13344
|
}
|
|
13686
13345
|
}
|
|
13687
|
-
if (
|
|
13688
|
-
const tm =
|
|
13346
|
+
if (orientView?.trustMetrics) {
|
|
13347
|
+
const tm = orientView.trustMetrics;
|
|
13689
13348
|
if (tm.unverified > 0 || tm.verified > 0) {
|
|
13690
13349
|
const capNote = tm.scannedCap ? "+" : "";
|
|
13691
13350
|
lines.push(`Trust: ${tm.verified} verified, ${tm.unverified} unverified, ${tm.noStatus} no-status (${tm.total}${capNote} scanned).`);
|
|
13692
13351
|
}
|
|
13693
13352
|
}
|
|
13694
|
-
const briefWna = formatWhatNeedsAttentionBrief(
|
|
13353
|
+
const briefWna = formatWhatNeedsAttentionBrief(orientView?.whatNeedsAttention);
|
|
13695
13354
|
if (briefWna.length > 0) {
|
|
13696
13355
|
lines.push("");
|
|
13697
13356
|
lines.push(...briefWna);
|
|
@@ -13728,7 +13387,7 @@ function registerOrientTool(server) {
|
|
|
13728
13387
|
lines.push(...docCompleteness.lines);
|
|
13729
13388
|
}
|
|
13730
13389
|
}
|
|
13731
|
-
if (
|
|
13390
|
+
if (orientView) {
|
|
13732
13391
|
lines.push("");
|
|
13733
13392
|
if (task) {
|
|
13734
13393
|
const mapGovernanceEntry = (e) => ({
|
|
@@ -13737,10 +13396,11 @@ function registerOrientTool(server) {
|
|
|
13737
13396
|
description: typeof e.preview === "string" ? e.preview : void 0,
|
|
13738
13397
|
tier: typeof e.tier === "number" ? e.tier : void 0
|
|
13739
13398
|
});
|
|
13399
|
+
const gov = orientView.governance ?? { principles: [], standards: [], businessRules: [] };
|
|
13740
13400
|
lines.push(...buildOperatingProtocol({
|
|
13741
|
-
principles: (
|
|
13742
|
-
standards: (
|
|
13743
|
-
businessRules: (
|
|
13401
|
+
principles: (gov.principles ?? []).map(mapGovernanceEntry),
|
|
13402
|
+
standards: (gov.standards ?? []).map(mapGovernanceEntry),
|
|
13403
|
+
businessRules: (gov.businessRules ?? []).map(mapGovernanceEntry)
|
|
13744
13404
|
}, task));
|
|
13745
13405
|
} else {
|
|
13746
13406
|
lines.push(...buildOperatingProtocol());
|
|
@@ -13766,9 +13426,9 @@ function registerOrientTool(server) {
|
|
|
13766
13426
|
lines.push("---");
|
|
13767
13427
|
lines.push("_No active agent session. Call `session action=start` to begin._");
|
|
13768
13428
|
}
|
|
13769
|
-
const briefTruncated =
|
|
13429
|
+
const briefTruncated = orientView?._budget?.truncated ?? orientView?._truncated ?? false;
|
|
13770
13430
|
if (briefTruncated) {
|
|
13771
|
-
const reasons =
|
|
13431
|
+
const reasons = orientView?._budget?.truncationReasons;
|
|
13772
13432
|
lines.push("");
|
|
13773
13433
|
if (reasons && reasons.length > 0) {
|
|
13774
13434
|
lines.push(`_Context truncated to fit tier budget: ${reasons.join(", ")}. Use tier=full for complete payload._`);
|
|
@@ -13792,7 +13452,8 @@ function registerOrientTool(server) {
|
|
|
13792
13452
|
taskGroundingStatus: hasTaskGrounding ? "task_scoped" : task ? "missing_task_context" : "workspace_only",
|
|
13793
13453
|
taskGroundingRequired: !hasTaskGrounding,
|
|
13794
13454
|
orientationStatus: orientationStatus2,
|
|
13795
|
-
nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.'
|
|
13455
|
+
nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.',
|
|
13456
|
+
...orientView?._budget ? { _budget: orientView._budget } : {}
|
|
13796
13457
|
}
|
|
13797
13458
|
)
|
|
13798
13459
|
};
|
|
@@ -13851,8 +13512,8 @@ function registerOrientTool(server) {
|
|
|
13851
13512
|
if (task) {
|
|
13852
13513
|
lines.push(`**Brain stage: ${orientStage}.** Working on: **${task}**`);
|
|
13853
13514
|
lines.push("");
|
|
13854
|
-
if (
|
|
13855
|
-
const sc =
|
|
13515
|
+
if (orientView?.strategicContext) {
|
|
13516
|
+
const sc = orientView.strategicContext;
|
|
13856
13517
|
lines.push("## Strategic Context");
|
|
13857
13518
|
if (sc.vision) lines.push(`**Vision:** ${sc.vision}`);
|
|
13858
13519
|
if (sc.purpose) lines.push(`**Purpose:** ${sc.purpose}`);
|
|
@@ -13866,11 +13527,11 @@ function registerOrientTool(server) {
|
|
|
13866
13527
|
lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
|
|
13867
13528
|
lines.push("");
|
|
13868
13529
|
}
|
|
13869
|
-
if (
|
|
13530
|
+
if (orientView?.continuingFrom && orientView.continuingFrom.length > 0) {
|
|
13870
13531
|
lines.push("## Continuing from");
|
|
13871
13532
|
lines.push("_Prior-session entries most relevant to your task._");
|
|
13872
13533
|
lines.push("");
|
|
13873
|
-
for (const e of
|
|
13534
|
+
for (const e of orientView.continuingFrom) {
|
|
13874
13535
|
const id = e.entryId ?? e.name;
|
|
13875
13536
|
const type = e.canonicalKey ?? "generic";
|
|
13876
13537
|
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
@@ -13889,11 +13550,11 @@ function registerOrientTool(server) {
|
|
|
13889
13550
|
}
|
|
13890
13551
|
lines.push("");
|
|
13891
13552
|
}
|
|
13892
|
-
if (
|
|
13553
|
+
if (orientView?.lastSessionTouched && orientView.lastSessionTouched.length > 0) {
|
|
13893
13554
|
lines.push("## Last session touched");
|
|
13894
13555
|
lines.push("_Entries created or modified in your most recent session._");
|
|
13895
13556
|
lines.push("");
|
|
13896
|
-
for (const e of
|
|
13557
|
+
for (const e of orientView.lastSessionTouched) {
|
|
13897
13558
|
const id = e.entryId ?? e.name;
|
|
13898
13559
|
const type = e.canonicalKey ?? "generic";
|
|
13899
13560
|
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
@@ -13911,11 +13572,11 @@ function registerOrientTool(server) {
|
|
|
13911
13572
|
}
|
|
13912
13573
|
lines.push("");
|
|
13913
13574
|
}
|
|
13914
|
-
if (
|
|
13915
|
-
const tc =
|
|
13575
|
+
if (orientView?.taskContext && orientView.taskContext.context.length > 0) {
|
|
13576
|
+
const tc = orientView.taskContext;
|
|
13916
13577
|
lines.push("## Task Context");
|
|
13917
|
-
if (
|
|
13918
|
-
const retrieval =
|
|
13578
|
+
if (orientView.startup?.domainRetrieval) {
|
|
13579
|
+
const retrieval = orientView.startup.domainRetrieval;
|
|
13919
13580
|
const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
|
|
13920
13581
|
lines.push(`_Domain retrieval: ${retrieval.state}${scopedTo}_`);
|
|
13921
13582
|
if (retrieval.activeSource) lines.push(`_Active source: ${retrieval.activeSource}_`);
|
|
@@ -13935,34 +13596,34 @@ function registerOrientTool(server) {
|
|
|
13935
13596
|
}
|
|
13936
13597
|
lines.push("");
|
|
13937
13598
|
}
|
|
13938
|
-
if (
|
|
13599
|
+
if (orientView?.taskContext?.constellationEntries && orientView.taskContext.constellationEntries.length > 0) {
|
|
13939
13600
|
lines.push(...formatBetConstellationLines(
|
|
13940
|
-
|
|
13941
|
-
|
|
13601
|
+
orientView.taskContext.constellationEntries,
|
|
13602
|
+
orientView.taskContext.constellationBetName
|
|
13942
13603
|
));
|
|
13943
13604
|
}
|
|
13944
|
-
if (
|
|
13605
|
+
if (orientView?.writeBackHints && orientView.writeBackHints.length > 0) {
|
|
13945
13606
|
lines.push("");
|
|
13946
13607
|
lines.push("## Write-Back Hints");
|
|
13947
13608
|
lines.push("_What to capture this session (derived from task context):_");
|
|
13948
13609
|
lines.push("");
|
|
13949
|
-
for (const h of
|
|
13610
|
+
for (const h of orientView.writeBackHints) {
|
|
13950
13611
|
lines.push(`- **${h.collectionSlug}**: ${h.hint}`);
|
|
13951
13612
|
}
|
|
13952
13613
|
lines.push("");
|
|
13953
13614
|
}
|
|
13954
|
-
if (
|
|
13615
|
+
if (orientView) {
|
|
13955
13616
|
const result = await runAlignmentCheck(
|
|
13956
13617
|
task,
|
|
13957
|
-
|
|
13958
|
-
|
|
13618
|
+
orientView.activeBets ?? [],
|
|
13619
|
+
orientView.taskContext?.context
|
|
13959
13620
|
);
|
|
13960
13621
|
lines.push(...buildAlignmentCheckLines(result));
|
|
13961
|
-
lines.push(...formatInitiativeStatusLines(
|
|
13962
|
-
lines.push(...formatWorkstreamHealthLines(
|
|
13963
|
-
lines.push(...formatWhatNeedsAttentionLines(
|
|
13964
|
-
if (
|
|
13965
|
-
const tm =
|
|
13622
|
+
lines.push(...formatInitiativeStatusLines(orientView.initiativeStatus));
|
|
13623
|
+
lines.push(...formatWorkstreamHealthLines(orientView.workstreamHealth));
|
|
13624
|
+
lines.push(...formatWhatNeedsAttentionLines(orientView.whatNeedsAttention));
|
|
13625
|
+
if (orientView.trustMetrics) {
|
|
13626
|
+
const tm = orientView.trustMetrics;
|
|
13966
13627
|
if (tm.verified > 0 || tm.unverified > 0) {
|
|
13967
13628
|
const capNote = tm.scannedCap ? " (capped at 500)" : "";
|
|
13968
13629
|
lines.push("## Trust metrics");
|
|
@@ -13985,12 +13646,12 @@ function registerOrientTool(server) {
|
|
|
13985
13646
|
lines.push(...docCompleteness.lines);
|
|
13986
13647
|
}
|
|
13987
13648
|
}
|
|
13988
|
-
if (
|
|
13989
|
-
if (
|
|
13649
|
+
if (orientView) {
|
|
13650
|
+
if (orientView.activeBets?.length > 0) {
|
|
13990
13651
|
lines.push("## Active bets \u2014 current scope");
|
|
13991
13652
|
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
13992
13653
|
lines.push("");
|
|
13993
|
-
for (const e of
|
|
13654
|
+
for (const e of orientView.activeBets) {
|
|
13994
13655
|
lines.push(fmt(e));
|
|
13995
13656
|
const tensions = e.linkedTensions;
|
|
13996
13657
|
if (tensions?.length) {
|
|
@@ -14003,44 +13664,44 @@ function registerOrientTool(server) {
|
|
|
14003
13664
|
}
|
|
14004
13665
|
lines.push("");
|
|
14005
13666
|
}
|
|
14006
|
-
if (
|
|
13667
|
+
if (orientView.activeGoals?.length > 0) {
|
|
14007
13668
|
lines.push("## Active goals");
|
|
14008
|
-
|
|
13669
|
+
orientView.activeGoals.forEach((e) => lines.push(fmt(e)));
|
|
14009
13670
|
lines.push("");
|
|
14010
13671
|
}
|
|
14011
|
-
if (
|
|
13672
|
+
if (orientView.strategyHighlights?.length > 0) {
|
|
14012
13673
|
lines.push("## Strategy highlights");
|
|
14013
13674
|
lines.push("_One-sentence strategy, positioning, moat, business model, GTM \u2014 high-level strategic context._");
|
|
14014
13675
|
lines.push("");
|
|
14015
|
-
|
|
13676
|
+
orientView.strategyHighlights.forEach((e) => lines.push(fmt(e)));
|
|
14016
13677
|
lines.push("");
|
|
14017
13678
|
}
|
|
14018
|
-
if (
|
|
13679
|
+
if (orientView.playingField?.length > 0) {
|
|
14019
13680
|
lines.push("## Playing field");
|
|
14020
13681
|
lines.push("_Products and opportunities in the strategic landscape._");
|
|
14021
13682
|
lines.push("");
|
|
14022
|
-
|
|
13683
|
+
orientView.playingField.forEach((e) => lines.push(fmt(e)));
|
|
14023
13684
|
lines.push("");
|
|
14024
13685
|
}
|
|
14025
|
-
if (
|
|
13686
|
+
if (orientView.recentDecisions?.length > 0) {
|
|
14026
13687
|
lines.push("## Recent decisions");
|
|
14027
|
-
|
|
13688
|
+
orientView.recentDecisions.forEach((e) => lines.push(fmt(e)));
|
|
14028
13689
|
lines.push("");
|
|
14029
13690
|
}
|
|
14030
|
-
if (
|
|
13691
|
+
if (orientView.recentlySuperseded?.length > 0) {
|
|
14031
13692
|
lines.push("## Recently superseded");
|
|
14032
|
-
|
|
13693
|
+
orientView.recentlySuperseded.forEach((e) => lines.push(fmt(e)));
|
|
14033
13694
|
lines.push("");
|
|
14034
13695
|
}
|
|
14035
|
-
if (
|
|
13696
|
+
if (orientView.staleEntries?.length > 0) {
|
|
14036
13697
|
lines.push("## Needs confirmation");
|
|
14037
|
-
lines.push(`_Domain stratum entries not confirmed in ${
|
|
14038
|
-
|
|
13698
|
+
lines.push(`_Domain stratum entries not confirmed in ${orientView.stalenessThresholdDays} days._`);
|
|
13699
|
+
orientView.staleEntries.forEach((e) => lines.push(fmt(e)));
|
|
14039
13700
|
lines.push("");
|
|
14040
13701
|
}
|
|
14041
|
-
if (
|
|
13702
|
+
if (orientView.architectureNotes?.length > 0) {
|
|
14042
13703
|
lines.push("## Architecture notes");
|
|
14043
|
-
|
|
13704
|
+
orientView.architectureNotes.forEach((e) => lines.push(fmt(e)));
|
|
14044
13705
|
lines.push("");
|
|
14045
13706
|
}
|
|
14046
13707
|
const mapGovernanceEntry = (e) => ({
|
|
@@ -14049,10 +13710,11 @@ function registerOrientTool(server) {
|
|
|
14049
13710
|
description: typeof e.preview === "string" ? e.preview : void 0,
|
|
14050
13711
|
tier: typeof e.tier === "number" ? e.tier : void 0
|
|
14051
13712
|
});
|
|
13713
|
+
const gov = orientView.governance ?? { principles: [], standards: [], businessRules: [] };
|
|
14052
13714
|
lines.push(...buildOperatingProtocol({
|
|
14053
|
-
principles: (
|
|
14054
|
-
standards: (
|
|
14055
|
-
businessRules: (
|
|
13715
|
+
principles: (gov.principles ?? []).map(mapGovernanceEntry),
|
|
13716
|
+
standards: (gov.standards ?? []).map(mapGovernanceEntry),
|
|
13717
|
+
businessRules: (gov.businessRules ?? []).map(mapGovernanceEntry)
|
|
14056
13718
|
}, task));
|
|
14057
13719
|
}
|
|
14058
13720
|
let allEntries = [];
|
|
@@ -14069,20 +13731,20 @@ function registerOrientTool(server) {
|
|
|
14069
13731
|
} else {
|
|
14070
13732
|
lines.push(`**Brain stage: ${orientStage}.**`);
|
|
14071
13733
|
lines.push("");
|
|
14072
|
-
if (
|
|
13734
|
+
if (orientView?.activeBets?.length) {
|
|
14073
13735
|
lines.push("## Active bets \u2014 current scope");
|
|
14074
13736
|
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
14075
13737
|
lines.push("");
|
|
14076
|
-
for (const e of
|
|
13738
|
+
for (const e of orientView.activeBets) {
|
|
14077
13739
|
lines.push(fmt(e));
|
|
14078
13740
|
}
|
|
14079
13741
|
lines.push("");
|
|
14080
13742
|
}
|
|
14081
|
-
lines.push(...formatInitiativeStatusLines(
|
|
14082
|
-
lines.push(...formatWorkstreamHealthLines(
|
|
14083
|
-
lines.push(...formatWhatNeedsAttentionLines(
|
|
14084
|
-
if (
|
|
14085
|
-
const tm =
|
|
13743
|
+
lines.push(...formatInitiativeStatusLines(orientView?.initiativeStatus));
|
|
13744
|
+
lines.push(...formatWorkstreamHealthLines(orientView?.workstreamHealth));
|
|
13745
|
+
lines.push(...formatWhatNeedsAttentionLines(orientView?.whatNeedsAttention));
|
|
13746
|
+
if (orientView?.trustMetrics) {
|
|
13747
|
+
const tm = orientView.trustMetrics;
|
|
14086
13748
|
if (tm.verified > 0 || tm.unverified > 0) {
|
|
14087
13749
|
const capNote = tm.scannedCap ? " (capped at 500)" : "";
|
|
14088
13750
|
lines.push("## Trust metrics");
|
|
@@ -14165,9 +13827,9 @@ function registerOrientTool(server) {
|
|
|
14165
13827
|
lines.push("---");
|
|
14166
13828
|
lines.push("_No active agent session. Call `session action=start` to begin a tracked session._");
|
|
14167
13829
|
}
|
|
14168
|
-
const fullTruncated =
|
|
13830
|
+
const fullTruncated = orientView?._budget?.truncated ?? orientView?._truncated ?? false;
|
|
14169
13831
|
if (fullTruncated) {
|
|
14170
|
-
const reasons =
|
|
13832
|
+
const reasons = orientView?._budget?.truncationReasons;
|
|
14171
13833
|
lines.push("");
|
|
14172
13834
|
if (reasons && reasons.length > 0) {
|
|
14173
13835
|
lines.push(`_Context truncated to fit tier budget: ${reasons.join(", ")}. Use tier=full for complete payload._`);
|
|
@@ -14186,9 +13848,10 @@ function registerOrientTool(server) {
|
|
|
14186
13848
|
sessionId: agentSessionId,
|
|
14187
13849
|
taskGroundingStatus: hasTaskGrounding ? "task_scoped" : task ? "missing_task_context" : "workspace_only",
|
|
14188
13850
|
taskGroundingRequired: !hasTaskGrounding,
|
|
14189
|
-
domainRetrieval:
|
|
13851
|
+
domainRetrieval: orientView?.startup?.domainRetrieval,
|
|
14190
13852
|
orientationStatus,
|
|
14191
|
-
nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.'
|
|
13853
|
+
nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.',
|
|
13854
|
+
...orientView?._budget ? { _budget: orientView._budget } : {}
|
|
14192
13855
|
}
|
|
14193
13856
|
)
|
|
14194
13857
|
};
|
|
@@ -14607,7 +14270,7 @@ var ALL_TOOL_SCHEMAS = [
|
|
|
14607
14270
|
{ name: "update-entry", schema: updateEntrySchema },
|
|
14608
14271
|
{ name: "get-history", schema: getHistorySchema },
|
|
14609
14272
|
{ name: "commit-entry", schema: commitEntrySchema },
|
|
14610
|
-
{ name: "
|
|
14273
|
+
{ name: "start_pb", schema: startPbSchema },
|
|
14611
14274
|
{ name: "get-usage-summary", schema: usageSummarySchema },
|
|
14612
14275
|
{ name: "chain", schema: chainSchema },
|
|
14613
14276
|
{ name: "chain-version", schema: chainVersionSchema },
|
|
@@ -14701,15 +14364,61 @@ function registerHealthTools(server) {
|
|
|
14701
14364
|
);
|
|
14702
14365
|
}
|
|
14703
14366
|
|
|
14704
|
-
// src/tools/
|
|
14367
|
+
// src/tools/record_activation.ts
|
|
14705
14368
|
import { z as z24 } from "zod";
|
|
14369
|
+
var recordActivationSchema = z24.object({
|
|
14370
|
+
confirmedEntryCount: z24.number().int().min(0).describe(
|
|
14371
|
+
"Confirmed-entry count from the Phase 4 capture loop. The mutation enforces >=10 (DEC-994)."
|
|
14372
|
+
),
|
|
14373
|
+
entriesAcrossCollections: z24.number().int().min(0).describe(
|
|
14374
|
+
"Number of distinct collections those entries span. The mutation enforces >=2 (DEC-994 diversity soft-gate)."
|
|
14375
|
+
),
|
|
14376
|
+
retrievalDemoConfirmed: z24.boolean().describe(
|
|
14377
|
+
"True if the BR-141 retrieval round-trip ran successfully in Phase 4. The mutation rejects false."
|
|
14378
|
+
)
|
|
14379
|
+
});
|
|
14380
|
+
function registerRecordActivationTools(server) {
|
|
14381
|
+
server.registerTool(
|
|
14382
|
+
"record_activation",
|
|
14383
|
+
{
|
|
14384
|
+
title: "Record activation receipt \u2014 chat-only",
|
|
14385
|
+
description: "Writes the per-user activation receipt that closes the WP-431 install-to-activation arc for chat-only surfaces (Cursor Desktop, Claude Desktop, ChatGPT). The skill body's Phase 5 calls this AFTER Phase 4 gates pass: confirmedEntryCount>=10, entriesAcrossCollections>=2, retrievalDemoConfirmed=true. surfaceCapability is hardcoded to 'chat-only' \u2014 this tool is NOT for CLI agents (CLI uses the gateway directly). Idempotent: a re-run after activation returns alreadyActivated=true.",
|
|
14386
|
+
inputSchema: recordActivationSchema,
|
|
14387
|
+
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false, destructiveHint: false }
|
|
14388
|
+
},
|
|
14389
|
+
thinWrapper(async ({ confirmedEntryCount, entriesAcrossCollections, retrievalDemoConfirmed }) => {
|
|
14390
|
+
const mutationResult = await kernelMutation(
|
|
14391
|
+
"setup.recordActivationReceipt",
|
|
14392
|
+
{
|
|
14393
|
+
surfaceCapability: "chat-only",
|
|
14394
|
+
confirmedEntryCount,
|
|
14395
|
+
entriesAcrossCollections,
|
|
14396
|
+
retrievalDemoConfirmed
|
|
14397
|
+
}
|
|
14398
|
+
);
|
|
14399
|
+
const result = {
|
|
14400
|
+
activated: true,
|
|
14401
|
+
alreadyActivated: mutationResult.alreadyActivated,
|
|
14402
|
+
receiptId: mutationResult.receiptId ?? null
|
|
14403
|
+
};
|
|
14404
|
+
const summary = mutationResult.alreadyActivated ? "Activation receipt already recorded \u2014 chat-only flow short-circuits." : "Activation recorded \u2014 chat-only flow complete.";
|
|
14405
|
+
return {
|
|
14406
|
+
content: textContent(summary),
|
|
14407
|
+
structuredContent: success(summary, result)
|
|
14408
|
+
};
|
|
14409
|
+
})
|
|
14410
|
+
);
|
|
14411
|
+
}
|
|
14412
|
+
|
|
14413
|
+
// src/tools/audit.ts
|
|
14414
|
+
import { z as z25 } from "zod";
|
|
14706
14415
|
var AUDIT_ACTIONS = ["run"];
|
|
14707
|
-
var auditSchema =
|
|
14708
|
-
action:
|
|
14416
|
+
var auditSchema = z25.object({
|
|
14417
|
+
action: z25.enum(AUDIT_ACTIONS).describe(
|
|
14709
14418
|
"'run': run the STD-113 hygiene audit for a bet entry."
|
|
14710
14419
|
),
|
|
14711
|
-
entryId:
|
|
14712
|
-
phase:
|
|
14420
|
+
entryId: z25.string().describe("Bet entry ID to audit, e.g. 'BET-182'"),
|
|
14421
|
+
phase: z25.enum(["shaping", "handoff"]).default("shaping").optional().describe(
|
|
14713
14422
|
"'shaping': check shaping-phase fields only. 'handoff': check all required fields including buildContract/buildSequence/exclusions/risks. Default: shaping."
|
|
14714
14423
|
)
|
|
14715
14424
|
});
|
|
@@ -14718,7 +14427,7 @@ function registerAuditTools(server) {
|
|
|
14718
14427
|
"audit",
|
|
14719
14428
|
{
|
|
14720
14429
|
title: "Audit",
|
|
14721
|
-
description: "Run STD-113 hygiene audit on a bet entry. Checks 13 gates covering:\n\n- **Relations**: intentional links, no auto-link noise, strategic anchoring\n- **Risk/Tension**: risks in correct collection, tensions linked\n- **Field completeness**: mandatory fields populated for current phase\n- **Elements**: correct collection, committed status\n- **Acceptance criteria**: doneWhen populated and verifiable\n- **Structural**: no circular relations, appetite consistency\n\nUse `phase=shaping` (default) before starting implementation. Use `phase=handoff` before handing off to AI Engineer.",
|
|
14430
|
+
description: "Run STD-113 hygiene audit on a bet entry. Checks 13 gates (shaping) / 18 gates (handoff) covering:\n\n- **Relations**: intentional links, no auto-link noise, strategic anchoring\n- **Risk/Tension**: risks in correct collection, tensions linked\n- **Field completeness**: mandatory fields populated for current phase\n- **Elements**: correct collection, committed status\n- **Acceptance criteria**: doneWhen populated and verifiable\n- **Structural**: no circular relations, appetite consistency\n\nUse `phase=shaping` (default) before starting implementation. Use `phase=handoff` before handing off to AI Engineer.",
|
|
14722
14431
|
inputSchema: auditSchema,
|
|
14723
14432
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
14724
14433
|
},
|
|
@@ -14820,13 +14529,13 @@ async function handleAuditRun(entryId, phase) {
|
|
|
14820
14529
|
}
|
|
14821
14530
|
|
|
14822
14531
|
// src/tools/governance.ts
|
|
14823
|
-
import { z as
|
|
14824
|
-
var governanceSchema =
|
|
14825
|
-
action:
|
|
14826
|
-
proposalId:
|
|
14827
|
-
verdict:
|
|
14828
|
-
reason:
|
|
14829
|
-
status:
|
|
14532
|
+
import { z as z26 } from "zod";
|
|
14533
|
+
var governanceSchema = z26.object({
|
|
14534
|
+
action: z26.enum(["list", "respond", "count"]).describe("Action: list open proposals, respond to a proposal, or count open proposals"),
|
|
14535
|
+
proposalId: z26.string().optional().describe("Proposal ID (required for respond action)"),
|
|
14536
|
+
verdict: z26.enum(["approve", "reject"]).optional().describe("Verdict for respond action: approve or reject"),
|
|
14537
|
+
reason: z26.string().optional().describe("Reason for the verdict (required when rejecting)"),
|
|
14538
|
+
status: z26.enum(["open", "approved", "objected", "expired"]).optional().describe("Filter proposals by status (default: open). Only used with list action.")
|
|
14830
14539
|
});
|
|
14831
14540
|
function registerGovernanceTools(server) {
|
|
14832
14541
|
const tool = server.registerTool(
|
|
@@ -14952,16 +14661,16 @@ function registerGovernanceTools(server) {
|
|
|
14952
14661
|
}
|
|
14953
14662
|
|
|
14954
14663
|
// src/tools/documents.ts
|
|
14955
|
-
import { z as
|
|
14664
|
+
import { z as z27 } from "zod";
|
|
14956
14665
|
var DOCUMENTS_ACTIONS = ["get-last-verified-brief"];
|
|
14957
|
-
var documentsSchema =
|
|
14958
|
-
action:
|
|
14666
|
+
var documentsSchema = z27.object({
|
|
14667
|
+
action: z27.enum(DOCUMENTS_ACTIONS).describe(
|
|
14959
14668
|
"'get-last-verified-brief': fetch the most recent verified brief snapshot for a (templateId, scopeKey) pair. Returns the verified summary so agents can build delta narratives ('since you last verified, X workstreams advanced')."
|
|
14960
14669
|
),
|
|
14961
|
-
templateId:
|
|
14670
|
+
templateId: z27.string().describe(
|
|
14962
14671
|
"Brief template identifier \u2014 currently 'steering-brief' is the only registered template."
|
|
14963
14672
|
),
|
|
14964
|
-
scopeKey:
|
|
14673
|
+
scopeKey: z27.string().describe(
|
|
14965
14674
|
"Canonical scope key. Use 'workspace:<workspaceId>' for the full workspace brief, or 'initiative:<INI-ID>' for an initiative-scoped brief. The same formula is applied at write time (chainwork/docKernel/scopeKey.ts), so passing the wrong shape returns exists:false."
|
|
14966
14675
|
)
|
|
14967
14676
|
});
|
|
@@ -15559,12 +15268,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
|
|
|
15559
15268
|
}
|
|
15560
15269
|
|
|
15561
15270
|
// src/prompts/index.ts
|
|
15562
|
-
import { z as
|
|
15271
|
+
import { z as z28 } from "zod";
|
|
15563
15272
|
function registerPrompts(server) {
|
|
15564
15273
|
server.prompt(
|
|
15565
15274
|
"review-against-rules",
|
|
15566
15275
|
"Review code or a design decision against all business rules for a given domain. Fetches the rules and asks you to do a structured compliance review.",
|
|
15567
|
-
{ domain:
|
|
15276
|
+
{ domain: z28.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
|
|
15568
15277
|
async ({ domain }) => {
|
|
15569
15278
|
const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
|
|
15570
15279
|
const rules = entries.filter((e) => e.data?.domain === domain);
|
|
@@ -15617,7 +15326,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
|
|
|
15617
15326
|
server.prompt(
|
|
15618
15327
|
"name-check",
|
|
15619
15328
|
"Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
|
|
15620
|
-
{ names:
|
|
15329
|
+
{ names: z28.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
|
|
15621
15330
|
async ({ names }) => {
|
|
15622
15331
|
const terms = await kernelQuery("chain.listEntries", { collectionSlug: "glossary" });
|
|
15623
15332
|
const glossaryContext = terms.map(
|
|
@@ -15653,7 +15362,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
|
|
|
15653
15362
|
server.prompt(
|
|
15654
15363
|
"draft-decision-record",
|
|
15655
15364
|
"Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
|
|
15656
|
-
{ context:
|
|
15365
|
+
{ context: z28.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
|
|
15657
15366
|
async ({ context }) => {
|
|
15658
15367
|
const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
|
|
15659
15368
|
const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
|
|
@@ -15691,8 +15400,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
|
|
|
15691
15400
|
"draft-rule-from-context",
|
|
15692
15401
|
"Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
|
|
15693
15402
|
{
|
|
15694
|
-
observation:
|
|
15695
|
-
domain:
|
|
15403
|
+
observation: z28.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
|
|
15404
|
+
domain: z28.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
|
|
15696
15405
|
},
|
|
15697
15406
|
async ({ observation, domain }) => {
|
|
15698
15407
|
const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
|
|
@@ -15770,7 +15479,7 @@ var INSTRUCTIONS = [
|
|
|
15770
15479
|
"",
|
|
15771
15480
|
"## Workflow",
|
|
15772
15481
|
"",
|
|
15773
|
-
" 1. Start: call `
|
|
15482
|
+
" 1. Start: call `start_pb` to begin \u2014 it starts a session automatically, detects your workspace stage, and returns the right guidance. Fresh workspaces get the pb-setup skill body; active workspaces get a standup briefing.",
|
|
15774
15483
|
" 2. Re-orient: call `orient` mid-session for task-scoped context or a compact status refresh.",
|
|
15775
15484
|
" 3. Discover: use `entries action=search` to find entries, or `entries action=list` to browse.",
|
|
15776
15485
|
' 4. Drill in: use `entries action=get entryId="..."` for full details \u2014 data, labels, relations, history.',
|
|
@@ -15782,7 +15491,7 @@ var INSTRUCTIONS = [
|
|
|
15782
15491
|
" 10. Close: call `session action=close` when done \u2014 records session activity. Auto-nudges if wrapup was skipped.",
|
|
15783
15492
|
"",
|
|
15784
15493
|
"Write tools (capture, update-entry, relations, commit-entry) require:",
|
|
15785
|
-
" - Call `
|
|
15494
|
+
" - Call `start_pb` to begin (starts session + loads context). Call `orient` mid-session for a refresh.",
|
|
15786
15495
|
" - A readwrite API key scope",
|
|
15787
15496
|
"",
|
|
15788
15497
|
"Commit-on-confirm: always capture as draft first and show the user what was captured.",
|
|
@@ -15831,7 +15540,9 @@ function createProductBrainServer() {
|
|
|
15831
15540
|
if (enabledModules.has("gitchain")) registerGitChainTools(server);
|
|
15832
15541
|
if (enabledModules.has("gitchain")) registerMapTools(server);
|
|
15833
15542
|
if (enabledModules.has("arch")) registerArchitectureTools(server);
|
|
15834
|
-
|
|
15543
|
+
registerSkillsTools(server);
|
|
15544
|
+
registerStartPbTools(server);
|
|
15545
|
+
registerRecordActivationTools(server);
|
|
15835
15546
|
registerUsageTools(server);
|
|
15836
15547
|
registerFacilitateTools(server);
|
|
15837
15548
|
registerAuditTools(server);
|
|
@@ -15861,12 +15572,18 @@ function readProcessEnvFlag(name) {
|
|
|
15861
15572
|
return void 0;
|
|
15862
15573
|
}
|
|
15863
15574
|
var KILL = readViteEnvFlag("VITE_FEATURE_KILL_SWITCH") === "true" || readProcessEnvFlag("FEATURE_KILL_SWITCH") === "true";
|
|
15575
|
+
var QUERY_BUDGET_KILL = readViteEnvFlag("VITE_PB_QUERY_BUDGET_KILL") === "true" || readProcessEnvFlag("PB_QUERY_BUDGET_KILL") === "true";
|
|
15864
15576
|
var WS = {
|
|
15865
15577
|
RANDY_DEV_PRIMARY: "p5796jhec0nhyp417ekrt8x4w181t41f",
|
|
15866
15578
|
RANDY_DEV_SECONDARY: "p5791wr86cj0thx2vxpqavj0mn82rssd",
|
|
15867
15579
|
RANDY_PROD_PRIMARY: "mx784e9jjdqhsk2r0098bpvtes84e792",
|
|
15868
15580
|
RANDY_PROD_SECONDARY: "mx74q0mkwn4r920q2837qk7dsx84pq60"
|
|
15869
15581
|
};
|
|
15582
|
+
var USR = {
|
|
15583
|
+
RANDY: "user_3C31UK8CWugoDXUj2RGu1HDudas",
|
|
15584
|
+
/** Niels — full MVP unlock in any workspace (Clerk JWT `sub` / `FlagCtx.clerkUserId`). */
|
|
15585
|
+
NIELS: "user_3DOKRUaiY9KS3RKtC7shak3nB0N"
|
|
15586
|
+
};
|
|
15870
15587
|
var FLAGS = {
|
|
15871
15588
|
// WP-355 — MVP unlock: full Spine navigation (all modes + access patterns visible)
|
|
15872
15589
|
"mvp-unlock-spine": {
|
|
@@ -15876,7 +15593,8 @@ var FLAGS = {
|
|
|
15876
15593
|
WS.RANDY_DEV_SECONDARY,
|
|
15877
15594
|
WS.RANDY_PROD_PRIMARY,
|
|
15878
15595
|
WS.RANDY_PROD_SECONDARY
|
|
15879
|
-
]
|
|
15596
|
+
],
|
|
15597
|
+
userAllow: [USR.NIELS]
|
|
15880
15598
|
},
|
|
15881
15599
|
// WP-355 — MVP unlock: Bridge full view (beyond locked /bridge dashboard)
|
|
15882
15600
|
"mvp-unlock-bridge-full": {
|
|
@@ -15886,7 +15604,8 @@ var FLAGS = {
|
|
|
15886
15604
|
WS.RANDY_DEV_SECONDARY,
|
|
15887
15605
|
WS.RANDY_PROD_PRIMARY,
|
|
15888
15606
|
WS.RANDY_PROD_SECONDARY
|
|
15889
|
-
]
|
|
15607
|
+
],
|
|
15608
|
+
userAllow: [USR.NIELS]
|
|
15890
15609
|
},
|
|
15891
15610
|
// WP-355 — MVP unlock: Rail full context (beyond Rail context-only mode)
|
|
15892
15611
|
"mvp-unlock-rail-full": {
|
|
@@ -15896,7 +15615,8 @@ var FLAGS = {
|
|
|
15896
15615
|
WS.RANDY_DEV_SECONDARY,
|
|
15897
15616
|
WS.RANDY_PROD_PRIMARY,
|
|
15898
15617
|
WS.RANDY_PROD_SECONDARY
|
|
15899
|
-
]
|
|
15618
|
+
],
|
|
15619
|
+
userAllow: [USR.NIELS]
|
|
15900
15620
|
},
|
|
15901
15621
|
// WP-355 — MVP unlock: Import surface enabled
|
|
15902
15622
|
"mvp-unlock-import": {
|
|
@@ -15906,9 +15626,10 @@ var FLAGS = {
|
|
|
15906
15626
|
WS.RANDY_DEV_SECONDARY,
|
|
15907
15627
|
WS.RANDY_PROD_PRIMARY,
|
|
15908
15628
|
WS.RANDY_PROD_SECONDARY
|
|
15909
|
-
]
|
|
15629
|
+
],
|
|
15630
|
+
userAllow: [USR.NIELS]
|
|
15910
15631
|
},
|
|
15911
|
-
// WP-355 — Brain Chat:
|
|
15632
|
+
// WP-355 — Brain Chat: off by default; same workspace + user allowlists as other mvp-unlock flags.
|
|
15912
15633
|
"mvp-unlock-brain-chat": {
|
|
15913
15634
|
default: false,
|
|
15914
15635
|
workspaceAllow: [
|
|
@@ -15916,13 +15637,22 @@ var FLAGS = {
|
|
|
15916
15637
|
WS.RANDY_DEV_SECONDARY,
|
|
15917
15638
|
WS.RANDY_PROD_PRIMARY,
|
|
15918
15639
|
WS.RANDY_PROD_SECONDARY
|
|
15919
|
-
]
|
|
15640
|
+
],
|
|
15641
|
+
userAllow: [USR.NIELS]
|
|
15920
15642
|
},
|
|
15921
15643
|
// WP-373 E3 — Routing Resolver SSOT killswitch.
|
|
15922
15644
|
// When true, the new pure resolver short-circuits and mount sites execute the
|
|
15923
15645
|
// legacy inline redirect/error logic verbatim. Default false: resolver runs.
|
|
15924
15646
|
// FEATURE_KILL_SWITCH env beats this flag (precedence per .claude/rules/feature-flags.md).
|
|
15925
|
-
"routing-resolver-killswitch": false
|
|
15647
|
+
"routing-resolver-killswitch": false,
|
|
15648
|
+
// WP-384 S3: Child-domain creation affordance. OFF by default — "Coming soon" tooltip per DEC-97 / STA-11.
|
|
15649
|
+
"custom-authority-domains": false,
|
|
15650
|
+
// FEAT-1139 — Query budget kill-switch.
|
|
15651
|
+
// When true, `cappedQuery` reverts to raw `.collect()` behavior (no row caps)
|
|
15652
|
+
// while still populating `_budget` for WP-380 telemetry. Default false.
|
|
15653
|
+
"query-budget-kill": {
|
|
15654
|
+
default: QUERY_BUDGET_KILL
|
|
15655
|
+
}
|
|
15926
15656
|
};
|
|
15927
15657
|
|
|
15928
15658
|
// src/featureFlags.ts
|
|
@@ -15944,4 +15674,4 @@ export {
|
|
|
15944
15674
|
createProductBrainServer,
|
|
15945
15675
|
initFeatureFlags
|
|
15946
15676
|
};
|
|
15947
|
-
//# sourceMappingURL=chunk-
|
|
15677
|
+
//# sourceMappingURL=chunk-AC5PU57Y.js.map
|