@productbrain/mcp 0.0.1-beta.8423 → 0.0.1-beta.8425

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.
@@ -5033,7 +5033,7 @@ var contextSchema = z15.object({
5033
5033
  ),
5034
5034
  direction: z15.enum(["outgoing", "incoming"]).default("outgoing").optional().describe("For 'chain' action: traversal direction. 'outgoing' follows relations from source, 'incoming' follows relations to source. Default: outgoing."),
5035
5035
  relationType: z15.string().max(200).optional().describe(
5036
- "Relation type filter. For 'chain': optional filter to traverse only this relation type. For 'cross-cut': required \u2014 scans all relations of this type across the workspace. Examples: 'part_of', 'informs', 'governs', 'blocks', 'depends_on'."
5036
+ "Relation type filter. For 'chain': optional filter to traverse only this relation type. For 'cross-cut': required \u2014 scans all relations of this type across the workspace. Examples: 'part_of', 'informs', 'governs', 'depends_on'."
5037
5037
  ),
5038
5038
  mode: z15.enum(["search", "graph"]).default("search").optional().describe("For gather: 'search' (default) or 'graph' (enhanced with provenance paths). Ignored when mapEntryId is provided."),
5039
5039
  maxHops: z15.number().min(1).max(4).default(2).describe("Relation traversal depth (1=direct only, 2=default, 3=wide net, 4=deep chain walk)"),
@@ -8677,14 +8677,11 @@ async function _handleOrient({ mode = "full", tier, task, startupSignals, invoca
8677
8677
  const fmt = (e) => {
8678
8678
  const type = e.canonicalKey ?? "generic";
8679
8679
  const stratum = e.stratum ?? "?";
8680
- const confidencePart = e.confidence ? ` [confidence: ${e.confidence}]` : "";
8681
- const reviewOverdue = e.reviewAt && new Date(e.reviewAt).getTime() < Date.now();
8682
- const overduePart = reviewOverdue ? " [review overdue]" : "";
8683
8680
  const originPart = e.origin ? ` (origin: ${e.origin})` : "";
8684
8681
  const mark = provenanceMark(e.provenance);
8685
8682
  const anchorSuffix = e.anchoredBy ? ` (${e.anchoredBy})` : "";
8686
8683
  const coherencyPart = coherencyCheckTag(e.coherencyCheckStatus);
8687
- return `- ${mark}\`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}${anchorSuffix}${originPart}${confidencePart}${overduePart}${coherencyPart}`;
8684
+ return `- ${mark}\`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}${anchorSuffix}${originPart}${coherencyPart}`;
8688
8685
  };
8689
8686
  const fullCoherence = await buildCoherenceSection();
8690
8687
  if (fullCoherence) {
@@ -10729,9 +10726,9 @@ var auditSchema = z21.object({
10729
10726
  "'run': run the hygiene audit for a work package entry."
10730
10727
  ),
10731
10728
  entryId: z21.string().describe("Work package entry ID to audit, e.g. '<PREFIX>-<n>'"),
10732
- // Chain: WP-770 K2, DEC-1796 — doneWhen/exclusions run unconditionally in both phases.
10729
+ // Chain: WP-770 K2, DEC-1796 — doneWhen runs unconditionally (DEC-1852) in both phases.
10733
10730
  phase: z21.enum(["shaping", "handoff"]).default("shaping").optional().describe(
10734
- "'shaping': run the core hygiene gates only. 'handoff': also runs the outcome-coupling gates (KR linkage, verification window). doneWhen/exclusions are checked unconditionally either way. Default: shaping."
10731
+ "'shaping': run the core hygiene gates only. 'handoff': also runs the outcome-coupling gates (KR linkage, verification window). doneWhen is checked unconditionally either way. Default: shaping."
10735
10732
  )
10736
10733
  });
10737
10734
  async function handleAuditRun(entryId, phase) {
@@ -11015,9 +11012,9 @@ var qualitySchema = z22.object({
11015
11012
  context: z22.enum(["capture", "commit", "review"]).default("review").optional().describe("For re-evaluate: evaluation context"),
11016
11013
  collection: z22.string().max(200).optional().describe("For 'verify-chain': collection slug to verify (default: glossary)."),
11017
11014
  mode: z22.enum(["report", "fix"]).optional().describe("For 'verify-chain': 'report' = read-only trust report (default). 'fix' = also update drifted codeMapping statuses."),
11018
- // Chain: WP-770 K2, DEC-1796 — doneWhen/exclusions run unconditionally in both phases.
11015
+ // Chain: WP-770 K2, DEC-1796 — doneWhen runs unconditionally (DEC-1852) in both phases.
11019
11016
  phase: z22.enum(["shaping", "handoff"]).optional().describe(
11020
- "For 'audit': 'shaping' runs the core hygiene gates only (default). 'handoff' also runs the outcome-coupling gates (KR linkage, verification window). doneWhen/exclusions are checked unconditionally either way."
11017
+ "For 'audit': 'shaping' runs the core hygiene gates only (default). 'handoff' also runs the outcome-coupling gates (KR linkage, verification window). doneWhen is checked unconditionally either way."
11021
11018
  ),
11022
11019
  story: z22.boolean().optional().describe("For 'spine-check': also run the on-demand one-story LLM judgment (never scheduled; result labelled [unvalidated])."),
11023
11020
  refresh: z22.boolean().optional().describe(
@@ -13797,6 +13794,83 @@ import { dirname, join, resolve as resolve4 } from "path";
13797
13794
  import { fileURLToPath } from "url";
13798
13795
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
13799
13796
 
13797
+ // src/lib/relationTypes.ts
13798
+ var ALLOWED_RELATION_TYPES_LIST = [
13799
+ // Core semantic types (in RELATION_TYPE_REGISTRY)
13800
+ "informs",
13801
+ "governs",
13802
+ "surfaces_tension_in",
13803
+ "defines_term_for",
13804
+ "belongs_to",
13805
+ "references",
13806
+ "related_to",
13807
+ "fills_slot",
13808
+ "commits_to",
13809
+ "informed_by",
13810
+ "depends_on",
13811
+ "conflicts_with",
13812
+ "confused_with",
13813
+ // WP-449: Glossary Foundation — negative assertion for agents (spec §4.5)
13814
+ "do_not_confuse_with",
13815
+ "replaces",
13816
+ "part_of",
13817
+ "constrains",
13818
+ // Epistemic types — evidence linking
13819
+ "validates",
13820
+ "invalidates",
13821
+ // Extended types (used in codebase, not in RELATION_TYPE_REGISTRY yet)
13822
+ "governed_by",
13823
+ "alternative_to",
13824
+ "has_proposal",
13825
+ "requests_promotion_of",
13826
+ "resolves",
13827
+ // WP-228 Slice 0: Playing Field relation types
13828
+ "experiences",
13829
+ "addresses",
13830
+ // Tier 1 Hierarchy: DEC-401 (experiments prereqs), DEC-402 (risk views)
13831
+ "threatens",
13832
+ "tests",
13833
+ "produces",
13834
+ // WP-274: Capability tier relation types
13835
+ "realizes",
13836
+ "requires",
13837
+ "invests_in",
13838
+ "enables",
13839
+ // PARKED (park note, 2026-08-23) — these 5 verbs are untouched by a separate
13840
+ // governance-verb-ownership review: no delete, no lock, no whitelist, no picker change,
13841
+ // and no relation of this note to that review. Live source-canonicalKey row counts, from
13842
+ // a read-only pass over an uncapped, exhaustive census of a real workspace (measured
13843
+ // 2026-08-23): follows = 11 rows (decision 2, milestone 5, work_package 4);
13844
+ // extracted_from = 4 rows (decision 1, insight 1, tension 1, work_package 1);
13845
+ // part_of_workstream = 0 rows; stated_by = 0 rows; discussed_in = 0 rows. Whether any of
13846
+ // these should be retired, merged, or promoted is a founder call, deliberately deferred —
13847
+ // not decided here, and not decided by silence either. Re-verify the counts before acting
13848
+ // on them; they will drift.
13849
+ // WP-344 S0a: Evolve-First Ingestion relation types
13850
+ "follows",
13851
+ // sequential/derived — new entry follows/continues from another
13852
+ "extracted_from",
13853
+ // provenance — this entry was extracted from a source document
13854
+ // WP-353 S0: scope-propagated commit relation types (DEC-A captured as exploring per RULE-187)
13855
+ "part_of_workstream",
13856
+ // entry belongs to a workstream anchor confirmed at scope step
13857
+ "stated_by",
13858
+ // entry was stated by a speaker in the source document
13859
+ "discussed_in",
13860
+ // entry was discussed within a particular meeting/doc context
13861
+ // Specification types — WP-363 E2: precision alternatives to governs for decision→governance relations
13862
+ "specializes",
13863
+ // DEC narrows or refines a broader STD/PRI — more specific application of the parent
13864
+ "implements",
13865
+ // DEC/WP realizes a BR/STD in concrete operational form
13866
+ // WP-641 S2a (WP-647): belongs_to vocabulary split — the strategy-scope successor.
13867
+ // The other two successors (authority_domain, domain_routed_to) and `blocks` are no longer
13868
+ // legal to write: a new workspace carries none of them (DEC-1845, TASK-331, TASK-425).
13869
+ "anchored_to"
13870
+ // strategy-scope anchor: a raw strategy entry belongs_to a product (DEC-1523)
13871
+ ];
13872
+ var ALLOWED_RELATION_TYPES = new Set(ALLOWED_RELATION_TYPES_LIST);
13873
+
13800
13874
  // src/resources/agentCheatsheet.ts
13801
13875
  var DATA_HANDLING_SECTION = "### How PB Handles Your Data\n- **The reply is the source of truth**: every reply carries `ok`; on failure it carries `code` and guidance. Read the reply, never an expectation.\n- **Empty-workspace safe**: the orientation resource and this cheatsheet render on a workspace with no entries and no collections.\n- **Workspace-agnostic**: PB is one product used by many teams. Nothing in these instructions is specific to your workspace; your own principles, standards and rules arrive through `productbrain://orientation`.\n- **Served, not installed**: these instructions, `productbrain://orientation` and `productbrain://agent-cheatsheet` are served over MCP.";
13802
13876
  var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
@@ -13828,12 +13902,8 @@ DEC (decisions), STR (strategy), TEN (tensions), FEAT (features),
13828
13902
  BET (bets), INS (insights), ARCH (architecture), TEAM (teams),
13829
13903
  ROL (roles), MTRC (tracking-events), ST (semantic-types)
13830
13904
 
13831
- ## Valid Relation Types (24)
13832
- informs, governs, surfaces_tension_in, defines_term_for, belongs_to,
13833
- references, related_to, fills_slot, commits_to, informed_by, depends_on,
13834
- conflicts_with, confused_with, replaces, part_of, constrains,
13835
- governed_by, alternative_to, has_proposal, requests_promotion_of, resolves,
13836
- anchored_to, authority_domain, domain_routed_to
13905
+ ## Valid Relation Types (${ALLOWED_RELATION_TYPES_LIST.length})
13906
+ ${ALLOWED_RELATION_TYPES_LIST.join(", ")}
13837
13907
 
13838
13908
  ## Lifecycle Status
13839
13909
  Write via \`update\`: \`draft\` | \`archived\` (\`accepted\` via \`commit\`, never \`update\`). \`active\`/\`deprecated\` are legacy: readable, refused on write.
@@ -13872,7 +13942,7 @@ function buildOrientationMarkdown(collections, standards, businessRules, princip
13872
13942
  wsRules.push({ id: s.entryId ?? "", name: s.name, severity: s.data?.severity ?? void 0, source: "standards" });
13873
13943
  }
13874
13944
  for (const r of businessRules ?? []) {
13875
- wsRules.push({ id: r.entryId ?? "", name: r.name, severity: r.data?.severity ?? void 0, source: "business-rules" });
13945
+ wsRules.push({ id: r.entryId ?? "", name: r.name, severity: r.data?.severity ?? void 0, source: "rules" });
13876
13946
  }
13877
13947
  if (wsRules.length > 0) {
13878
13948
  const ruleLines2 = wsRules.map((r) => {
@@ -13887,7 +13957,7 @@ These are principles, standards, and rules your team has accepted onto the Chain
13887
13957
  );
13888
13958
  } else {
13889
13959
  sections.push(
13890
- "## Your Workspace Principles & Rules\nNo active principles, standards, or business rules on the Chain yet.\nUse `capture` with collection `principles`, `standards`, or `business-rules` to add your team's guardrails.\nOnce accepted, they appear here at orient time \u2014 so every agent session starts with your rules visible."
13960
+ "## Your Workspace Principles & Rules\nNo active principles, standards, or rules on the Chain yet.\nUse `capture` with collection `principles`, `standards`, or `rules` to add your team's guardrails.\nOnce accepted, they appear here at orient time \u2014 so every agent session starts with your rules visible."
13891
13961
  );
13892
13962
  }
13893
13963
  sections.push(
@@ -13916,14 +13986,14 @@ Tags for filtering (e.g. \`severity:high\`), relations via \`entryRelations\`, v
13916
13986
  }
13917
13987
  const rulesCount = businessRules ? `${businessRules.length} entries` : "not loaded \u2014 collection may not exist yet";
13918
13988
  sections.push(
13919
- `## Business Rules
13920
- Collection: \`business-rules\` (${rulesCount}).
13921
- Find rules: \`entries action=search\` for text search, \`entries action=list collection=business-rules\` to browse.
13989
+ `## Rules
13990
+ Collection: \`rules\` (${rulesCount}).
13991
+ Find rules: \`entries action=search\` for text search, \`entries action=list collection=rules\` to browse.
13922
13992
  Check compliance: use the \`review-against-rules\` prompt (pass a domain).
13923
13993
  Draft a new rule: use the \`draft-rule-from-context\` prompt.`
13924
13994
  );
13925
13995
  sections.push(
13926
- "## Knowledge Graph\nEntries are connected via typed relations (`entryRelations` table). Relations are bidirectional and collection-agnostic \u2014 any entry can link to any other entry.\n\n**Recommended relation types** (extensible \u2014 any string accepted):\n- `governs` \u2014 a rule constrains behavior of a feature\n- `defines_term_for` \u2014 a glossary term is canonical vocabulary for a feature/area\n- `belongs_to` \u2014 a feature belongs to a product area or parent concept (typed successors: `anchored_to`, `authority_domain`)\n- `informs` \u2014 a decision or insight informs a feature\n- `surfaces_tension_in` \u2014 a tension exists within a feature area\n- `related_to`, `depends_on`, `replaces`, `conflicts_with`, `references`, `confused_with`\n\nEach relation type is defined as a glossary entry (prefix `GT-REL-*`) to prevent terminology drift. **Workflow-owned, NOT generic:** `domain_routed_to` (a question routed to a domain) \u2014 use the `question` tool (`action=create`/`adopt`, `domain=<slug>`), never `relations action=create`; the generic door has no replace/re-judge logic, so a raw write can leave duplicate routing edges and a stale suggested owner.\n\n**Tools:**\n- `context action=gather` \u2014 get the full context around any entry (multi-hop graph traversal)\n- `relations action=suggest` \u2014 discover potential connections for an entry\n- `relations action=create` \u2014 create a typed link between two entries\n- `relations action=find` \u2014 list direct relations for an entry\n\n**Convention:** When creating or updating entries in governed collections, always use `relations action=suggest` to discover and create relevant relations."
13996
+ "## Knowledge Graph\nEntries are connected via typed relations (`entryRelations` table). Relations are bidirectional and collection-agnostic \u2014 any entry can link to any other entry.\n\n**Recommended relation types** (extensible \u2014 any string accepted):\n- `governs` \u2014 a rule constrains behavior of a feature\n- `defines_term_for` \u2014 a glossary term is canonical vocabulary for a feature/area\n- `belongs_to` \u2014 a feature belongs to a product area or parent concept (typed successor: `anchored_to`)\n- `informs` \u2014 a decision or insight informs a feature\n- `surfaces_tension_in` \u2014 a tension exists within a feature area\n- `related_to`, `depends_on`, `replaces`, `conflicts_with`, `references`, `confused_with`\n\nEach relation type is defined as a glossary entry (prefix `GT-REL-*`) to prevent terminology drift.\n\n**Tools:**\n- `context action=gather` \u2014 get the full context around any entry (multi-hop graph traversal)\n- `relations action=suggest` \u2014 discover potential connections for an entry\n- `relations action=create` \u2014 create a typed link between two entries\n- `relations action=find` \u2014 list direct relations for an entry\n\n**Convention:** When creating or updating entries in governed collections, always use `relations action=suggest` to discover and create relevant relations."
13927
13997
  );
13928
13998
  sections.push(
13929
13999
  `## Creating Knowledge
@@ -13955,7 +14025,7 @@ async function buildOrientationResourceText(kernelQuery2, agentSessionId) {
13955
14025
  const [collectionsResult, standardsResult, rulesResult, principlesResult, orientViewResult] = await Promise.allSettled([
13956
14026
  kernelQuery2("chain.listCollections"),
13957
14027
  kernelQuery2("chain.listEntries", { collectionSlug: "standards" }),
13958
- kernelQuery2("chain.listEntries", { collectionSlug: "business-rules" }),
14028
+ kernelQuery2("chain.listEntries", { collectionSlug: "rules" }),
13959
14029
  kernelQuery2("chain.listEntries", { collectionSlug: "principles" }),
13960
14030
  kernelQuery2("chain.getOrientView", {
13961
14031
  tier: "summary",
@@ -14345,8 +14415,8 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
14345
14415
  // src/prompts/index.ts
14346
14416
  import { z as z34 } from "zod/v3";
14347
14417
  async function buildReviewAgainstRulesPrompt(domain) {
14348
- const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
14349
- const rules = entries.filter((e) => e.data?.domain === domain);
14418
+ const entries = await kernelQuery("chain.listEntries", { collectionSlug: "rules" });
14419
+ const rules = entries.slice(0, 50);
14350
14420
  if (rules.length === 0) {
14351
14421
  return {
14352
14422
  messages: [
@@ -14354,7 +14424,7 @@ async function buildReviewAgainstRulesPrompt(domain) {
14354
14424
  role: "user",
14355
14425
  content: {
14356
14426
  type: "text",
14357
- text: `No business rules found for domain "${domain}". Use \`entries action=list collection=business-rules\` to see available domains.`
14427
+ text: `No rules found. Use \`entries action=list collection=rules\` to see available rules.`
14358
14428
  }
14359
14429
  }
14360
14430
  ]
@@ -14362,12 +14432,10 @@ async function buildReviewAgainstRulesPrompt(domain) {
14362
14432
  }
14363
14433
  const rulesText = rules.map(
14364
14434
  (r) => `### ${r.entryId ?? ""}: ${r.name}
14365
- Status: ${r.status} | Severity: ${r.data?.severity ?? "unknown"}
14366
- Description: ${readEntryBody(r.data, "description") ?? ""}
14367
- Data Impact: ${r.data?.dataImpact ?? ""}
14368
- Platforms: ${(r.data?.platforms ?? []).join(", ")}
14369
- ` + (r.data?.conflictWith ? `CONFLICT: ${r.data.conflictWith.rule} \u2014 ${r.data.conflictWith.nature}
14370
- ` : "")
14435
+ Status: ${r.status} | Applies: ${r.data?.applies ?? "unspecified"}
14436
+ Body: ${readEntryBody(r.data, "body") ?? ""}
14437
+ Why: ${r.data?.why ?? ""}
14438
+ `
14371
14439
  ).join("\n---\n\n");
14372
14440
  return {
14373
14441
  messages: [
@@ -14375,14 +14443,14 @@ Platforms: ${(r.data?.platforms ?? []).join(", ")}
14375
14443
  role: "user",
14376
14444
  content: {
14377
14445
  type: "text",
14378
- text: `Review the current code or design against the following business rules for the "${domain}" domain.
14446
+ text: `Review the current code or design against the following rules, focusing on the "${domain}" area.
14379
14447
 
14380
14448
  For each rule, assess:
14381
14449
  1. Is the current implementation compliant?
14382
14450
  2. Are there potential violations or edge cases?
14383
14451
  3. What specific changes would be needed for compliance?
14384
14452
 
14385
- Business Rules:
14453
+ Rules (up to 50):
14386
14454
 
14387
14455
  ${rulesText}
14388
14456
 
@@ -14426,7 +14494,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
14426
14494
  function registerPrompts(server) {
14427
14495
  server.prompt(
14428
14496
  "review-against-rules",
14429
- "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.",
14497
+ "Review code or a design decision against the rules, focused on a given area. Fetches the rules and asks you to do a structured compliance review.",
14430
14498
  { domain: z34.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
14431
14499
  async ({ domain }) => buildReviewAgainstRulesPrompt(domain)
14432
14500
  );
@@ -14440,38 +14508,7 @@ function registerPrompts(server) {
14440
14508
  "draft-decision-record",
14441
14509
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
14442
14510
  { context: z34.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
14443
- async ({ context }) => {
14444
- const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
14445
- const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
14446
- const recentContext = sorted.length > 0 ? sorted.map((d) => `- [${d.status}] ${d.name} (${d.data?.date ?? "no date"})`).join("\n") : "No previous decisions recorded.";
14447
- return {
14448
- messages: [
14449
- {
14450
- role: "user",
14451
- content: {
14452
- type: "text",
14453
- text: `Draft a structured decision record from the following context:
14454
-
14455
- "${context}"
14456
-
14457
- Recent decisions for reference:
14458
- ${recentContext}
14459
-
14460
- Structure the decision record with:
14461
- 1. **Title**: Concise decision statement
14462
- 2. **Decided by**: Who made or approved this decision
14463
- 3. **Date**: When it was decided
14464
- 4. **Status**: decided / proposed / revisited
14465
- 5. **Rationale**: Why this decision was made, including trade-offs considered
14466
- 6. **Alternatives considered**: What else was on the table
14467
- 7. **Related rules or tensions**: Any business rules or tensions this connects to
14468
-
14469
- After drafting, I can log it using the capture tool with collection "decisions".`
14470
- }
14471
- }
14472
- ]
14473
- };
14474
- }
14511
+ async ({ context }) => buildDraftDecisionRecordPrompt(context)
14475
14512
  );
14476
14513
  server.prompt(
14477
14514
  "draft-rule-from-context",
@@ -14483,35 +14520,62 @@ After drafting, I can log it using the capture tool with collection "decisions".
14483
14520
  async ({ observation, domain }) => buildDraftRuleFromContextPrompt(observation, domain)
14484
14521
  );
14485
14522
  }
14523
+ async function buildDraftDecisionRecordPrompt(context) {
14524
+ const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
14525
+ const created = (d) => d.createdAt ?? 0;
14526
+ const sorted = [...recentDecisions].sort((a, b) => created(b) - created(a)).slice(0, 5);
14527
+ const recentContext = sorted.length > 0 ? sorted.map((d) => `- [${d.status}] ${d.name}`).join("\n") : "No previous decisions recorded.";
14528
+ return {
14529
+ messages: [
14530
+ {
14531
+ role: "user",
14532
+ content: {
14533
+ type: "text",
14534
+ text: `Draft a structured decision record from the following context:
14535
+
14536
+ "${context}"
14537
+
14538
+ Recent decisions for reference:
14539
+ ${recentContext}
14540
+
14541
+ Structure the decision record with:
14542
+ 1. **Title**: Concise decision statement
14543
+ 2. **Body**: What was decided, including the alternatives considered and the trade-offs
14544
+ 3. **Why**: The reason behind the decision
14545
+ 4. **Related rules or tensions**: Any business rules or tensions this connects to
14546
+
14547
+ Who decided it and when are recorded by the Chain itself; do not add them as fields. After drafting, I can log it using the capture tool with collection "decisions".`
14548
+ }
14549
+ }
14550
+ ]
14551
+ };
14552
+ }
14486
14553
  async function buildDraftRuleFromContextPrompt(observation, domain) {
14487
- const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
14488
- const existingRules = allRules.filter((r) => r.data?.domain === domain);
14489
- const existingContext = existingRules.length > 0 ? existingRules.map((r) => `${r.entryId ?? ""}: ${r.name} [${r.status}] \u2014 ${readEntryBody(r.data, "description") ?? ""}`).join("\n") : "No existing rules for this domain.";
14554
+ const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "rules" });
14555
+ const existingRules = allRules.slice(0, 25);
14556
+ const existingContext = existingRules.length > 0 ? existingRules.map((r) => `${r.entryId ?? ""}: ${r.name} [${r.status}] \u2014 ${readEntryBody(r.data, "body") ?? ""}`).join("\n") : "No existing rules for this domain.";
14490
14557
  return {
14491
14558
  messages: [
14492
14559
  {
14493
14560
  role: "user",
14494
14561
  content: {
14495
14562
  type: "text",
14496
- text: `Draft a business rule based on this observation:
14563
+ text: `Draft a rule based on this observation:
14497
14564
 
14498
14565
  "${observation}"
14499
14566
 
14500
14567
  Domain: ${domain}
14501
14568
 
14502
- Existing rules in this domain:
14569
+ Existing rules (up to 25):
14503
14570
  ${existingContext}
14504
14571
 
14505
- Draft the rule with these fields (do not specify entryId \u2014 Convex assigns sequential IDs automatically, e.g. <PREFIX>-<n>):
14572
+ Draft the rule with these fields (do not specify entryId \u2014 Convex assigns sequential IDs automatically, e.g. RULE-<n>):
14506
14573
  1. **name**: Concise rule title
14507
- 2. **data.description**: What the rule states
14508
- 3. **data.rationale**: Why this rule matters
14509
- 4. **data.dataImpact**: How this affects data models, APIs, or storage
14510
- 5. **data.severity**: high / medium / low
14511
- 6. **data.platforms**: Which platforms are affected
14512
- 7. **data.relatedRules**: Any related existing rules
14513
-
14514
- Make sure the rule is consistent with existing rules and doesn't contradict them. After drafting, I can create it using the capture tool with collection "business-rules".`
14574
+ 2. **data.body**: What the rule states, and whether it is an enforced invariant or a belief you reason from
14575
+ 3. **data.why**: Why this rule matters
14576
+ 4. **data.applies**: everyone / team / role (optional)
14577
+
14578
+ Make sure the rule is consistent with existing rules and doesn't contradict them. After drafting, I can create it using the capture tool with collection "rules".`
14515
14579
  }
14516
14580
  }
14517
14581
  ]
@@ -14638,4 +14702,4 @@ export {
14638
14702
  createProductBrainServer,
14639
14703
  initFeatureFlags
14640
14704
  };
14641
- //# sourceMappingURL=chunk-RLXYUP6A.js.map
14705
+ //# sourceMappingURL=chunk-BIHHHC2H.js.map