@productbrain/mcp 0.0.1-beta.63 → 0.0.1-beta.65

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.
@@ -3,6 +3,7 @@ import {
3
3
  captureSchema,
4
4
  checkEntryQuality,
5
5
  classifyCollection,
6
+ clearSessionGaps,
6
7
  closeAgentSession,
7
8
  deriveEpistemicStatus,
8
9
  extractPreview,
@@ -14,6 +15,8 @@ import {
14
15
  getAgentSessionId,
15
16
  getApiKeyScope,
16
17
  getAuditLog,
18
+ getSessionGaps,
19
+ getTopGaps,
17
20
  getWorkspaceContext,
18
21
  getWorkspaceId,
19
22
  initToolSurface,
@@ -23,6 +26,7 @@ import {
23
26
  mcpQuery,
24
27
  notFoundResult,
25
28
  parseOrFail,
29
+ recordGap,
26
30
  recordSessionActivity,
27
31
  registerSmartCaptureTools,
28
32
  requireActiveSession,
@@ -38,7 +42,7 @@ import {
38
42
  unknownAction,
39
43
  validationResult,
40
44
  withEnvelope
41
- } from "./chunk-DM5DZC7B.js";
45
+ } from "./chunk-UWILSX73.js";
42
46
  import {
43
47
  trackKnowledgeGap,
44
48
  trackQualityCheck,
@@ -233,7 +237,7 @@ ${formatted}` }],
233
237
  },
234
238
  withEnvelope(async ({ entryId }) => {
235
239
  requireWriteAccess();
236
- const { runContradictionCheck } = await import("./smart-capture-NYOKGAFS.js");
240
+ const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
237
241
  const entry = await mcpQuery("chain.getEntry", { entryId });
238
242
  if (!entry) {
239
243
  return notFoundResult(entryId, `Entry '${entryId}' not found. Try search to find the right ID.`);
@@ -353,43 +357,6 @@ ${formatted}` }],
353
357
 
354
358
  // src/tools/entries.ts
355
359
  import { z as z2 } from "zod";
356
-
357
- // src/gap-store.ts
358
- var SESSION_GAPS = [];
359
- var DEDUP_WINDOW_MS = 6e4;
360
- function isDuplicate(gap) {
361
- const cutoff = gap.timestamp - DEDUP_WINDOW_MS;
362
- return SESSION_GAPS.some(
363
- (existing) => existing.query === gap.query && existing.tool === gap.tool && existing.action === gap.action && existing.timestamp >= cutoff
364
- );
365
- }
366
- function recordGap(gap) {
367
- const timestamped = { ...gap, timestamp: Date.now() };
368
- if (isDuplicate(timestamped)) return false;
369
- SESSION_GAPS.push(timestamped);
370
- return true;
371
- }
372
- function getSessionGaps() {
373
- return SESSION_GAPS;
374
- }
375
- function getTopGaps(limit = 5) {
376
- const counts = /* @__PURE__ */ new Map();
377
- for (const gap of SESSION_GAPS) {
378
- const key = gap.query.toLowerCase().trim();
379
- const existing = counts.get(key);
380
- if (existing) {
381
- existing.count++;
382
- } else {
383
- counts.set(key, { count: 1, gapType: gap.gapType });
384
- }
385
- }
386
- return [...counts.entries()].map(([query, { count, gapType }]) => ({ query, count, gapType })).sort((a, b) => b.count - a.count).slice(0, limit);
387
- }
388
- function clearSessionGaps() {
389
- SESSION_GAPS.length = 0;
390
- }
391
-
392
- // src/tools/entries.ts
393
360
  function sanitizeEntryData(data) {
394
361
  if (!data || typeof data !== "object") return void 0;
395
362
  const filtered = Object.fromEntries(
@@ -6305,7 +6272,7 @@ async function handleCommitConstellation(args) {
6305
6272
  }
6306
6273
  let contradictionWarnings = [];
6307
6274
  try {
6308
- const { runContradictionCheck } = await import("./smart-capture-NYOKGAFS.js");
6275
+ const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
6309
6276
  const descField = betData.problem ?? betData.description ?? "";
6310
6277
  contradictionWarnings = await runContradictionCheck(
6311
6278
  betEntry.name ?? betId,
@@ -6966,23 +6933,32 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
6966
6933
  // src/tools/orient-shared.ts
6967
6934
  function runAlignmentCheck(task, activeBets, taskContextHits) {
6968
6935
  const betNames = activeBets.map((b) => b.name);
6969
- const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
6936
+ const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
6970
6937
  const matchingBet = activeBets.find((b) => {
6971
6938
  const name = (b.name ?? "").toLowerCase();
6972
6939
  return taskWords.some((w) => name.includes(w));
6973
6940
  });
6941
+ if (matchingBet) {
6942
+ return { aligned: true, matchedBet: matchingBet.name, matchSource: "active_bet", betNames };
6943
+ }
6974
6944
  const betHits = (taskContextHits ?? []).filter(
6975
6945
  (e) => e.collectionSlug === "bets"
6976
6946
  );
6977
- const aligned = !!matchingBet || betHits.length > 0;
6978
- const matchedBet = matchingBet?.name ?? betHits[0]?.name ?? null;
6979
- return { aligned, matchedBet, betNames };
6947
+ if (betHits.length > 0) {
6948
+ return { aligned: true, matchedBet: betHits[0]?.name ?? null, matchSource: "task_context", betNames };
6949
+ }
6950
+ return { aligned: false, matchedBet: null, matchSource: null, betNames };
6980
6951
  }
6981
6952
  function buildAlignmentCheckLines(result) {
6982
6953
  const lines = ["## Alignment Check"];
6983
- if (result.aligned) {
6954
+ if (result.aligned && result.matchSource === "active_bet") {
6984
6955
  lines.push(
6985
- `Task appears related to active bet: **${result.matchedBet}**. Proceed.`
6956
+ `Task aligns with active bet: **${result.matchedBet}**. Proceed.`
6957
+ );
6958
+ } else if (result.aligned && result.matchSource === "task_context") {
6959
+ const noActiveBets = result.betNames.length === 0;
6960
+ lines.push(
6961
+ `Task related to **${result.matchedBet}** via task context${noActiveBets ? " (no active bets in horizon=now)" : ""}. Proceed with caution \u2014 confirm scope with the user if uncertain.`
6986
6962
  );
6987
6963
  } else if (result.betNames.length === 0) {
6988
6964
  lines.push(
@@ -7006,8 +6982,9 @@ var CORE_PROTOCOL = [
7006
6982
  "**Validate against governance.** Before proposing any solution, check it against the Workspace Governance directives below. If your proposal conflicts with a principle, standard, or business rule \u2014 stop, name the conflict, and get explicit user confirmation before proceeding."
7007
6983
  ];
7008
6984
  var MAX_ENTRIES_NO_TASK = 3;
6985
+ var MAX_ENTRIES_WITH_TASK = 5;
7009
6986
  function extractKeywords(text) {
7010
- return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length > 3))];
6987
+ return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length > 2))];
7011
6988
  }
7012
6989
  function scoreEntry(entry, keywords) {
7013
6990
  const text = `${entry.name} ${entry.description ?? ""}`.toLowerCase();
@@ -7041,13 +7018,20 @@ function buildOperatingProtocol(governanceOrStandards, task) {
7041
7018
  if (task) {
7042
7019
  const keywords = extractKeywords(task);
7043
7020
  let anyRelevant = false;
7021
+ let shownCount = 0;
7022
+ let totalRelevant = 0;
7023
+ let totalGovernance = 0;
7044
7024
  for (const type of types) {
7045
7025
  if (type.entries.length === 0) continue;
7026
+ totalGovernance += type.entries.length;
7046
7027
  const scored = type.entries.map((e) => ({ entry: e, score: scoreEntry(e, keywords) })).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
7047
7028
  if (scored.length > 0) {
7048
7029
  anyRelevant = true;
7030
+ totalRelevant += scored.length;
7031
+ const capped = scored.slice(0, MAX_ENTRIES_WITH_TASK);
7032
+ shownCount += capped.length;
7049
7033
  lines.push(type.header);
7050
- for (const s of scored) {
7034
+ for (const s of capped) {
7051
7035
  lines.push(formatGovernanceEntry(s.entry));
7052
7036
  }
7053
7037
  lines.push("");
@@ -7062,10 +7046,15 @@ function buildOperatingProtocol(governanceOrStandards, task) {
7062
7046
  }
7063
7047
  lines.push("");
7064
7048
  }
7049
+ lines.push(
7050
+ `_No governance matched task. Showing top ${MAX_ENTRIES_NO_TASK} per type. Run \`orient\` without a task to see all._`
7051
+ );
7052
+ } else {
7053
+ const overflow = totalRelevant > shownCount ? ` ${totalRelevant - shownCount} more matched \u2014 use \`entries action=search query="..."\` before proposing.` : "";
7054
+ lines.push(
7055
+ `_${shownCount} of ${totalGovernance} governance entries shown, filtered by task.${overflow}_`
7056
+ );
7065
7057
  }
7066
- lines.push(
7067
- "_Governance filtered by task relevance. Run `orient` without a task to see all._"
7068
- );
7069
7058
  } else {
7070
7059
  for (const type of types) {
7071
7060
  if (type.entries.length === 0) continue;
@@ -7155,12 +7144,12 @@ function getInterviewInstructions(workspaceName) {
7155
7144
  return {
7156
7145
  systemPrompt: `You are activating the **${workspaceName}** Product Brain. Ask 1\u20132 focused questions, then extract structured knowledge and feed it to batch-capture. In Open governance mode, entries are committed automatically. In consensus/role mode, they stay as drafts for review.`,
7157
7146
  question1: `**Q1 \u2014 What are you building and for whom?** Describe your product in 1\u20132 sentences and who it's for. (This becomes your Product Vision and primary Audience.)`,
7158
- question2: `**Q2 (optional) \u2014 What's your tech stack or key domain terms?** Name a few core technologies or terms your team uses. (These become Architecture + Glossary entries.)`,
7147
+ question2: `**Q2 (optional)** What's one word or phrase that would trip someone up if they didn't know your context? (This becomes your first glossary term \u2014 the one that matters most.)`,
7159
7148
  extractionGuidance: `After the user answers, extract:
7160
7149
  - vision: the core product purpose (required)
7161
7150
  - audience: who it's for (optional)
7162
7151
  - techStack: technologies mentioned (optional, array)
7163
- - keyTerms: domain terms (optional, array)
7152
+ - keyTerms: domain terms \u2014 Q2 answer (the word/phrase that trips people up) goes here (optional, array)
7164
7153
  - keyDecisions: any decisions stated (optional, array)
7165
7154
  - tensions: any pain points stated (optional, array)
7166
7155
 
@@ -7174,19 +7163,22 @@ Map to batch-capture entries:
7174
7163
  captureInstructions: `Call batch-capture with the extracted entries. Omit \`autoCommit\` to follow workspace governance automatically, or pass \`autoCommit: false\` if the user wants a review-first pass. 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 those individually.`,
7175
7164
  qualityNote: (
7176
7165
  // FEAT-149: Retrieval-First Proof Moment.
7177
- // After capture, demonstrate retrieval instead of offering commit ceremony.
7178
- // The aha moment is the AI using the user's knowledge, not the user entering it.
7166
+ // The aha is friction elimination ("I'll never have to explain this again"),
7167
+ // not recognition ("it remembered me"). Simulate a FUTURE session, not a recap.
7179
7168
  `After capturing, silently call graph action=suggest on the strategy or architecture entry to build connections.
7180
7169
 
7181
7170
  **Then demonstrate the proof moment \u2014 this is the most important step:**
7182
7171
  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.
7183
7172
 
7184
- Present the results conversationally:
7185
- "Here's what I already know about your product that I didn't know 5 minutes ago \u2014 if you asked me to **<task>**, I'd bring this context:"
7173
+ Present with the "tomorrow" framing \u2014 simulate a future session, not a recap:
7174
+ "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:
7175
+ - [vision applied to that task \u2014 one sentence]
7176
+ - [ICP applied \u2014 who this feature is for in their context]
7177
+ - [any key term that matters for this task]
7186
7178
 
7187
- Then list the key entries that context returned: their product vision, audience, tech decisions, glossary terms. Keep it concrete \u2014 entry names and one-line summaries, not abstract descriptions.
7179
+ That's context you won't have to explain again."
7188
7180
 
7189
- End with: "**Try it \u2014 ask me to help with something on your product.** I'll use everything I just learned."`
7181
+ 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."`
7190
7182
  ),
7191
7183
  scanOffer: `After the proof moment, offer the codebase scan as a way to deepen the knowledge:
7192
7184
  "Want me to learn even more? I can scan your project files (README, package.json, source structure) and pick up technical decisions, conventions, and architecture that I missed. Takes about 2 minutes."`
@@ -7221,6 +7213,9 @@ async function tryMarkOriented(agentSessionId) {
7221
7213
  var startSchema = z15.object({
7222
7214
  preset: z15.string().optional().describe(
7223
7215
  "Collection preset ID to seed (e.g. 'software-product', 'content-business', 'agency', 'saas-api', 'general'). Only used for fresh workspaces. If omitted on a fresh workspace, returns the preset menu."
7216
+ ),
7217
+ task: z15.string().optional().describe(
7218
+ "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."
7224
7219
  )
7225
7220
  });
7226
7221
  function registerStartTools(server) {
@@ -7232,7 +7227,7 @@ function registerStartTools(server) {
7232
7227
  inputSchema: startSchema,
7233
7228
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
7234
7229
  },
7235
- withEnvelope(async ({ preset }) => {
7230
+ withEnvelope(async ({ preset, task }) => {
7236
7231
  const errors = [];
7237
7232
  const agentSessionId = getAgentSessionId();
7238
7233
  let wsCtx = null;
@@ -7292,7 +7287,7 @@ function registerStartTools(server) {
7292
7287
  void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
7293
7288
  });
7294
7289
  }
7295
- const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors);
7290
+ const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors, task);
7296
7291
  return {
7297
7292
  content: [{ type: "text", text: orientResult.text }],
7298
7293
  structuredContent: orientResult.structuredContent
@@ -7302,73 +7297,83 @@ function registerStartTools(server) {
7302
7297
  }
7303
7298
  function buildBlankResponse(wsCtx, sessionCtx) {
7304
7299
  const instructions = getInterviewInstructions(wsCtx.workspaceName);
7300
+ const presetList = listPresets();
7301
+ const presets = presetList.map((p) => p.id).join(", ");
7305
7302
  const text = [
7303
+ // ── User-facing welcome — present this to the user ─────────────────
7306
7304
  `# Welcome to ${wsCtx.workspaceName}`,
7307
7305
  "",
7308
- "Your workspace is fresh. Let's set it up.",
7306
+ "Your workspace is fresh. Let's bring it to life.",
7309
7307
  "",
7310
- "## How would you like to start?",
7308
+ "Tell me what you're building \u2014 paste a doc, describe it in a sentence, or say **scan my codebase**.",
7311
7309
  "",
7312
- "Present these options to the user:",
7310
+ "---",
7313
7311
  "",
7314
- "**1. Tell me about your product** _(recommended)_",
7315
- "Ask the user:",
7316
- `> ${instructions.question1}`,
7312
+ // ── Agent instructions follow these exactly, do not present them ──
7313
+ "## Agent Instructions",
7317
7314
  "",
7318
- "_A sentence or two is enough. This becomes the foundation \u2014 vision and audience \u2014 that makes everything else more relevant._",
7315
+ "### Step 1 \u2014 Open with their name (FLO-1)",
7316
+ `Ask: _"What should I call you?"_ before asking about the product. Use their name throughout.`,
7319
7317
  "",
7320
- "**2. Scan my codebase**",
7321
- "Skip the questions and jump straight to reading the project files.",
7318
+ "### Step 2 \u2014 Detect path from their first response (no menu, no numbered options)",
7322
7319
  "",
7323
- "**3. Use a preset**",
7324
- `Seed collections for a domain. Available: software-product, content-business, agency, saas-api, general.`,
7320
+ "Read signals from the user's first substantive message and branch silently:",
7325
7321
  "",
7326
- "---",
7322
+ "**document** \u2014 paste >200 words, markdown headers, or says 'here's my doc/brief/spec'",
7323
+ "\u2192 Extract vision, audience, keyTerms, keyDecisions, tensions from the document.",
7324
+ "\u2192 Present a numbered confirmation list before capturing. Capture only confirmed entries.",
7325
+ "\u2192 Fallback: if sparse or unclear, switch to interview path.",
7327
7326
  "",
7328
- "## Path 1: Interview first (default if user just starts talking)",
7327
+ "**codebase** \u2014 mentions 'scan', 'codebase', README, or provides a file path",
7328
+ `\u2192 First ask: _"Tell me one thing about this product that's not obvious from the code \u2014 what's it actually for?"_`,
7329
+ "\u2192 Then read: README.md, package.json / pyproject.toml / Cargo.toml, top-level source dirs.",
7330
+ "\u2192 Infer 5\u20138 entries. Present numbered list for confirmation. Capture only confirmed entries.",
7331
+ "\u2192 Fallback: if no README or empty project, immediately switch to interview path.",
7329
7332
  "",
7330
- instructions.systemPrompt,
7333
+ `**preset** \u2014 mentions a domain keyword (SaaS, agency, content business, API)`,
7334
+ `\u2192 Confirm which preset fits. Available: ${presets}.`,
7335
+ "\u2192 Call `start` with `preset: '<chosen-preset>'`.",
7336
+ "\u2192 Fallback: if no preset matches or seeding fails, switch to interview path.",
7331
7337
  "",
7332
- "After the user answers Q1, extract:",
7338
+ "**interview** (default \u2014 1\u20135 sentence description or anything else)",
7339
+ `\u2192 ${instructions.systemPrompt}`,
7340
+ `\u2192 Q1: ${instructions.question1}`,
7341
+ `\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.)`,
7333
7342
  instructions.extractionGuidance,
7343
+ `\u2192 ${instructions.captureInstructions}`,
7344
+ "\u2192 Omit `autoCommit` \u2014 workspace governance applies automatically.",
7334
7345
  "",
7335
- instructions.captureInstructions,
7336
- "Omit `autoCommit` \u2014 capture tools auto-commit in Open mode and create drafts in consensus/role mode.",
7346
+ "### Step 3 \u2014 Proof moment (most important \u2014 run after every path)",
7337
7347
  "",
7338
7348
  instructions.qualityNote,
7339
7349
  "",
7340
- instructions.scanOffer,
7350
+ "### Step 4 \u2014 Deepen (offer after proof moment)",
7341
7351
  "",
7352
+ instructions.scanOffer,
7342
7353
  "If they say yes, scan README.md, package.json/pyproject.toml/Cargo.toml, and top-level source dirs.",
7343
- "Infer entries WITH the product context you now have. Present as a numbered list for confirmation.",
7344
- "Do not capture anything until the user confirms what to keep. Then capture only the confirmed entries and let normal workspace governance apply.",
7345
- "",
7346
- "## Path 2: Scan first",
7347
- "",
7348
- "Read these files:",
7349
- "1. README.md (or equivalent top-level docs)",
7350
- "2. package.json / pyproject.toml / Cargo.toml (project manifest)",
7351
- "3. Top-level source directories and their purpose",
7352
- "",
7353
- "Infer 5\u20138 product knowledge entries (product name, tech decisions, features, conventions).",
7354
- "Present as a numbered list. Ask which to keep. Only after confirmation, capture the confirmed entries and let normal workspace governance apply.",
7355
- "",
7356
- "**If the codebase is sparse** (no README, empty project): fall back to Path 1 (ask the user about their product).",
7357
- "",
7358
- "_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._",
7359
- "",
7360
- "**After scan capture, run the proof moment (same as Path 1):**",
7361
- instructions.qualityNote,
7362
- "",
7363
- "## Path 3: Preset",
7364
- "",
7365
- "If the user picks a preset, call `start` again with `preset: '<chosen-preset>'`."
7354
+ "Infer entries with the product context you now have. Present as a numbered list for confirmation.",
7355
+ "Capture only confirmed entries."
7366
7356
  ].join("\n");
7367
7357
  return {
7368
7358
  text,
7369
7359
  structuredContent: success(
7370
- `Workspace is blank. Choose: tell me about your product, scan codebase, or use a preset.`,
7371
- { stage: "blank", ...sessionCtx },
7360
+ "Workspace is blank. Ready for activation \u2014 interview, document, codebase scan, or preset.",
7361
+ {
7362
+ stage: "blank",
7363
+ interviewSchema: {
7364
+ fields: ["vision", "audience", "techStack", "keyTerms", "keyDecisions", "tensions"],
7365
+ mapping: {
7366
+ vision: "strategy",
7367
+ audience: "audiences",
7368
+ techStack: "architecture + top 3 terms \u2192 glossary",
7369
+ keyTerms: "glossary",
7370
+ keyDecisions: "decisions",
7371
+ tensions: "tensions"
7372
+ }
7373
+ },
7374
+ availablePresets: presetList.map((p) => ({ id: p.id, name: p.name })),
7375
+ ...sessionCtx
7376
+ },
7372
7377
  [
7373
7378
  { tool: "capture", description: "Capture product knowledge", parameters: {} },
7374
7379
  { tool: "start", description: "Seed with a preset", parameters: { preset: "software-product" } }
@@ -7524,7 +7529,7 @@ function pickNextAction(gaps, openTensions, priorSessions) {
7524
7529
  }
7525
7530
  return null;
7526
7531
  }
7527
- async function buildOrientResponse(wsCtx, agentSessionId, errors) {
7532
+ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7528
7533
  const wsFullCtx = await getWorkspaceContext();
7529
7534
  const { ageDays, isNeglected } = computeWorkspaceAge(wsFullCtx.createdAt);
7530
7535
  let priorSessions = [];
@@ -7583,42 +7588,6 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
7583
7588
  lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)? Just ask \u2014 I'll set it up._");
7584
7589
  lines.push("");
7585
7590
  } else if (isHighReadiness) {
7586
- let wsPrinciples = [];
7587
- let wsStandards = [];
7588
- let wsBusinessRules = [];
7589
- try {
7590
- const govSlugs = ["principles", "standards", "business-rules"];
7591
- const govLabels = {
7592
- principles: "Principles",
7593
- standards: "Standards",
7594
- "business-rules": "Business Rules"
7595
- };
7596
- const govEntries = [];
7597
- for (const slug of govSlugs) {
7598
- const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
7599
- const active = (entries ?? []).filter((e) => e.status === "active");
7600
- if (active.length > 0) {
7601
- govEntries.push({ slug, entries: active });
7602
- if (slug === "principles") wsPrinciples = active;
7603
- if (slug === "standards") wsStandards = active;
7604
- if (slug === "business-rules") wsBusinessRules = active;
7605
- }
7606
- }
7607
- if (govEntries.length > 0) {
7608
- lines.push("");
7609
- lines.push("## Workspace Governance \u2014 constraints");
7610
- lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it._");
7611
- lines.push("");
7612
- for (const g of govEntries) {
7613
- lines.push(`**${govLabels[g.slug]}:**`);
7614
- for (const e of g.entries.slice(0, 8)) {
7615
- lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
7616
- }
7617
- lines.push("");
7618
- }
7619
- }
7620
- } catch {
7621
- }
7622
7591
  let activeBets = [];
7623
7592
  try {
7624
7593
  const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
@@ -7635,6 +7604,19 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
7635
7604
  }
7636
7605
  } catch {
7637
7606
  }
7607
+ let wsPrinciples = [];
7608
+ let wsStandards = [];
7609
+ let wsBusinessRules = [];
7610
+ try {
7611
+ for (const slug of ["principles", "standards", "business-rules"]) {
7612
+ const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
7613
+ const active = (entries ?? []).filter((e) => e.status === "active");
7614
+ if (slug === "principles") wsPrinciples = active;
7615
+ if (slug === "standards") wsStandards = active;
7616
+ if (slug === "business-rules") wsBusinessRules = active;
7617
+ }
7618
+ } catch {
7619
+ }
7638
7620
  const mapGovEntry = (e) => ({
7639
7621
  entryId: e.entryId,
7640
7622
  name: e.name,
@@ -7644,7 +7626,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
7644
7626
  principles: wsPrinciples.map(mapGovEntry),
7645
7627
  standards: wsStandards.map(mapGovEntry),
7646
7628
  businessRules: wsBusinessRules.map(mapGovEntry)
7647
- }));
7629
+ }, task));
7648
7630
  const plannedWork = await queryPlannedWork();
7649
7631
  if (hasPlannedWork(plannedWork)) {
7650
7632
  lines.push("");
@@ -10241,29 +10223,6 @@ function registerHealthTools(server) {
10241
10223
  orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
10242
10224
  lines.push("");
10243
10225
  }
10244
- const hasPrinciples = orientEntries.principles?.length > 0;
10245
- const hasStandards = orientEntries.standards?.length > 0;
10246
- const hasBusinessRules = orientEntries.businessRules.length > 0;
10247
- if (hasPrinciples || hasStandards || hasBusinessRules) {
10248
- lines.push("## Workspace Governance \u2014 constraints");
10249
- lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it. Do not design around them without explicit user confirmation._");
10250
- lines.push("");
10251
- if (hasPrinciples) {
10252
- lines.push("**Principles** \u2014 beliefs that guide decisions:");
10253
- orientEntries.principles.forEach((e) => lines.push(fmt(e)));
10254
- lines.push("");
10255
- }
10256
- if (hasStandards) {
10257
- lines.push("**Standards** \u2014 conventions for how work is done:");
10258
- orientEntries.standards.forEach((e) => lines.push(fmt(e)));
10259
- lines.push("");
10260
- }
10261
- if (hasBusinessRules) {
10262
- lines.push("**Business Rules** \u2014 system constraints:");
10263
- orientEntries.businessRules.forEach((e) => lines.push(fmt(e)));
10264
- lines.push("");
10265
- }
10266
- }
10267
10226
  if (orientEntries.architectureNotes.length > 0) {
10268
10227
  lines.push("## Architecture notes");
10269
10228
  orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
@@ -11745,4 +11704,4 @@ export {
11745
11704
  SERVER_VERSION,
11746
11705
  createProductBrainServer
11747
11706
  };
11748
- //# sourceMappingURL=chunk-GKMXGRJW.js.map
11707
+ //# sourceMappingURL=chunk-VBKAAFR6.js.map