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

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.
@@ -31,6 +31,7 @@ import {
31
31
  registerSmartCaptureTools,
32
32
  requireActiveSession,
33
33
  requireWriteAccess,
34
+ resolveCollection,
34
35
  runWithToolContext,
35
36
  setSessionOriented,
36
37
  startAgentSession,
@@ -42,7 +43,7 @@ import {
42
43
  unknownAction,
43
44
  validationResult,
44
45
  withEnvelope
45
- } from "./chunk-UWILSX73.js";
46
+ } from "./chunk-TH5AUVVM.js";
46
47
  import {
47
48
  trackKnowledgeGap,
48
49
  trackQualityCheck,
@@ -237,7 +238,7 @@ ${formatted}` }],
237
238
  },
238
239
  withEnvelope(async ({ entryId }) => {
239
240
  requireWriteAccess();
240
- const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
241
+ const { runContradictionCheck } = await import("./smart-capture-Q64ZXK65.js");
241
242
  const entry = await mcpQuery("chain.getEntry", { entryId });
242
243
  if (!entry) {
243
244
  return notFoundResult(entryId, `Entry '${entryId}' not found. Try search to find the right ID.`);
@@ -364,14 +365,15 @@ function sanitizeEntryData(data) {
364
365
  );
365
366
  return Object.keys(filtered).length > 0 ? filtered : void 0;
366
367
  }
367
- var ENTRIES_ACTIONS = ["list", "get", "batch", "search"];
368
+ var ENTRIES_ACTIONS = ["list", "get", "batch", "search", "move"];
368
369
  var entriesSchema = z2.object({
369
370
  action: z2.enum(ENTRIES_ACTIONS).describe(
370
- "'list': browse entries with filters. 'get': fetch one entry by ID. 'batch': fetch multiple entries. 'search': full-text search."
371
+ "'list': browse entries with filters. 'get': fetch one entry by ID. 'batch': fetch multiple entries. 'search': full-text search. 'move': move entry to a different collection."
371
372
  ),
372
- entryId: z2.string().optional().describe("Entry ID for get action, e.g. 'T-SUPPLIER', 'BR-001'"),
373
+ entryId: z2.string().optional().describe("Entry ID for get/move action, e.g. 'DEC-42', 'BR-001'"),
373
374
  entryIds: z2.array(z2.string()).min(1).max(20).optional().describe("Entry IDs for batch action, e.g. ['TYPE-strategy', 'STR-jljeg7']"),
374
375
  collection: z2.string().optional().describe("Collection slug for list/search, e.g. 'glossary', 'tracking-events', 'business-rules'"),
376
+ toCollection: z2.string().optional().describe("Target collection slug for move action, e.g. 'decisions', 'architecture'"),
375
377
  status: z2.string().optional().describe("Filter: draft | active | deprecated | archived"),
376
378
  tag: z2.string().optional().describe("Filter by internal tag for list"),
377
379
  label: z2.string().optional().describe("Filter by label slug for list \u2014 matches entries across all collections"),
@@ -426,14 +428,14 @@ function registerEntriesTools(server) {
426
428
  "entries",
427
429
  {
428
430
  title: "Entries",
429
- description: 'Read entries from the Chain. One tool for all entry reading.\n\n- **list**: Browse entries with optional filters (collection, status, tag, label). Use collections action=list first to discover slugs.\n- **get**: Fetch a single entry by ID \u2014 full record with data, labels, relations, history.\n- **batch**: Fetch multiple entries (max 20) in one call. Same shape as get per entry.\n- **search**: Full-text search across entries. Scope by collection or filter by status.\n\nUse `entries action=get entryId="..."` to fetch one. Use `entries action=search query="..."` to discover.',
431
+ description: 'Read entries from the Chain. One tool for all entry reading.\n\n- **list**: Browse entries with optional filters (collection, status, tag, label). Use collections action=list first to discover slugs.\n- **get**: Fetch a single entry by ID \u2014 full record with data, labels, relations, history.\n- **batch**: Fetch multiple entries (max 20) in one call. Same shape as get per entry.\n- **search**: Full-text search across entries. Scope by collection or filter by status.\n- **move**: Move an entry to a different collection. Use when classifier misrouted or user wants to reclassify.\n\nUse `entries action=get entryId="..."` to fetch one. Use `entries action=search query="..."` to discover.',
430
432
  inputSchema: entriesSchema,
431
- annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
433
+ annotations: { idempotentHint: false, openWorldHint: false }
432
434
  },
433
435
  withEnvelope(async (args) => {
434
436
  const parsed = parseOrFail(entriesSchema, args);
435
437
  if (!parsed.ok) return parsed.result;
436
- const { action, entryId, entryIds, collection, status, tag, label, query } = parsed.data;
438
+ const { action, entryId, entryIds, collection, toCollection, status, tag, label, query } = parsed.data;
437
439
  return runWithToolContext({ tool: "entries", action }, async () => {
438
440
  if (action === "get") {
439
441
  if (!entryId) {
@@ -456,6 +458,11 @@ function registerEntriesTools(server) {
456
458
  if (action === "list") {
457
459
  return handleList(collection, status, tag, label);
458
460
  }
461
+ if (action === "move") {
462
+ if (!entryId) return validationResult("entryId is required when action is 'move'.");
463
+ if (!toCollection) return validationResult("toCollection is required when action is 'move'.");
464
+ return handleMove(entryId, toCollection);
465
+ }
459
466
  return unknownAction(action, ENTRIES_ACTIONS);
460
467
  });
461
468
  })
@@ -657,6 +664,41 @@ ${formatted}` }],
657
664
  structuredContent: success(`Found ${total} entries${scope}.`, { entries: structuredEntries, total })
658
665
  };
659
666
  }
667
+ async function handleMove(entryId, toCollection) {
668
+ try {
669
+ const result = await mcpMutation("chain.moveToCollection", { entryId, toCollectionSlug: toCollection });
670
+ const lines = [
671
+ `## Move Result`,
672
+ "",
673
+ `Moved **${result.entryId}** from \`${result.fromCollection}\` \u2192 \`${result.toCollection}\``
674
+ ];
675
+ if (result.workflowStatusReset) {
676
+ lines.push(``, `\u26A0\uFE0F Workflow status \`${result.previousWorkflowStatus}\` was reset (incompatible with target collection).`);
677
+ }
678
+ return {
679
+ content: [{ type: "text", text: lines.join("\n") }],
680
+ structuredContent: success(
681
+ `Moved ${result.entryId} from ${result.fromCollection} to ${result.toCollection}.`,
682
+ result,
683
+ [
684
+ { tool: "entries", description: "View updated entry", parameters: { action: "get", entryId } },
685
+ { tool: "quality", description: "Re-check quality", parameters: { action: "check", entryId } }
686
+ ]
687
+ )
688
+ };
689
+ } catch (error) {
690
+ const msg = error instanceof Error ? error.message : String(error);
691
+ const isNotFound = msg.includes("not found");
692
+ if (isNotFound) return notFoundResult(entryId, `Could not move entry: ${msg}`);
693
+ return failureResult(
694
+ `Could not move **${entryId}** to \`${toCollection}\`: ${msg}`,
695
+ "MOVE_FAILED",
696
+ msg,
697
+ "Check the entry ID and target collection slug, then retry.",
698
+ [{ tool: "entries", description: "Search for entry", parameters: { action: "search", query: entryId } }]
699
+ );
700
+ }
701
+ }
660
702
  async function handleSearch(server, query, collection, status) {
661
703
  const scope = collection ? ` in \`${collection}\`` : "";
662
704
  await server.sendLoggingMessage({
@@ -3053,7 +3095,7 @@ If any facilitate call fails:
3053
3095
  label: "De-risk \u2014 Rabbit Holes & No-Gos",
3054
3096
  type: "open",
3055
3097
  instruction: "What could go wrong? What are we NOT building? Walk through the solution slowly and find the risks.",
3056
- facilitatorGuidance: "**Investigate first.** Read the investigationBrief. Spawn sub-agents to search the codebase for fragile code, tight coupling, missing tests, performance-sensitive paths, and external dependencies near the affected modules. Propose risks with evidence: 'I found that X module has no tests and is tightly coupled to Y \u2014 this is a rabbit hole.' Every confirmed risk MUST be captured with its own respond call: `facilitate action=respond betEntryId='...' dimension='risks' source='user_reaction' userInput='...' capture={type:'risk', name:'Risk Name', description:'What could go wrong', theme:'implementation'}`. Each becomes a tensions entry linked to the bet via constrains. Push for mitigations \u2014 every risk needs a patch or explicit acceptance. Then declare no-gos: 'Based on what I've seen, I'd suggest these no-gos.' Every no-go MUST use capture: `facilitate action=respond betEntryId='...' dimension='boundaries' source='user_reaction' userInput='...' capture={type:'noGo', name:'No-Go Title', description:'Why we are not doing this'}`. Without the `capture` parameter on each call, the item is NOT saved \u2014 you will get a WARNING. When risks score 6+ and boundaries score 4+, present Shape Go gate: 'Is this buildable within the appetite?'",
3098
+ facilitatorGuidance: "**Investigate first.** Read the investigationBrief. Spawn sub-agents to search the codebase for fragile code, tight coupling, missing tests, performance-sensitive paths, and external dependencies near the affected modules. Propose risks with evidence: 'I found that X module has no tests and is tightly coupled to Y \u2014 this is a rabbit hole.' Every confirmed risk MUST be captured with its own respond call: `facilitate action=respond betEntryId='...' dimension='risks' source='user_reaction' userInput='...' capture={type:'risk', name:'Risk Name', description:'What could go wrong', theme:'implementation'}`. Each becomes a tensions entry linked to the bet via constrains. Push for mitigations \u2014 every risk needs a patch or explicit acceptance. Then declare no-gos: 'Based on what I've seen, I'd suggest these no-gos.' Every no-go MUST use capture: `facilitate action=respond betEntryId='...' dimension='boundaries' source='user_reaction' userInput='...' capture={type:'noGo', name:'No-Go Title', description:'Why we are not doing this'}`. Without the `capture` parameter on each call, the item is NOT saved \u2014 you will get a WARNING. **Counter-Metric Mandate (STD-19):** If the derisking references any quantitative gates or metrics (accuracy targets, performance budgets, adoption thresholds), stress-test them: 'What's the denominator? What gets excluded? What counter-metric would expose a false positive?' A metric gate that can be gamed by abstaining or cherry-picking the population is not a gate \u2014 name the compound metric that can't be gamed. When risks score 6+ and boundaries score 4+, present Shape Go gate: 'Is this buildable within the appetite?'",
3057
3099
  questions: [
3058
3100
  {
3059
3101
  id: "shape-go",
@@ -3079,7 +3121,7 @@ If any facilitate call fails:
3079
3121
  label: "Validate & Build Contract",
3080
3122
  type: "synthesis",
3081
3123
  instruction: "Check completeness. Generate the build contract. Review the full scorecard.",
3082
- facilitatorGuidance: "Call `facilitate action=score` to get the full scorecard. Present it as a table. Check: does the response include a buildContract? If captureReady is true, it should. Verify the 5 core ingredients: Problem, Appetite, Solution elements, Rabbit Holes, No-Gos. For Big Batch: also verify Architecture, narrative hook, and value-flow annotations. Present the build contract. Ask: 'Anything to adjust before we capture to the Chain?'",
3124
+ facilitatorGuidance: "Call `facilitate action=score` to get the full scorecard. Present it as a table. Check: does the response include a buildContract? If captureReady is true, it should. Verify the 5 core ingredients: Problem, Appetite, Solution elements, Rabbit Holes, No-Gos. For Big Batch: also verify Architecture, narrative hook, and value-flow annotations. **Counter-Metric Mandate (STD-19):** If the scorecard or build contract contains metrics or gates, verify each has a denominator, a coverage rate, and a counter-metric. Flag any gate that can be satisfied by abstaining or excluding the hard cases. Present the build contract. Ask: 'Anything to adjust before we capture to the Chain?'",
3083
3125
  outputSchema: {
3084
3126
  field: "validation",
3085
3127
  description: "Completeness check result and build contract",
@@ -3166,6 +3208,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
3166
3208
  ## Review scope (CRITICAL)
3167
3209
 
3168
3210
  - **Default scope is this conversation only.** Only the work discussed or produced in this conversation is in scope. Do not review or touch files, BETs, or work outside that scope unless the user explicitly expands it (e.g. "also review the auth module").
3211
+ - **Every file touched must be explicit.** Scope is not complete until you have listed every file that was touched (created, modified, or deleted) in the work under review. Use git status/diff and conversation context to enumerate them. The review then assesses holistic coherence: does this set of changes fit our system, bring us closer to our vision, and make our code and architecture cleaner?
3169
3212
  - **Infer scope first.** Use conversation context, git status/diff, and recent edits to determine which BET, feature, or files are in scope. Call orient/start and \`context action=gather\` with the inferred scope; do not ask the user to pick what to review unless it's ambiguous.
3170
3213
  - **Ask only to clarify boundaries.** If scope is ambiguous after inference, ask a single, focused question to clarify (e.g. "Should this review include only the changes we just made, or the whole feature?"). Do not ask open-ended "what should we review?" or "which work area?" unless inference failed.
3171
3214
  - **State scope explicitly in Round 01.** After orient and context gather, state in one sentence what is in scope (e.g. "Scope: changes in this conversation for BET-72" or "Scope: files X, Y and BET-72"). Confirm only if the user might reasonably expect something else.
@@ -3200,11 +3243,11 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
3200
3243
  num: "01",
3201
3244
  label: "Orient & Scope",
3202
3245
  type: "open",
3203
- instruction: "Apply the Review scope rules: infer scope from this conversation, git diff, and recent edits (default = work in this conversation only). Call orient or start, then context action=gather for the inferred BET/feature. State scope in one explicit sentence. Only ask the participant if scope is ambiguous.",
3204
- facilitatorGuidance: "Infer scope from conversation context, git status/diff, and recent edits \u2014 default is work in this conversation only. Call orient or start to load governance and active bets. Use context action=gather task='implementation review for [inferred-BET-or-feature]' to load related entries. State scope explicitly in one sentence (e.g. 'Scope: changes in this conversation for BET-72'). Present: stated scope, relevant BETs/DECs/BRs, and any stale entries orient surfaces. Ask the participant only if scope is ambiguous (e.g. 'Only the files we changed, or the whole feature?').",
3246
+ instruction: "Apply the Review scope rules: infer scope from this conversation, git diff, and recent edits (default = work in this conversation only). Enumerate every file touched (created, modified, deleted). Call orient or start, then context action=gather for the inferred BET/feature. State scope in one explicit sentence and list all files in scope. Only ask the participant if scope is ambiguous. The review will assess: holistic coherence with our system, whether this brings us closer to our vision, and whether it makes our code and architecture cleaner.",
3247
+ facilitatorGuidance: "Infer scope from conversation context, git status/diff, and recent edits \u2014 default is work in this conversation only. List every file touched (created, modified, or deleted) in the work under review; scope is not complete without this list. Call orient or start to load governance and active bets. Use context action=gather task='implementation review for [inferred-BET-or-feature]' to load related entries. State scope explicitly in one sentence (e.g. 'Scope: changes in this conversation for BET-72'). Present: stated scope, the full list of files touched, relevant BETs/DECs/BRs, and any stale entries orient surfaces. Frame the review: we will assess holistic coherence with our system, whether this brings us closer to our vision, and whether it makes our code and architecture cleaner. Ask the participant only if scope is ambiguous (e.g. 'Only the files we changed, or the whole feature?').",
3205
3248
  outputSchema: {
3206
3249
  field: "scope",
3207
- description: "Explicit scope statement plus BET/feature IDs and related Chain context",
3250
+ description: "Explicit scope statement, every file touched, BET/feature IDs and related Chain context",
3208
3251
  format: "structured"
3209
3252
  },
3210
3253
  maxDurationHint: "3 min"
@@ -3229,7 +3272,7 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
3229
3272
  label: "Code & Test Honesty",
3230
3273
  type: "open",
3231
3274
  instruction: "Spawn sub-agents to review implementation and tests for the stated scope only. Did we meet high standards? Are tests validating real behavior or did we edit them just to pass?",
3232
- facilitatorGuidance: "Restrict all review to the scope stated in Round 01 (by default, work in this conversation only). Spawn 1\u20132 sub-agents in parallel: (1) explore agent \u2014 code review, architecture boundaries, type-safety for scoped files only. (2) generalPurpose agent \u2014 test review for scoped code: are tests asserting real behavior or were they edited to pass? Pass the scope (files/BET) explicitly to sub-agents so they do not touch other work. Use Context7 (query-docs) for relevant testing and framework best practices if needed. Classify any test failures: staleness, regression, or flaky. Synthesize: implementation grade, test honesty verdict, refactoring suggestions (within scope only).",
3275
+ facilitatorGuidance: "Restrict all review to the scope stated in Round 01 (by default, work in this conversation only). Spawn 1\u20132 sub-agents in parallel: (1) explore agent \u2014 code review, architecture boundaries, type-safety for scoped files only. (2) generalPurpose agent \u2014 test review for scoped code: are tests asserting real behavior or were they edited to pass? Pass the scope (files/BET) explicitly to sub-agents so they do not touch other work. Use Context7 (query-docs) for relevant testing and framework best practices if needed. Classify any test failures: staleness, regression, or flaky. **Counter-Metric Mandate (STD-19):** When reporting test coverage, accuracy, or any quantitative result, include the denominator, what was excluded, and at least one counter-metric. Never report accuracy without recall. Never report pass rate without coverage. Synthesize: implementation grade, test honesty verdict, refactoring suggestions (within scope only).",
3233
3276
  outputSchema: {
3234
3277
  field: "codeAndTests",
3235
3278
  description: "Implementation grade, test honesty, refactor suggestions",
@@ -3256,8 +3299,8 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
3256
3299
  num: "05",
3257
3300
  label: "Synthesis & Sign-Off",
3258
3301
  type: "commit",
3259
- instruction: "Summarize the review. Refactoring needed? Ship or conditional? End with BET/chain IDs.",
3260
- facilitatorGuidance: "Present the full synthesis: standards pass/fail, code grade, test honesty, chain validation. Recommend: ship, ship with conditions, or do not ship. CRITICAL: The output MUST end with one sentence: 'BET/feature IDs reviewed: [IDs]. [One-line summary].' Example: 'BET-xxx, STD-xxx reviewed. Conditional ship \u2014 fix [specific issues] and add [missing tests].'",
3302
+ instruction: "Summarize the review. Answer: Does this bring us closer to our vision and make our code and architecture cleaner? Refactoring needed? Ship or conditional? End with BET/chain IDs.",
3303
+ facilitatorGuidance: "Present the full synthesis: standards pass/fail, code grade, test honesty, chain validation. Explicitly answer: Does this bring us closer to our vision and make our code and architecture cleaner? **Counter-Metric Mandate (STD-19):** For EVERY quantitative result you present, you MUST include: (1) the denominator \u2014 what is the total population measured, (2) the coverage/abstention rate \u2014 what was excluded, (3) at least one counter-metric that tells the opposite story (recall vs precision, error rate vs success rate), (4) the 'student exam' reframe \u2014 score on the full exam, not just questions answered. **Red-team your own synthesis:** Before presenting, ask yourself: 'What's the worst honest interpretation of these numbers? What metric looks good but hides a real problem? What would a skeptic challenge?' Present that alongside the headline. Recommend: ship, ship with conditions, or do not ship. CRITICAL: The output MUST end with one sentence: 'BET/feature IDs reviewed: [IDs]. [One-line summary].' Example: 'BET-xxx, STD-xxx reviewed. Conditional ship \u2014 fix [specific issues] and add [missing tests].'",
3261
3304
  outputSchema: {
3262
3305
  field: "synthesis",
3263
3306
  description: "Full review synthesis with BET/chain IDs",
@@ -3852,6 +3895,7 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3852
3895
  runId
3853
3896
  );
3854
3897
  }
3898
+ const resolvedSummaryCapture = summaryCapture;
3855
3899
  const existingRun = await mcpQuery(
3856
3900
  "chainwork.getLatestWorkflowRun",
3857
3901
  {
@@ -3865,6 +3909,15 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3865
3909
  );
3866
3910
  }
3867
3911
  try {
3912
+ let resolvedCollectionSlug;
3913
+ if (resolvedSummaryCapture.routing.mode === "classifier") {
3914
+ const classifyName = summaryName ?? wf.name;
3915
+ const classifyDesc = summaryDescription ?? outputPreview;
3916
+ const resolved = await resolveCollection({ name: classifyName, description: classifyDesc });
3917
+ if (resolved && resolved.tier !== "low") {
3918
+ resolvedCollectionSlug = resolved.collection;
3919
+ }
3920
+ }
3868
3921
  const result = await mcpMutation("chainwork.finalizeWorkflowRun", {
3869
3922
  agentSessionId,
3870
3923
  workflowId: wf.id,
@@ -3882,22 +3935,23 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3882
3935
  finalRoundId: roundId,
3883
3936
  finalOutput: normalizedOutput,
3884
3937
  summaryCapture: {
3885
- routing: summaryCapture.routing.mode === "fixed" ? {
3938
+ routing: resolvedSummaryCapture.routing.mode === "fixed" ? {
3886
3939
  mode: "fixed",
3887
- collection: summaryCapture.routing.collection
3940
+ collection: resolvedSummaryCapture.routing.collection
3888
3941
  } : {
3889
3942
  mode: "classifier"
3890
3943
  },
3891
- nameTemplate: summaryCapture.nameTemplate,
3892
- descriptionField: summaryCapture.descriptionField,
3893
- descriptionSource: summaryCapture.descriptionSource
3944
+ nameTemplate: resolvedSummaryCapture.nameTemplate,
3945
+ descriptionField: resolvedSummaryCapture.descriptionField,
3946
+ descriptionSource: resolvedSummaryCapture.descriptionSource
3894
3947
  },
3895
3948
  summaryDescription,
3896
- summaryName
3949
+ summaryName,
3950
+ resolvedCollectionSlug
3897
3951
  });
3898
3952
  lines.push(
3899
3953
  result.summary.created ? `**Draft Created**: \`${result.summary.entryId}\`` : `**Draft Already Exists**: \`${result.summary.entryId}\``,
3900
- `Collection: \`${result.summary.collection ?? (summaryCapture.routing.mode === "fixed" ? summaryCapture.routing.collection : "classifier-routed")}\``,
3954
+ `Collection: \`${result.summary.collection ?? (resolvedSummaryCapture.routing.mode === "fixed" ? resolvedSummaryCapture.routing.collection : "classifier-routed")}\``,
3901
3955
  `Name: ${result.summary.name}`,
3902
3956
  "",
3903
3957
  `The summary is captured as a draft. Use \`commit-entry entryId="${result.summary.entryId}"\` to promote it to the Chain.`,
@@ -3911,7 +3965,7 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3911
3965
  entryId: result.summary.entryId,
3912
3966
  workflowId,
3913
3967
  roundId,
3914
- collection: result.summary.collection ?? (summaryCapture.routing.mode === "fixed" ? summaryCapture.routing.collection : null),
3968
+ collection: result.summary.collection ?? (resolvedSummaryCapture.routing.mode === "fixed" ? resolvedSummaryCapture.routing.collection : null),
3915
3969
  isFinal: true,
3916
3970
  runId: result.runId,
3917
3971
  created: result.summary.created
@@ -3937,8 +3991,8 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3937
3991
  "",
3938
3992
  `The workflow output is preserved in this conversation. `,
3939
3993
  `You can manually create the entry later using \`capture\` with:`,
3940
- `- Collection: \`${summaryCapture.routing.mode === "fixed" ? summaryCapture.routing.collection : "classifier-routed"}\``,
3941
- `- Name: ${summaryName ?? summaryCapture.nameTemplate}`,
3994
+ `- Collection: \`${resolvedSummaryCapture.routing.mode === "fixed" ? resolvedSummaryCapture.routing.collection : "classifier-routed"}\``,
3995
+ `- Name: ${summaryName ?? resolvedSummaryCapture.nameTemplate}`,
3942
3996
  `- Description: ${summaryDescription ?? outputPreview}`
3943
3997
  );
3944
3998
  return {
@@ -5489,11 +5543,12 @@ async function processCaptures(opts) {
5489
5543
  const entriesCreated = [];
5490
5544
  let relationsCreated = 0;
5491
5545
  const capAgentId = getAgentSessionId();
5492
- const collectionMap = {
5493
- element: { slug: "features", dataField: "description", relationType: "part_of" },
5494
- risk: { slug: "tensions", dataField: "description", relationType: "constrains" },
5495
- decision: { slug: "decisions", dataField: "rationale", relationType: "informs" }
5546
+ const CAPTURE_DEFAULTS = {
5547
+ features: { dataField: "description", relationType: "part_of" },
5548
+ tensions: { dataField: "description", relationType: "constrains" },
5549
+ decisions: { dataField: "rationale", relationType: "informs" }
5496
5550
  };
5551
+ const FALLBACK_DEFAULTS = { dataField: "description", relationType: "related_to" };
5497
5552
  let runningBetData = { ...betData };
5498
5553
  for (const item of captureItems) {
5499
5554
  if (item.type === "noGo") {
@@ -5517,17 +5572,28 @@ async function processCaptures(opts) {
5517
5572
  }
5518
5573
  continue;
5519
5574
  }
5520
- const mapping = collectionMap[item.type];
5521
- if (!mapping) continue;
5575
+ const resolved = await resolveCollection({
5576
+ name: item.name,
5577
+ description: item.description,
5578
+ typeHint: item.type
5579
+ });
5580
+ if (!resolved) {
5581
+ captureErrors.push({
5582
+ operation: "classify",
5583
+ detail: `Could not resolve collection for ${item.type} "${item.name}" \u2014 skipped.`
5584
+ });
5585
+ continue;
5586
+ }
5587
+ const { dataField, relationType } = CAPTURE_DEFAULTS[resolved.collection] ?? FALLBACK_DEFAULTS;
5522
5588
  let capturedEntryId = null;
5523
5589
  try {
5524
5590
  const result = await mcpMutation(
5525
5591
  "chain.createEntry",
5526
5592
  {
5527
- collectionSlug: mapping.slug,
5593
+ collectionSlug: resolved.collection,
5528
5594
  name: item.name,
5529
5595
  status: "draft",
5530
- data: { [mapping.dataField]: item.description },
5596
+ data: { [dataField]: item.description },
5531
5597
  createdBy: capAgentId ? `agent:${capAgentId}` : "facilitate",
5532
5598
  sessionId: capAgentId ?? void 0
5533
5599
  }
@@ -5539,9 +5605,9 @@ async function processCaptures(opts) {
5539
5605
  if (msg.includes("Duplicate entry") || msg.includes("already exists")) {
5540
5606
  const fallback = await findAndLinkExisting(
5541
5607
  item.name,
5542
- mapping.slug,
5608
+ resolved.collection,
5543
5609
  betEntryId,
5544
- mapping.relationType,
5610
+ relationType,
5545
5611
  capAgentId
5546
5612
  );
5547
5613
  if (fallback) {
@@ -5550,7 +5616,7 @@ async function processCaptures(opts) {
5550
5616
  if (fallback.linked) relationsCreated++;
5551
5617
  captureErrors.push({
5552
5618
  operation: "info",
5553
- detail: `Linked existing ${mapping.slug} entry \`${fallback.entryId}\` instead of creating duplicate.`
5619
+ detail: `Linked existing ${resolved.collection} entry \`${fallback.entryId}\` instead of creating duplicate.`
5554
5620
  });
5555
5621
  } else {
5556
5622
  captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
@@ -5564,7 +5630,7 @@ async function processCaptures(opts) {
5564
5630
  await mcpMutation("chain.createEntryRelation", {
5565
5631
  fromEntryId: capturedEntryId,
5566
5632
  toEntryId: betEntryId,
5567
- type: mapping.relationType,
5633
+ type: relationType,
5568
5634
  sessionId: capAgentId ?? void 0
5569
5635
  });
5570
5636
  relationsCreated++;
@@ -5573,7 +5639,7 @@ async function processCaptures(opts) {
5573
5639
  if (!msg.includes("already exists") && !msg.includes("Duplicate")) {
5574
5640
  captureErrors.push({
5575
5641
  operation: "relation",
5576
- detail: `${mapping.relationType} from ${capturedEntryId} to ${betEntryId}: ${msg}`
5642
+ detail: `${relationType} from ${capturedEntryId} to ${betEntryId}: ${msg}`
5577
5643
  });
5578
5644
  }
5579
5645
  }
@@ -6272,7 +6338,7 @@ async function handleCommitConstellation(args) {
6272
6338
  }
6273
6339
  let contradictionWarnings = [];
6274
6340
  try {
6275
- const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
6341
+ const { runContradictionCheck } = await import("./smart-capture-Q64ZXK65.js");
6276
6342
  const descField = betData.problem ?? betData.description ?? "";
6277
6343
  contradictionWarnings = await runContradictionCheck(
6278
6344
  betEntry.name ?? betId,
@@ -6974,13 +7040,14 @@ function buildAlignmentCheckLines(result) {
6974
7040
  lines.push("");
6975
7041
  return lines;
6976
7042
  }
6977
- var CORE_PROTOCOL = [
7043
+ var CORE_PROTOCOL_BASE = [
6978
7044
  '**Search before proposing.** Before suggesting new features, architecture, or changes, search the Chain: `entries action=search query="<relevant terms>"`. Build on what exists.',
6979
7045
  "**Reference by ID.** When discussing a topic that has Chain entries, cite them by entry ID (e.g. `DEC-50`, `PRI-3`). This keeps conversations grounded in shared knowledge.",
6980
7046
  "**Check scope.** If the proposed work doesn't fall under an active bet, stop and say so. Do not design implementation for out-of-scope work without explicit user go-ahead.",
6981
- `**Capture continuously.** When a decision, tension, insight, or new term surfaces during work, capture it as a draft immediately. Don't defer to "later."`,
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."
7047
+ `**Capture continuously.** When a decision, tension, insight, or new term surfaces during work, capture it as a draft immediately. Don't defer to "later."`
6983
7048
  ];
7049
+ var RULE5_WITH_GOVERNANCE = "**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.";
7050
+ var RULE5_COMPACT = '**Validate against governance.** Before proposing or building anything, call `orient task="<your task>"` to load relevant governance. If your proposal conflicts with a principle, standard, or business rule \u2014 stop, name the conflict, and get explicit user confirmation.';
6984
7051
  var MAX_ENTRIES_NO_TASK = 3;
6985
7052
  var MAX_ENTRIES_WITH_TASK = 5;
6986
7053
  function extractKeywords(text) {
@@ -6997,16 +7064,18 @@ function formatGovernanceEntry(entry) {
6997
7064
  }
6998
7065
  function buildOperatingProtocol(governanceOrStandards, task) {
6999
7066
  const governance = Array.isArray(governanceOrStandards) ? { standards: governanceOrStandards } : governanceOrStandards ?? {};
7067
+ const { principles = [], standards = [], businessRules = [] } = governance;
7068
+ const hasGovernance = principles.length > 0 || standards.length > 0 || businessRules.length > 0;
7069
+ const rule5 = hasGovernance ? RULE5_WITH_GOVERNANCE : RULE5_COMPACT;
7070
+ const protocol = [...CORE_PROTOCOL_BASE, rule5];
7000
7071
  const lines = [
7001
7072
  "## Operating Protocol",
7002
7073
  "_How to work in this workspace. Follow these before and during every task._",
7003
7074
  ""
7004
7075
  ];
7005
- for (let i = 0; i < CORE_PROTOCOL.length; i++) {
7006
- lines.push(`${i + 1}. ${CORE_PROTOCOL[i]}`);
7076
+ for (let i = 0; i < protocol.length; i++) {
7077
+ lines.push(`${i + 1}. ${protocol[i]}`);
7007
7078
  }
7008
- const { principles = [], standards = [], businessRules = [] } = governance;
7009
- const hasGovernance = principles.length > 0 || standards.length > 0 || businessRules.length > 0;
7010
7079
  if (hasGovernance) {
7011
7080
  lines.push("");
7012
7081
  lines.push("### Workspace governance directives");
@@ -7592,98 +7661,92 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7592
7661
  try {
7593
7662
  const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
7594
7663
  activeBets = (betEntries ?? []).filter((e) => e.status === "active" && e.data?.horizon === "now").slice(0, 8);
7664
+ } catch {
7665
+ }
7666
+ if (task) {
7595
7667
  if (activeBets.length > 0) {
7596
7668
  lines.push("");
7597
7669
  lines.push("## Active bets \u2014 current scope");
7598
- lines.push("_These define what you're building now. Work outside these bets requires explicit user confirmation before designing._");
7670
+ lines.push("_Work outside these bets requires explicit user confirmation._");
7599
7671
  lines.push("");
7600
7672
  for (const e of activeBets) {
7601
7673
  lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
7602
7674
  }
7603
7675
  lines.push("");
7604
7676
  }
7605
- } catch {
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;
7677
+ let wsPrinciples = [];
7678
+ let wsStandards = [];
7679
+ let wsBusinessRules = [];
7680
+ try {
7681
+ for (const slug of ["principles", "standards", "business-rules"]) {
7682
+ const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
7683
+ const active = (entries ?? []).filter((e) => e.status === "active");
7684
+ if (slug === "principles") wsPrinciples = active;
7685
+ if (slug === "standards") wsStandards = active;
7686
+ if (slug === "business-rules") wsBusinessRules = active;
7687
+ }
7688
+ } catch {
7689
+ }
7690
+ const mapGovEntry = (e) => ({
7691
+ entryId: e.entryId,
7692
+ name: e.name,
7693
+ description: typeof e.data?.description === "string" ? e.data.description : typeof e.description === "string" ? e.description : void 0
7694
+ });
7695
+ lines.push(...buildOperatingProtocol({
7696
+ principles: wsPrinciples.map(mapGovEntry),
7697
+ standards: wsStandards.map(mapGovEntry),
7698
+ businessRules: wsBusinessRules.map(mapGovEntry)
7699
+ }, task));
7700
+ const plannedWork = await queryPlannedWork();
7701
+ if (hasPlannedWork(plannedWork)) {
7702
+ lines.push("");
7703
+ lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
7704
+ } else if (recoveryBlock) {
7705
+ lines.push("");
7706
+ lines.push(...formatRecoveryBlock(recoveryBlock));
7707
+ }
7708
+ try {
7709
+ const allEntries = await mcpQuery("chain.listEntries", {});
7710
+ const committed = (allEntries ?? []).filter(
7711
+ (e) => e.status === "active" && !e.seededByPlatform
7712
+ );
7713
+ const committedCollections = new Set(committed.map((e) => e.collectionId ?? e.collection));
7714
+ if (committed.length >= 10 && committedCollections.size >= 3) {
7715
+ lines.push("");
7716
+ lines.push(`_${committed.length} entries across ${committedCollections.size} collections on the Chain._`);
7717
+ }
7718
+ } catch {
7617
7719
  }
7618
- } catch {
7619
- }
7620
- const mapGovEntry = (e) => ({
7621
- entryId: e.entryId,
7622
- name: e.name,
7623
- description: typeof e.data?.description === "string" ? e.data.description : typeof e.description === "string" ? e.description : void 0
7624
- });
7625
- lines.push(...buildOperatingProtocol({
7626
- principles: wsPrinciples.map(mapGovEntry),
7627
- standards: wsStandards.map(mapGovEntry),
7628
- businessRules: wsBusinessRules.map(mapGovEntry)
7629
- }, task));
7630
- const plannedWork = await queryPlannedWork();
7631
- if (hasPlannedWork(plannedWork)) {
7632
7720
  lines.push("");
7633
- lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
7721
+ lines.push(`Working on: **${task}**`);
7722
+ lines.push("");
7634
7723
  } else {
7635
- const briefingItems = [];
7724
+ if (activeBets.length > 0) {
7725
+ lines.push("");
7726
+ lines.push("## Active bets \u2014 current scope");
7727
+ lines.push("_Work outside these bets requires explicit user confirmation._");
7728
+ lines.push("");
7729
+ for (const e of activeBets) {
7730
+ lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
7731
+ }
7732
+ lines.push("");
7733
+ }
7734
+ lines.push(...buildOperatingProtocol());
7636
7735
  if (priorSessions.length > 0 && !recoveryBlock) {
7637
7736
  const last = priorSessions[0];
7638
- const date = new Date(last.startedAt).toISOString().split("T")[0];
7737
+ const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
7639
7738
  const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
7640
7739
  const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
7641
- briefingItems.push(`**Last session** (${date}): ${created} created, ${modified} modified`);
7642
- }
7643
- if (readiness?.gaps?.length > 0) {
7644
- briefingItems.push(`**${readiness.gaps.length} gap${readiness.gaps.length === 1 ? "" : "s"}** remaining`);
7645
- }
7646
- if (briefingItems.length > 0) {
7647
- lines.push("");
7648
- lines.push("## Briefing");
7649
- for (const item of briefingItems) {
7650
- lines.push(`- ${item}`);
7651
- }
7740
+ lines.push(`_Last session (${date}): ${created} created, ${modified} modified._`);
7652
7741
  lines.push("");
7653
7742
  }
7654
7743
  if (recoveryBlock) {
7655
7744
  lines.push("");
7656
7745
  lines.push(...formatRecoveryBlock(recoveryBlock));
7657
7746
  }
7747
+ lines.push("**What are you working on?** Tell me and I'll load relevant governance and context.");
7748
+ lines.push("");
7658
7749
  }
7659
- try {
7660
- const allEntries = await mcpQuery("chain.listEntries", {});
7661
- const committed = (allEntries ?? []).filter(
7662
- (e) => e.status === "active" && !e.seededByPlatform
7663
- );
7664
- const committedCollections = new Set(committed.map((e) => e.collectionId ?? e.collection));
7665
- if (committed.length >= 10 && committedCollections.size >= 3) {
7666
- lines.push("");
7667
- lines.push("## Your workspace is activated");
7668
- lines.push(
7669
- `**${committed.length} entries** across **${committedCollections.size} collections** \u2014 your knowledge graph is alive.`
7670
- );
7671
- lines.push("");
7672
- lines.push(
7673
- `**Try it:** Ask me anything about your product \u2014 architecture, decisions, tensions \u2014 and I'll pull context from the graph.`
7674
- );
7675
- lines.push("");
7676
- if (wsCtx.workspaceSlug) {
7677
- lines.push(`**View in Studio:** \`/w/${wsCtx.workspaceSlug}/studio\``);
7678
- lines.push("");
7679
- }
7680
- lines.push("_Tip: Ask me to suggest connections between entries to unlock deeper context._");
7681
- lines.push("");
7682
- }
7683
- } catch {
7684
- }
7685
- lines.push("What would you like to work on?");
7686
- lines.push("");
7687
7750
  }
7688
7751
  if (errors.length > 0) {
7689
7752
  lines.push("## Errors");
@@ -9409,6 +9472,7 @@ var CALL_CATEGORIES = {
9409
9472
  "chain.searchEntries": "search",
9410
9473
  "chain.createEntry": "write",
9411
9474
  "chain.updateEntry": "write",
9475
+ "chain.moveToCollection": "write",
9412
9476
  "chain.createEntryRelation": "write",
9413
9477
  "chain.applyLabel": "label",
9414
9478
  "chain.removeLabel": "label",
@@ -10042,23 +10106,27 @@ function registerHealthTools(server) {
10042
10106
  lines.push(...formatRecoveryBlock(recoveryBlock));
10043
10107
  } else if (priorSessions.length > 0) {
10044
10108
  const last = priorSessions[0];
10045
- const date = new Date(last.startedAt).toISOString().split("T")[0];
10109
+ const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
10046
10110
  const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
10047
10111
  const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
10048
10112
  lines.push(`Last session (${date}): ${created} created, ${modified} modified`);
10049
10113
  }
10050
10114
  if (orientEntries) {
10051
- const mapGovernanceEntry = (e) => ({
10052
- entryId: e.entryId,
10053
- name: e.name,
10054
- description: typeof e.preview === "string" ? e.preview : void 0
10055
- });
10056
10115
  lines.push("");
10057
- lines.push(...buildOperatingProtocol({
10058
- principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
10059
- standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
10060
- businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
10061
- }, task));
10116
+ if (task) {
10117
+ const mapGovernanceEntry = (e) => ({
10118
+ entryId: e.entryId,
10119
+ name: e.name,
10120
+ description: typeof e.preview === "string" ? e.preview : void 0
10121
+ });
10122
+ lines.push(...buildOperatingProtocol({
10123
+ principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
10124
+ standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
10125
+ businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
10126
+ }, task));
10127
+ } else {
10128
+ lines.push(...buildOperatingProtocol());
10129
+ }
10062
10130
  }
10063
10131
  if (agentSessionId) {
10064
10132
  try {
@@ -10118,195 +10186,164 @@ function registerHealthTools(server) {
10118
10186
  lines.push("_Use `collections action=create` to add it, or ask me to propose collections for your domain._");
10119
10187
  lines.push("");
10120
10188
  } else if (readiness) {
10121
- lines.push(`**Brain stage: ${orientStage}.**`);
10122
- lines.push("");
10123
- if (orientEntries?.strategicContext) {
10124
- const sc = orientEntries.strategicContext;
10125
- lines.push("## Strategic Context");
10126
- if (sc.vision) lines.push(`**Vision:** ${sc.vision}`);
10127
- if (sc.purpose) lines.push(`**Purpose:** ${sc.purpose}`);
10128
- if (sc.productAreaCount != null && sc.productAreaCount > 0) {
10129
- lines.push(`**Product areas (${sc.productAreaCount}):** ${(sc.productAreas ?? []).join(", ")}`);
10130
- }
10131
- const betLine = sc.currentBet ? `**Current bet:** ${sc.currentBet}. ${sc.activeBetCount} active bet(s).` : "No active bets.";
10132
- lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
10133
- lines.push("");
10134
- }
10135
- if (orientEntries?.continuingFrom && orientEntries.continuingFrom.length > 0) {
10136
- lines.push("## Continuing from");
10137
- lines.push("_Prior-session entries most relevant to your task._");
10138
- lines.push("");
10139
- for (const e of orientEntries.continuingFrom) {
10140
- const id = e.entryId ?? e.name;
10141
- const type = e.canonicalKey ?? "generic";
10142
- const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10143
- lines.push(`- \`${id}\` (score ${e.score}) [${type}]${coll} \u2014 ${e.name}`);
10144
- if (e.reasoning) lines.push(` _${e.reasoning}_`);
10145
- }
10146
- lines.push("");
10147
- }
10148
- if (orientEntries?.lastSessionTouched && orientEntries.lastSessionTouched.length > 0) {
10149
- lines.push("## Last session touched");
10150
- lines.push("_Entries created or modified in your most recent session._");
10151
- lines.push("");
10152
- for (const e of orientEntries.lastSessionTouched) {
10153
- const id = e.entryId ?? e.name;
10154
- const type = e.canonicalKey ?? "generic";
10155
- const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10156
- lines.push(`- \`${id}\` [${type}]${coll} \u2014 ${e.name}`);
10157
- }
10158
- lines.push("");
10159
- }
10160
- if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
10161
- const tc = orientEntries.taskContext;
10162
- lines.push("## Task Context");
10163
- lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
10189
+ const fmt = (e) => {
10190
+ const type = e.canonicalKey ?? "generic";
10191
+ const stratum = e.stratum ?? "?";
10192
+ return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
10193
+ };
10194
+ if (task) {
10195
+ lines.push(`**Brain stage: ${orientStage}.** Working on: **${task}**`);
10164
10196
  lines.push("");
10165
- for (const e of tc.context) {
10166
- const id = e.entryId ?? e.name;
10167
- const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10168
- lines.push(`- \`${id}\` (score ${e.score})${coll}${e.name !== id ? ` \u2014 ${e.name}` : ""}`);
10197
+ if (orientEntries?.strategicContext) {
10198
+ const sc = orientEntries.strategicContext;
10199
+ lines.push("## Strategic Context");
10200
+ if (sc.vision) lines.push(`**Vision:** ${sc.vision}`);
10201
+ if (sc.purpose) lines.push(`**Purpose:** ${sc.purpose}`);
10202
+ if (sc.productAreaCount != null && sc.productAreaCount > 0) {
10203
+ lines.push(`**Product areas (${sc.productAreaCount}):** ${(sc.productAreas ?? []).join(", ")}`);
10204
+ }
10205
+ const betLine = sc.currentBet ? `**Current bet:** ${sc.currentBet}. ${sc.activeBetCount} active bet(s).` : "No active bets.";
10206
+ lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
10207
+ lines.push("");
10169
10208
  }
10170
- lines.push("");
10171
- }
10172
- if (task && orientEntries) {
10173
- const result = runAlignmentCheck(
10174
- task,
10175
- orientEntries.activeBets ?? [],
10176
- orientEntries.taskContext?.context
10177
- );
10178
- lines.push(...buildAlignmentCheckLines(result));
10179
- }
10180
- if (orientEntries) {
10181
- const fmt = (e) => {
10182
- const type = e.canonicalKey ?? "generic";
10183
- const stratum = e.stratum ?? "?";
10184
- return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
10185
- };
10186
- if (orientEntries.activeBets?.length > 0) {
10187
- lines.push("## Active bets \u2014 current scope");
10188
- lines.push("_These define what you're building now. Work outside these bets requires explicit user confirmation before designing._");
10209
+ if (orientEntries?.continuingFrom && orientEntries.continuingFrom.length > 0) {
10210
+ lines.push("## Continuing from");
10211
+ lines.push("_Prior-session entries most relevant to your task._");
10189
10212
  lines.push("");
10190
- for (const e of orientEntries.activeBets) {
10191
- lines.push(fmt(e));
10192
- const tensions = e.linkedTensions;
10193
- if (tensions?.length) {
10194
- const tensionLines = tensions.map((t) => {
10195
- const meta = [t.severity, t.priority].filter(Boolean).join(", ");
10196
- return `\`${t.entryId ?? t.name}\` (${t.name}${meta ? `, ${meta}` : ""})`;
10197
- });
10198
- lines.push(` Tensions: ${tensionLines.join("; ")}`);
10199
- } else {
10200
- lines.push(` Tensions: No linked tensions`);
10201
- }
10213
+ for (const e of orientEntries.continuingFrom) {
10214
+ const id = e.entryId ?? e.name;
10215
+ const type = e.canonicalKey ?? "generic";
10216
+ const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10217
+ lines.push(`- \`${id}\` (score ${e.score}) [${type}]${coll} \u2014 ${e.name}`);
10218
+ if (e.reasoning) lines.push(` _${e.reasoning}_`);
10202
10219
  }
10203
10220
  lines.push("");
10204
10221
  }
10205
- if (orientEntries.activeGoals.length > 0) {
10206
- lines.push("## Active goals");
10207
- orientEntries.activeGoals.forEach((e) => lines.push(fmt(e)));
10222
+ if (orientEntries?.lastSessionTouched && orientEntries.lastSessionTouched.length > 0) {
10223
+ lines.push("## Last session touched");
10224
+ lines.push("_Entries created or modified in your most recent session._");
10208
10225
  lines.push("");
10209
- }
10210
- if (orientEntries.recentDecisions.length > 0) {
10211
- lines.push("## Recent decisions");
10212
- orientEntries.recentDecisions.forEach((e) => lines.push(fmt(e)));
10226
+ for (const e of orientEntries.lastSessionTouched) {
10227
+ const id = e.entryId ?? e.name;
10228
+ const type = e.canonicalKey ?? "generic";
10229
+ const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10230
+ lines.push(`- \`${id}\` [${type}]${coll} \u2014 ${e.name}`);
10231
+ }
10213
10232
  lines.push("");
10214
10233
  }
10215
- if (orientEntries.recentlySuperseded.length > 0) {
10216
- lines.push("## Recently superseded");
10217
- orientEntries.recentlySuperseded.forEach((e) => lines.push(fmt(e)));
10234
+ if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
10235
+ const tc = orientEntries.taskContext;
10236
+ lines.push("## Task Context");
10237
+ lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
10218
10238
  lines.push("");
10219
- }
10220
- if (orientEntries.staleEntries.length > 0) {
10221
- lines.push("## Needs confirmation");
10222
- lines.push(`_Domain stratum entries not confirmed in ${orientEntries.stalenessThresholdDays} days._`);
10223
- orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
10239
+ for (const e of tc.context) {
10240
+ const id = e.entryId ?? e.name;
10241
+ const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
10242
+ lines.push(`- \`${id}\` (score ${e.score})${coll}${e.name !== id ? ` \u2014 ${e.name}` : ""}`);
10243
+ }
10224
10244
  lines.push("");
10225
10245
  }
10226
- if (orientEntries.architectureNotes.length > 0) {
10227
- lines.push("## Architecture notes");
10228
- orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
10229
- lines.push("");
10246
+ if (orientEntries) {
10247
+ const result = runAlignmentCheck(
10248
+ task,
10249
+ orientEntries.activeBets ?? [],
10250
+ orientEntries.taskContext?.context
10251
+ );
10252
+ lines.push(...buildAlignmentCheckLines(result));
10253
+ }
10254
+ if (orientEntries) {
10255
+ if (orientEntries.activeBets?.length > 0) {
10256
+ lines.push("## Active bets \u2014 current scope");
10257
+ lines.push("_Work outside these bets requires explicit user confirmation._");
10258
+ lines.push("");
10259
+ for (const e of orientEntries.activeBets) {
10260
+ lines.push(fmt(e));
10261
+ const tensions = e.linkedTensions;
10262
+ if (tensions?.length) {
10263
+ const tensionLines = tensions.map((t) => {
10264
+ const meta = [t.severity, t.priority].filter(Boolean).join(", ");
10265
+ return `\`${t.entryId ?? t.name}\` (${t.name}${meta ? `, ${meta}` : ""})`;
10266
+ });
10267
+ lines.push(` Tensions: ${tensionLines.join("; ")}`);
10268
+ }
10269
+ }
10270
+ lines.push("");
10271
+ }
10272
+ if (orientEntries.activeGoals?.length > 0) {
10273
+ lines.push("## Active goals");
10274
+ orientEntries.activeGoals.forEach((e) => lines.push(fmt(e)));
10275
+ lines.push("");
10276
+ }
10277
+ if (orientEntries.recentDecisions?.length > 0) {
10278
+ lines.push("## Recent decisions");
10279
+ orientEntries.recentDecisions.forEach((e) => lines.push(fmt(e)));
10280
+ lines.push("");
10281
+ }
10282
+ if (orientEntries.recentlySuperseded?.length > 0) {
10283
+ lines.push("## Recently superseded");
10284
+ orientEntries.recentlySuperseded.forEach((e) => lines.push(fmt(e)));
10285
+ lines.push("");
10286
+ }
10287
+ if (orientEntries.staleEntries?.length > 0) {
10288
+ lines.push("## Needs confirmation");
10289
+ lines.push(`_Domain stratum entries not confirmed in ${orientEntries.stalenessThresholdDays} days._`);
10290
+ orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
10291
+ lines.push("");
10292
+ }
10293
+ if (orientEntries.architectureNotes?.length > 0) {
10294
+ lines.push("## Architecture notes");
10295
+ orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
10296
+ lines.push("");
10297
+ }
10298
+ const mapGovernanceEntry = (e) => ({
10299
+ entryId: e.entryId,
10300
+ name: e.name,
10301
+ description: typeof e.preview === "string" ? e.preview : void 0
10302
+ });
10303
+ lines.push(...buildOperatingProtocol({
10304
+ principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
10305
+ standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
10306
+ businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
10307
+ }, task));
10308
+ }
10309
+ let allEntries = [];
10310
+ try {
10311
+ allEntries = await mcpQuery("chain.listEntries", {}) ?? [];
10312
+ } catch {
10313
+ }
10314
+ const plannedWork = buildPlannedWork(allEntries);
10315
+ if (hasPlannedWork(plannedWork)) {
10316
+ lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
10317
+ } else if (recoveryBlock) {
10318
+ lines.push(...formatRecoveryBlock(recoveryBlock));
10230
10319
  }
10231
- const mapGovernanceEntry = (e) => ({
10232
- entryId: e.entryId,
10233
- name: e.name,
10234
- description: typeof e.preview === "string" ? e.preview : void 0
10235
- });
10236
- lines.push(...buildOperatingProtocol({
10237
- principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
10238
- standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
10239
- businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
10240
- }, task));
10241
- }
10242
- let allEntries = [];
10243
- try {
10244
- allEntries = await mcpQuery("chain.listEntries", {}) ?? [];
10245
- } catch {
10246
- }
10247
- const plannedWork = buildPlannedWork(allEntries);
10248
- if (hasPlannedWork(plannedWork)) {
10249
- lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
10250
10320
  } else {
10251
- const briefingItems = [];
10321
+ lines.push(`**Brain stage: ${orientStage}.**`);
10322
+ lines.push("");
10323
+ if (orientEntries?.activeBets?.length) {
10324
+ lines.push("## Active bets \u2014 current scope");
10325
+ lines.push("_Work outside these bets requires explicit user confirmation._");
10326
+ lines.push("");
10327
+ for (const e of orientEntries.activeBets) {
10328
+ lines.push(fmt(e));
10329
+ }
10330
+ lines.push("");
10331
+ }
10332
+ lines.push(...buildOperatingProtocol());
10252
10333
  if (priorSessions.length > 0 && !recoveryBlock) {
10253
10334
  const last = priorSessions[0];
10254
- const date = new Date(last.startedAt).toISOString().split("T")[0];
10335
+ const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
10255
10336
  const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
10256
10337
  const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
10257
- briefingItems.push(`**Last session** (${date}): ${created} created, ${modified} modified`);
10258
- }
10259
- if (readiness.gaps?.length > 0) {
10260
- briefingItems.push(`**${readiness.gaps.length} gap${readiness.gaps.length === 1 ? "" : "s"}** remaining`);
10261
- }
10262
- if (briefingItems.length > 0) {
10263
- lines.push("## Briefing");
10264
- for (const item of briefingItems) {
10265
- lines.push(`- ${item}`);
10266
- }
10338
+ lines.push(`_Last session (${date}): ${created} created, ${modified} modified._`);
10267
10339
  lines.push("");
10268
10340
  }
10269
10341
  if (recoveryBlock) {
10270
- lines.push("");
10271
10342
  lines.push(...formatRecoveryBlock(recoveryBlock));
10272
10343
  }
10273
- }
10274
- const activeEntries = allEntries.filter((e) => e.status === "active");
10275
- if (activeEntries.length > 0) {
10276
- const orgHealth = computeOrganisationHealth(activeEntries);
10277
- if (orgHealth.disagreements > 0) {
10278
- lines.push("## Organisation Health");
10279
- lines.push(...formatOrgHealthLines(orgHealth, 3));
10280
- lines.push("");
10281
- }
10282
- }
10283
- const epistemicEntries = activeEntries.filter(
10284
- (e) => e.collectionSlug === "insights" || e.collectionSlug === "assumptions"
10285
- );
10286
- if (epistemicEntries.length > 0) {
10287
- let validated = 0;
10288
- let evidenced = 0;
10289
- let hypotheses = 0;
10290
- let untested = 0;
10291
- for (const e of epistemicEntries) {
10292
- const ws = e.workflowStatus;
10293
- if (ws === "validated") validated++;
10294
- else if (ws === "evidenced") evidenced++;
10295
- else if (ws === "testing") evidenced++;
10296
- else if (ws === "invalidated") validated++;
10297
- else if (e.collectionSlug === "assumptions") untested++;
10298
- else hypotheses++;
10299
- }
10300
- const parts = [];
10301
- if (validated > 0) parts.push(`${validated} validated`);
10302
- if (evidenced > 0) parts.push(`${evidenced} evidenced`);
10303
- if (hypotheses > 0) parts.push(`${hypotheses} hypotheses`);
10304
- if (untested > 0) parts.push(`${untested} untested`);
10305
- lines.push(`**Epistemic health:** ${parts.join(" \xB7 ")} _(${epistemicEntries.length} claim-carrying entries)_`);
10344
+ lines.push("**What are you working on?** Tell me and I'll load relevant governance and context.");
10306
10345
  lines.push("");
10307
10346
  }
10308
- lines.push("What would you like to work on?");
10309
- lines.push("");
10310
10347
  }
10311
10348
  if (errors.length > 0) {
10312
10349
  lines.push("## Errors");
@@ -11704,4 +11741,4 @@ export {
11704
11741
  SERVER_VERSION,
11705
11742
  createProductBrainServer
11706
11743
  };
11707
- //# sourceMappingURL=chunk-VBKAAFR6.js.map
11744
+ //# sourceMappingURL=chunk-YARRUQBW.js.map