@productbrain/mcp 0.0.1-beta.69 → 0.0.1-beta.70

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.
@@ -2004,6 +2004,635 @@ import { z as z9 } from "zod";
2004
2004
 
2005
2005
  // src/tools/wrapup.ts
2006
2006
  import { z as z8 } from "zod";
2007
+
2008
+ // src/lib/coherence/data.ts
2009
+ var REGISTRY_MANIFEST = [
2010
+ // ── Canonical UI registries ──────────────────────────────────────
2011
+ {
2012
+ id: "ui-colors",
2013
+ path: "src/lib/utils/collection-colors.ts",
2014
+ exportName: "COLLECTION_COLORS",
2015
+ description: "CSS color mapping for UI components",
2016
+ expectedCoverage: "curated",
2017
+ keyExtraction: "object-keys"
2018
+ },
2019
+ {
2020
+ id: "routes",
2021
+ path: "src/lib/navigation.ts",
2022
+ exportName: "COLLECTION_ROUTES",
2023
+ description: "Collection slug to dedicated route path",
2024
+ expectedCoverage: "curated",
2025
+ keyExtraction: "object-keys"
2026
+ },
2027
+ {
2028
+ id: "gap-paths",
2029
+ path: "src/lib/navigation.ts",
2030
+ exportName: "GAP_PATHS",
2031
+ description: "Readiness gap ID to workspace-relative route path",
2032
+ expectedCoverage: "curated",
2033
+ keyExtraction: "object-keys"
2034
+ },
2035
+ {
2036
+ id: "sidebar-collections",
2037
+ path: "src/lib/navigation.ts",
2038
+ exportName: "SIDEBAR_COLLECTION_SLUGS",
2039
+ description: "Collection slugs with dedicated sidebar entries",
2040
+ expectedCoverage: "subset",
2041
+ keyExtraction: "set-values"
2042
+ },
2043
+ {
2044
+ id: "display-fields",
2045
+ path: "src/lib/components/EntryPreviewModal.svelte",
2046
+ exportName: "DISPLAY_FIELDS",
2047
+ description: "Fields displayed in entry preview modal per collection",
2048
+ expectedCoverage: "curated",
2049
+ keyExtraction: "object-keys"
2050
+ },
2051
+ // ── Brain Chat registries ────────────────────────────────────────
2052
+ {
2053
+ id: "brain-colors",
2054
+ path: "src/lib/components/brain/brain-types.ts",
2055
+ exportName: "COLLECTION_COLORS",
2056
+ description: "Brain Chat collection color palette",
2057
+ expectedCoverage: "subset",
2058
+ keyExtraction: "object-keys"
2059
+ },
2060
+ {
2061
+ id: "brain-icons",
2062
+ path: "src/lib/components/brain/brain-types.ts",
2063
+ exportName: "COLLECTION_ICONS",
2064
+ description: "Brain Chat collection icons",
2065
+ expectedCoverage: "subset",
2066
+ keyExtraction: "object-keys"
2067
+ },
2068
+ // ── Graph visualization registries ───────────────────────────────
2069
+ {
2070
+ id: "graph-colors",
2071
+ path: "src/lib/utils/graphToVisNetwork.ts",
2072
+ exportName: "GRAPH_COLLECTION_COLORS",
2073
+ description: "Graph visualization hex color palette",
2074
+ expectedCoverage: "subset",
2075
+ keyExtraction: "object-keys"
2076
+ },
2077
+ {
2078
+ id: "graph-type-colors",
2079
+ path: "src/lib/components/graph/graph.types.ts",
2080
+ exportName: "COLLECTION_COLORS",
2081
+ description: "Chain Graph collection colors (distinct palette)",
2082
+ expectedCoverage: "subset",
2083
+ keyExtraction: "object-keys"
2084
+ },
2085
+ // ── Studio registries ────────────────────────────────────────────
2086
+ {
2087
+ id: "renderers",
2088
+ path: "src/lib/components/studio/renderers/index.ts",
2089
+ exportName: "COLLECTION_RENDERERS",
2090
+ description: "Custom Svelte renderer component per collection",
2091
+ expectedCoverage: "subset",
2092
+ keyExtraction: "object-keys"
2093
+ },
2094
+ // ── Backend registries ───────────────────────────────────────────
2095
+ {
2096
+ id: "classification-map",
2097
+ path: "convex/lib/collectionClassification.ts",
2098
+ exportName: "CLASSIFICATION_MAP",
2099
+ description: "Collection classification: governance, nav group, ID prefix",
2100
+ expectedCoverage: "all",
2101
+ keyExtraction: "object-keys"
2102
+ },
2103
+ {
2104
+ id: "default-collections",
2105
+ path: "convex/lib/collectionClassification.ts",
2106
+ exportName: "DEFAULT_COLLECTIONS",
2107
+ description: "Default collection definitions with field schemas and purposes",
2108
+ expectedCoverage: "all",
2109
+ keyExtraction: "array-slug"
2110
+ },
2111
+ {
2112
+ id: "classification-meta",
2113
+ path: "convex/mcpKnowledge/classifierPrompt.ts",
2114
+ exportName: "CLASSIFICATION_META",
2115
+ description: "LLM classifier decision checks per collection",
2116
+ expectedCoverage: "all",
2117
+ keyExtraction: "object-keys"
2118
+ },
2119
+ {
2120
+ id: "collection-floors",
2121
+ path: "convex/workspaceReadiness.ts",
2122
+ exportName: "COLLECTION_FLOORS",
2123
+ description: "Minimum user-authored entries required per collection",
2124
+ expectedCoverage: "curated",
2125
+ keyExtraction: "object-keys"
2126
+ }
2127
+ ];
2128
+ var COLLECTION_REGISTRY_RULES = [
2129
+ // ── Sync: authoritative registries must agree ─────────────────────
2130
+ {
2131
+ id: "sync-classification-defaults",
2132
+ type: "sync",
2133
+ description: "CLASSIFICATION_MAP and DEFAULT_COLLECTIONS should cover the same collections",
2134
+ subject: "classification-map",
2135
+ reference: "default-collections"
2136
+ },
2137
+ {
2138
+ id: "sync-classification-classifier",
2139
+ type: "sync",
2140
+ description: "CLASSIFICATION_MAP and CLASSIFICATION_META should cover the same collections",
2141
+ subject: "classification-map",
2142
+ reference: "classification-meta"
2143
+ },
2144
+ // ── Coverage: downstream registries should cover important collections ──
2145
+ {
2146
+ id: "coverage-floors",
2147
+ type: "coverage",
2148
+ description: "COLLECTION_FLOORS should cover all classified collections",
2149
+ subject: "collection-floors",
2150
+ reference: "classification-map"
2151
+ },
2152
+ {
2153
+ id: "coverage-ui-colors",
2154
+ type: "coverage",
2155
+ description: "UI colors should cover collections that have sidebar routes",
2156
+ subject: "ui-colors",
2157
+ reference: "routes"
2158
+ },
2159
+ // ── Subset: specialized registries should only reference known collections ──
2160
+ {
2161
+ id: "subset-brain-colors",
2162
+ type: "subset",
2163
+ description: "Brain colors should only reference classified collections",
2164
+ subject: "brain-colors",
2165
+ reference: "classification-map"
2166
+ },
2167
+ {
2168
+ id: "subset-brain-icons",
2169
+ type: "subset",
2170
+ description: "Brain icons should only reference classified collections",
2171
+ subject: "brain-icons",
2172
+ reference: "classification-map"
2173
+ },
2174
+ {
2175
+ id: "subset-renderers",
2176
+ type: "subset",
2177
+ description: "Renderers should only reference classified collections",
2178
+ subject: "renderers",
2179
+ reference: "classification-map"
2180
+ },
2181
+ {
2182
+ id: "subset-display-fields",
2183
+ type: "subset",
2184
+ description: "Display fields should only reference classified collections",
2185
+ subject: "display-fields",
2186
+ reference: "classification-map"
2187
+ },
2188
+ {
2189
+ id: "subset-graph-colors",
2190
+ type: "subset",
2191
+ description: "Graph colors should only reference classified collections",
2192
+ subject: "graph-colors",
2193
+ reference: "classification-map"
2194
+ },
2195
+ {
2196
+ id: "subset-graph-type-colors",
2197
+ type: "subset",
2198
+ description: "Graph type colors should only reference classified collections",
2199
+ subject: "graph-type-colors",
2200
+ reference: "classification-map"
2201
+ },
2202
+ // ── Implication: presence in one registry implies presence in another ──
2203
+ {
2204
+ id: "implication-routes-need-colors",
2205
+ type: "implication",
2206
+ description: "Collections with routes should also have UI colors",
2207
+ subject: "routes",
2208
+ reference: "ui-colors"
2209
+ },
2210
+ {
2211
+ id: "implication-sidebar-needs-route",
2212
+ type: "implication",
2213
+ description: "Sidebar collections should have a route defined",
2214
+ subject: "sidebar-collections",
2215
+ reference: "routes"
2216
+ }
2217
+ ];
2218
+
2219
+ // src/lib/coherence/engine.ts
2220
+ var MAX_VIOLATIONS = 50;
2221
+ var SEVERITY_RANK = { error: 0, warning: 1, info: 2 };
2222
+ function severityFromCoverage(coverage) {
2223
+ switch (coverage) {
2224
+ case "all":
2225
+ return "error";
2226
+ case "curated":
2227
+ return "warning";
2228
+ case "subset":
2229
+ return "info";
2230
+ }
2231
+ }
2232
+ function evaluateCoverage({ rule, subjectRef, subjectKeys, referenceKeys }) {
2233
+ const severity = rule.severityOverride ?? severityFromCoverage(subjectRef.expectedCoverage);
2234
+ const violations = [];
2235
+ for (const slug of referenceKeys) {
2236
+ if (!subjectKeys.has(slug)) {
2237
+ violations.push({
2238
+ ruleId: rule.id,
2239
+ ruleType: "coverage",
2240
+ registryId: rule.subject,
2241
+ collectionSlug: slug,
2242
+ message: `"${slug}" missing from ${subjectRef.exportName} (${subjectRef.path})`,
2243
+ severity,
2244
+ fix: `Add "${slug}" to ${subjectRef.exportName} in ${subjectRef.path}`
2245
+ });
2246
+ }
2247
+ }
2248
+ return violations;
2249
+ }
2250
+ function evaluateSync({ rule, subjectRef, subjectKeys, referenceRef, referenceKeys }) {
2251
+ const violations = [];
2252
+ for (const slug of referenceKeys) {
2253
+ if (!subjectKeys.has(slug)) {
2254
+ violations.push({
2255
+ ruleId: rule.id,
2256
+ ruleType: "sync",
2257
+ registryId: rule.subject,
2258
+ collectionSlug: slug,
2259
+ message: `"${slug}" in ${referenceRef.exportName} but missing from ${subjectRef.exportName}`,
2260
+ severity: "error",
2261
+ fix: `Add "${slug}" to ${subjectRef.exportName} in ${subjectRef.path}`
2262
+ });
2263
+ }
2264
+ }
2265
+ for (const slug of subjectKeys) {
2266
+ if (!referenceKeys.has(slug)) {
2267
+ violations.push({
2268
+ ruleId: rule.id,
2269
+ ruleType: "sync",
2270
+ registryId: referenceRef.id,
2271
+ collectionSlug: slug,
2272
+ message: `"${slug}" in ${subjectRef.exportName} but missing from ${referenceRef.exportName}`,
2273
+ severity: "error",
2274
+ fix: `Add "${slug}" to ${referenceRef.exportName} in ${referenceRef.path}`
2275
+ });
2276
+ }
2277
+ }
2278
+ return violations;
2279
+ }
2280
+ function evaluateSubset({ rule, subjectRef, subjectKeys, referenceRef, referenceKeys }) {
2281
+ const severity = rule.severityOverride ?? severityFromCoverage(subjectRef.expectedCoverage);
2282
+ const violations = [];
2283
+ for (const slug of subjectKeys) {
2284
+ if (!referenceKeys.has(slug)) {
2285
+ violations.push({
2286
+ ruleId: rule.id,
2287
+ ruleType: "subset",
2288
+ registryId: rule.subject,
2289
+ collectionSlug: slug,
2290
+ message: `"${slug}" in ${subjectRef.exportName} is not a classified collection`,
2291
+ severity,
2292
+ fix: `Remove "${slug}" from ${subjectRef.exportName}, or add it to ${referenceRef.exportName}`
2293
+ });
2294
+ }
2295
+ }
2296
+ return violations;
2297
+ }
2298
+ function evaluateImplication({ rule, subjectRef, subjectKeys, referenceRef, referenceKeys }) {
2299
+ const severity = rule.severityOverride ?? severityFromCoverage(referenceRef.expectedCoverage);
2300
+ const violations = [];
2301
+ for (const slug of subjectKeys) {
2302
+ if (!referenceKeys.has(slug)) {
2303
+ violations.push({
2304
+ ruleId: rule.id,
2305
+ ruleType: "implication",
2306
+ registryId: referenceRef.id,
2307
+ collectionSlug: slug,
2308
+ message: `"${slug}" has ${subjectRef.description} but no ${referenceRef.description}`,
2309
+ severity,
2310
+ fix: `Add "${slug}" to ${referenceRef.exportName} in ${referenceRef.path}`
2311
+ });
2312
+ }
2313
+ }
2314
+ return violations;
2315
+ }
2316
+ var EVALUATORS = {
2317
+ coverage: evaluateCoverage,
2318
+ sync: evaluateSync,
2319
+ subset: evaluateSubset,
2320
+ implication: evaluateImplication
2321
+ };
2322
+ function evaluateCoherence(manifest, rules, resolved, checkedAt) {
2323
+ const registryMap = new Map(manifest.map((r) => [r.id, r]));
2324
+ const allViolations = [];
2325
+ for (const rule of rules) {
2326
+ const subjectRef = registryMap.get(rule.subject);
2327
+ if (!subjectRef) continue;
2328
+ const subjectKeys = resolved[rule.subject];
2329
+ if (!subjectKeys) continue;
2330
+ if (!rule.reference) continue;
2331
+ const referenceRef = registryMap.get(rule.reference);
2332
+ if (!referenceRef) continue;
2333
+ const referenceKeys = resolved[rule.reference];
2334
+ if (!referenceKeys) continue;
2335
+ allViolations.push(
2336
+ ...EVALUATORS[rule.type]({ rule, subjectRef, subjectKeys, referenceRef, referenceKeys })
2337
+ );
2338
+ }
2339
+ allViolations.sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]);
2340
+ const capped = allViolations.slice(0, MAX_VIOLATIONS);
2341
+ return {
2342
+ checkedAt: checkedAt ?? Date.now(),
2343
+ totalRegistries: manifest.length,
2344
+ totalRules: rules.length,
2345
+ violations: capped,
2346
+ summary: {
2347
+ errors: capped.filter((v) => v.severity === "error").length,
2348
+ warnings: capped.filter((v) => v.severity === "warning").length,
2349
+ infos: capped.filter((v) => v.severity === "info").length,
2350
+ total: capped.length
2351
+ }
2352
+ };
2353
+ }
2354
+ function toSnapshot(report, maxTopGaps = 10) {
2355
+ return {
2356
+ checkedAt: report.checkedAt,
2357
+ totalGaps: report.summary.total,
2358
+ topGaps: report.violations.filter((v) => v.severity !== "info").slice(0, maxTopGaps).map((v) => ({ registry: v.registryId, slug: v.collectionSlug, severity: v.severity }))
2359
+ };
2360
+ }
2361
+
2362
+ // src/lib/coherence/resolver.ts
2363
+ import { readFileSync } from "fs";
2364
+ import { resolve } from "path";
2365
+ function resolveRegistries(projectRoot, manifest) {
2366
+ const resolved = {};
2367
+ for (const ref of manifest) {
2368
+ try {
2369
+ const filePath = resolve(projectRoot, ref.path);
2370
+ const content = readFileSync(filePath, "utf-8");
2371
+ const block = extractBlock(content, ref.exportName);
2372
+ if (!block) continue;
2373
+ const keys = extractKeys(block, ref.keyExtraction);
2374
+ if (keys.size > 0) {
2375
+ resolved[ref.id] = keys;
2376
+ }
2377
+ } catch {
2378
+ }
2379
+ }
2380
+ return resolved;
2381
+ }
2382
+ function extractBlock(content, exportName) {
2383
+ const escaped = exportName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2384
+ const pattern = new RegExp(
2385
+ `(?:export\\s+)?(?:const|let|var)\\s+${escaped}\\b[^=]*=\\s*`
2386
+ );
2387
+ const match = pattern.exec(content);
2388
+ if (!match) return null;
2389
+ let pos = match.index + match[0].length;
2390
+ while (pos < content.length && /\s/.test(content[pos])) pos++;
2391
+ const ch = content[pos];
2392
+ if (ch === "{" || ch === "[") {
2393
+ return extractBalanced(content, pos, ch, ch === "{" ? "}" : "]");
2394
+ }
2395
+ if (content.slice(pos).startsWith("new Set") || content.slice(pos).startsWith("new\nSet") || content.slice(pos).startsWith("new\n Set")) {
2396
+ const setMatch = content.slice(pos).match(/^new\s+Set\s*\(\s*\[/);
2397
+ if (setMatch) {
2398
+ return extractBalanced(content, pos + setMatch[0].length - 1, "[", "]");
2399
+ }
2400
+ }
2401
+ return null;
2402
+ }
2403
+ function extractBalanced(content, openPos, openChar, closeChar) {
2404
+ let depth = 1;
2405
+ let i = openPos + 1;
2406
+ while (i < content.length && depth > 0) {
2407
+ const ch = content[i];
2408
+ if (ch === "'" || ch === '"' || ch === "`") {
2409
+ i = skipString(content, i, ch);
2410
+ continue;
2411
+ }
2412
+ if (ch === "/" && content[i + 1] === "/") {
2413
+ while (i < content.length && content[i] !== "\n") i++;
2414
+ continue;
2415
+ }
2416
+ if (ch === "/" && content[i + 1] === "*") {
2417
+ i += 2;
2418
+ while (i < content.length && !(content[i - 1] === "*" && content[i] === "/")) i++;
2419
+ i++;
2420
+ continue;
2421
+ }
2422
+ if (ch === openChar) depth++;
2423
+ else if (ch === closeChar) depth--;
2424
+ i++;
2425
+ }
2426
+ if (depth !== 0) return null;
2427
+ return content.slice(openPos + 1, i - 1);
2428
+ }
2429
+ function skipString(content, start, quote) {
2430
+ let i = start + 1;
2431
+ while (i < content.length) {
2432
+ if (content[i] === "\\" && quote !== "`") {
2433
+ i += 2;
2434
+ continue;
2435
+ }
2436
+ if (content[i] === quote) return i + 1;
2437
+ i++;
2438
+ }
2439
+ return i;
2440
+ }
2441
+ function extractKeys(block, extraction) {
2442
+ switch (extraction) {
2443
+ case "object-keys":
2444
+ return extractObjectKeys(block);
2445
+ case "array-slug":
2446
+ return extractArraySlugs(block);
2447
+ case "set-values":
2448
+ return extractSetValues(block);
2449
+ }
2450
+ }
2451
+ function extractObjectKeys(block) {
2452
+ const keys = /* @__PURE__ */ new Set();
2453
+ let depth = 0;
2454
+ let i = 0;
2455
+ while (i < block.length) {
2456
+ const ch = block[i];
2457
+ if (ch === "/" && block[i + 1] === "/") {
2458
+ while (i < block.length && block[i] !== "\n") i++;
2459
+ continue;
2460
+ }
2461
+ if (ch === "/" && block[i + 1] === "*") {
2462
+ i += 2;
2463
+ while (i < block.length && !(block[i - 1] === "*" && block[i] === "/")) i++;
2464
+ i++;
2465
+ continue;
2466
+ }
2467
+ if (depth === 0) {
2468
+ const rest = block.slice(i);
2469
+ const quoted = rest.match(/^(['"])([\w-]+)\1\s*:/);
2470
+ if (quoted) {
2471
+ keys.add(quoted[2]);
2472
+ i += quoted[0].length;
2473
+ continue;
2474
+ }
2475
+ const ident = rest.match(/^([\w-]+)\s*:/);
2476
+ if (ident) {
2477
+ keys.add(ident[1]);
2478
+ i += ident[0].length;
2479
+ continue;
2480
+ }
2481
+ }
2482
+ if (ch === "'" || ch === '"' || ch === "`") {
2483
+ i = skipString(block, i, ch);
2484
+ continue;
2485
+ }
2486
+ if (ch === "{" || ch === "[" || ch === "(") {
2487
+ depth++;
2488
+ i++;
2489
+ continue;
2490
+ }
2491
+ if (ch === "}" || ch === "]" || ch === ")") {
2492
+ depth--;
2493
+ i++;
2494
+ continue;
2495
+ }
2496
+ i++;
2497
+ }
2498
+ return keys;
2499
+ }
2500
+ function extractArraySlugs(block) {
2501
+ const slugs = /* @__PURE__ */ new Set();
2502
+ for (const m of block.matchAll(/slug:\s*['"](\w[\w-]*)['"]/g)) {
2503
+ slugs.add(m[1]);
2504
+ }
2505
+ return slugs;
2506
+ }
2507
+ function extractSetValues(block) {
2508
+ const values = /* @__PURE__ */ new Set();
2509
+ for (const m of block.matchAll(/['"](\w[\w-]*)['"]/g)) {
2510
+ values.add(m[1]);
2511
+ }
2512
+ return values;
2513
+ }
2514
+
2515
+ // src/lib/coherence/git-detection.ts
2516
+ import { execSync } from "child_process";
2517
+ function detectTouchedRegistries(projectRoot) {
2518
+ const manifestPaths = new Set(REGISTRY_MANIFEST.map((r) => r.path));
2519
+ const touched = /* @__PURE__ */ new Set();
2520
+ try {
2521
+ const raw = execSync("git diff --name-only HEAD 2>/dev/null || git diff --name-only", {
2522
+ cwd: projectRoot,
2523
+ encoding: "utf-8",
2524
+ timeout: 5e3
2525
+ });
2526
+ for (const line of raw.split("\n")) {
2527
+ const trimmed = line.trim();
2528
+ if (trimmed && manifestPaths.has(trimmed)) {
2529
+ touched.add(trimmed);
2530
+ }
2531
+ }
2532
+ } catch {
2533
+ }
2534
+ return touched;
2535
+ }
2536
+
2537
+ // src/lib/coherence/index.ts
2538
+ function runCoherenceCheck(projectRoot) {
2539
+ const resolved = resolveRegistries(projectRoot, REGISTRY_MANIFEST);
2540
+ if (Object.keys(resolved).length < 2) return null;
2541
+ return evaluateCoherence(REGISTRY_MANIFEST, COLLECTION_REGISTRY_RULES, resolved);
2542
+ }
2543
+ function renderCoherenceLines(report) {
2544
+ const lines = ["## Codebase Coherence"];
2545
+ if (report.summary.total === 0) {
2546
+ lines.push("All registries aligned \u2014 no gaps detected.");
2547
+ lines.push("");
2548
+ return lines;
2549
+ }
2550
+ const parts = [];
2551
+ if (report.summary.errors > 0) parts.push(`${report.summary.errors} error${report.summary.errors === 1 ? "" : "s"}`);
2552
+ if (report.summary.warnings > 0) parts.push(`${report.summary.warnings} warning${report.summary.warnings === 1 ? "" : "s"}`);
2553
+ if (report.summary.infos > 0) parts.push(`${report.summary.infos} info`);
2554
+ lines.push(`**${report.summary.total} gap${report.summary.total === 1 ? "" : "s"}** across ${report.totalRegistries} registries (${parts.join(", ")})`);
2555
+ const errors = report.violations.filter((v) => v.severity === "error");
2556
+ const warnings = report.violations.filter((v) => v.severity === "warning");
2557
+ if (errors.length > 0) {
2558
+ lines.push("");
2559
+ lines.push("**Errors** (must fix):");
2560
+ for (const v of errors.slice(0, 5)) {
2561
+ lines.push(`- ${v.message}`);
2562
+ }
2563
+ if (errors.length > 5) lines.push(`- ...and ${errors.length - 5} more`);
2564
+ }
2565
+ if (warnings.length > 0) {
2566
+ lines.push("");
2567
+ lines.push("**Warnings**:");
2568
+ for (const v of warnings.slice(0, 3)) {
2569
+ lines.push(`- ${v.message}`);
2570
+ }
2571
+ if (warnings.length > 3) lines.push(`- ...and ${warnings.length - 3} more`);
2572
+ }
2573
+ lines.push("");
2574
+ return lines;
2575
+ }
2576
+ function checkAndRender(projectRoot) {
2577
+ const report = runCoherenceCheck(projectRoot);
2578
+ if (!report) return null;
2579
+ return {
2580
+ lines: renderCoherenceLines(report),
2581
+ snapshot: toSnapshot(report)
2582
+ };
2583
+ }
2584
+ function computeDelta(before, after) {
2585
+ const beforeSet = new Set(before.topGaps.map((g) => `${g.registry}::${g.slug}`));
2586
+ const afterSet = new Set(after.topGaps.map((g) => `${g.registry}::${g.slug}`));
2587
+ let gapsFixed = 0;
2588
+ for (const key of beforeSet) {
2589
+ if (!afterSet.has(key)) gapsFixed++;
2590
+ }
2591
+ let gapsIntroduced = 0;
2592
+ for (const key of afterSet) {
2593
+ if (!beforeSet.has(key)) gapsIntroduced++;
2594
+ }
2595
+ const netChange = after.totalGaps - before.totalGaps;
2596
+ const verdict = netChange < 0 ? "improved" : netChange > 0 ? "degraded" : "unchanged";
2597
+ return { before, after, gapsFixed, gapsIntroduced, netChange, verdict };
2598
+ }
2599
+ function renderCoherenceDelta(delta) {
2600
+ const lines = ["### Coherence Delta"];
2601
+ const p = (n) => n === 1 ? "" : "s";
2602
+ if (delta.verdict === "improved") {
2603
+ lines.push(
2604
+ `Registry coherence **improved**: ${delta.gapsFixed} gap${p(delta.gapsFixed)} fixed` + (delta.gapsIntroduced > 0 ? `, ${delta.gapsIntroduced} introduced` : "") + ` (net ${delta.netChange}). ${delta.after.totalGaps} gap${p(delta.after.totalGaps)} remain${delta.after.totalGaps === 1 ? "s" : ""}.`
2605
+ );
2606
+ } else if (delta.verdict === "degraded") {
2607
+ lines.push(
2608
+ `**Registry coherence degraded**: ${delta.gapsIntroduced} new gap${p(delta.gapsIntroduced)} introduced` + (delta.gapsFixed > 0 ? `, ${delta.gapsFixed} fixed` : "") + ` (net +${delta.netChange}). ${delta.after.totalGaps} gap${p(delta.after.totalGaps)} total.`
2609
+ );
2610
+ } else {
2611
+ lines.push(
2612
+ `Registry coherence unchanged. ${delta.after.totalGaps} gap${p(delta.after.totalGaps)} remain${delta.after.totalGaps === 1 ? "s" : ""}` + (delta.gapsFixed > 0 ? ` (${delta.gapsFixed} fixed, ${delta.gapsIntroduced} introduced)` : "") + "."
2613
+ );
2614
+ }
2615
+ lines.push("");
2616
+ return lines;
2617
+ }
2618
+
2619
+ // src/lib/resolve-project-root.ts
2620
+ import { existsSync } from "fs";
2621
+ import { resolve as resolve2 } from "path";
2622
+ function resolveProjectRoot() {
2623
+ const candidates = [
2624
+ process.env.WORKSPACE_PATH,
2625
+ process.cwd(),
2626
+ resolve2(process.cwd(), "..")
2627
+ ].filter(Boolean);
2628
+ for (const dir of candidates) {
2629
+ const resolved = resolve2(dir);
2630
+ if (existsSync(resolve2(resolved, "convex/schema.ts"))) return resolved;
2631
+ }
2632
+ return null;
2633
+ }
2634
+
2635
+ // src/tools/wrapup.ts
2007
2636
  async function mapWithConcurrency(items, mapper, concurrency = 3) {
2008
2637
  const results = new Array(items.length);
2009
2638
  let index = 0;
@@ -2023,6 +2652,19 @@ async function mapWithConcurrency(items, mapper, concurrency = 3) {
2023
2652
  );
2024
2653
  return results;
2025
2654
  }
2655
+ async function fetchSessionCoherenceSnapshot(sessionId) {
2656
+ try {
2657
+ const session = await mcpCall("agent.getSession", {
2658
+ sessionId
2659
+ });
2660
+ const raw = session?.coherenceSnapshot;
2661
+ if (raw && typeof raw.checkedAt === "number" && typeof raw.totalGaps === "number") {
2662
+ return raw;
2663
+ }
2664
+ } catch {
2665
+ }
2666
+ return null;
2667
+ }
2026
2668
  async function suggestLinksForEntries(entries) {
2027
2669
  const suggestions = [];
2028
2670
  const results = await mapWithConcurrency(
@@ -2117,6 +2759,46 @@ async function runWrapupReview() {
2117
2759
  lines.push("_Capturing these will make future sessions more effective._");
2118
2760
  lines.push("");
2119
2761
  }
2762
+ let coherenceVerdict;
2763
+ try {
2764
+ const projectRoot = resolveProjectRoot();
2765
+ if (projectRoot) {
2766
+ const touchedFiles = detectTouchedRegistries(projectRoot);
2767
+ const sessionSnapshot = await fetchSessionCoherenceSnapshot(sessionId);
2768
+ const shouldCheck = touchedFiles.size > 0 || sessionSnapshot !== null;
2769
+ if (shouldCheck) {
2770
+ const report = runCoherenceCheck(projectRoot);
2771
+ if (report) {
2772
+ const afterSnapshot = toSnapshot(report);
2773
+ if (sessionSnapshot) {
2774
+ const delta = computeDelta(sessionSnapshot, afterSnapshot);
2775
+ coherenceVerdict = delta.verdict;
2776
+ const deltaLines = renderCoherenceDelta(delta);
2777
+ lines.push(...deltaLines);
2778
+ if (touchedFiles.size > 0) {
2779
+ lines.push(
2780
+ `_This session touched ${touchedFiles.size} registry file${touchedFiles.size === 1 ? "" : "s"}: ${[...touchedFiles].slice(0, 3).map((f) => `\`${f}\``).join(", ")}${touchedFiles.size > 3 ? ` and ${touchedFiles.size - 3} more` : ""}._`
2781
+ );
2782
+ lines.push("");
2783
+ }
2784
+ } else if (touchedFiles.size > 0) {
2785
+ lines.push("### Coherence Check");
2786
+ lines.push(
2787
+ `This session touched ${touchedFiles.size} registry file${touchedFiles.size === 1 ? "" : "s"} but no session-start snapshot exists \u2014 showing absolute state.`
2788
+ );
2789
+ const { summary } = report;
2790
+ if (summary.total === 0) {
2791
+ lines.push("All registries aligned \u2014 no gaps.");
2792
+ } else {
2793
+ lines.push(`**${summary.total} gap${summary.total === 1 ? "" : "s"}**: ${summary.errors} error${summary.errors === 1 ? "" : "s"}, ${summary.warnings} warning${summary.warnings === 1 ? "" : "s"}, ${summary.infos} info.`);
2794
+ }
2795
+ lines.push("");
2796
+ }
2797
+ }
2798
+ }
2799
+ }
2800
+ } catch {
2801
+ }
2120
2802
  if (data.drafts.length > 0) {
2121
2803
  const draftIds = data.drafts.map((d) => `\`${d.entryId}\``).join(", ");
2122
2804
  lines.push("### Actions", "");
@@ -2125,7 +2807,7 @@ async function runWrapupReview() {
2125
2807
  lines.push("- **Skip:** call `session action=close` \u2014 drafts remain for next session's orient recovery.");
2126
2808
  }
2127
2809
  const gapCount = getSessionGaps().length;
2128
- return { text: lines.join("\n"), data, suggestions, gapCount };
2810
+ return { text: lines.join("\n"), data, suggestions, gapCount, coherenceVerdict };
2129
2811
  }
2130
2812
  async function runWrapupCommitAll(data, cachedSuggestions) {
2131
2813
  requireWriteAccess();
@@ -2250,7 +2932,7 @@ function registerWrapupTools(server) {
2250
2932
  }
2251
2933
  );
2252
2934
  }
2253
- const { text, data, suggestions, failureCode, gapCount } = await runWrapupReview();
2935
+ const { text, data, suggestions, failureCode, gapCount, coherenceVerdict } = await runWrapupReview();
2254
2936
  lastReviewData = data;
2255
2937
  lastReviewSuggestions = suggestions;
2256
2938
  lastReviewSessionId = getAgentSessionId();
@@ -2262,15 +2944,17 @@ ${text}` : text;
2262
2944
  }
2263
2945
  const next = data.drafts.length > 0 ? [{ tool: "session-wrapup", description: "Commit all drafts", parameters: { action: "commit-all" } }] : void 0;
2264
2946
  const gapsSummary = gapCount ? `, ${gapCount} knowledge gaps detected` : "";
2947
+ const coherenceSummary = coherenceVerdict ? `, coherence: ${coherenceVerdict}` : "";
2265
2948
  return successResult(
2266
2949
  fullText,
2267
- `Session review: ${data.drafts.length} uncommitted, ${data.committed.length} committed, ${suggestions.length} link suggestions${gapsSummary}.`,
2950
+ `Session review: ${data.drafts.length} uncommitted, ${data.committed.length} committed, ${suggestions.length} link suggestions${gapsSummary}${coherenceSummary}.`,
2268
2951
  {
2269
2952
  drafts: data.drafts.length,
2270
2953
  committed: data.committed.length,
2271
2954
  uncommitted: data.summary.uncommitted,
2272
2955
  suggestedLinks: suggestions.length,
2273
- knowledgeGaps: gapCount ?? 0
2956
+ knowledgeGaps: gapCount ?? 0,
2957
+ ...coherenceVerdict ? { coherenceVerdict } : {}
2274
2958
  },
2275
2959
  next
2276
2960
  );
@@ -3271,11 +3955,11 @@ This block is the final deliverable of the review. Everything else is supporting
3271
3955
  num: "02",
3272
3956
  label: "Standards & Chain Coherence",
3273
3957
  type: "open",
3274
- instruction: "Run quality check, chain-review gate (if applicable), and review-against-rules. Is the implementation coherent with the Chain SSOT?",
3275
- facilitatorGuidance: "For the BET/entry: quality action=check entryId='<ID>'. If the work produced a chain artifact: chain-review action=gate. Call review-against-rules with the relevant domain (e.g. 'Governance & Decision-Making'). Present: quality score, gate result, rule compliance. Flag any conflicts with Chain entries.",
3958
+ instruction: "Run quality check, chain-review gate (if applicable), review-against-rules, and codebase coherence gate. Is the implementation coherent with the Chain SSOT? Did it introduce or fix registry drift?",
3959
+ facilitatorGuidance: "For the BET/entry: quality action=check entryId='<ID>'. If the work produced a chain artifact: chain-review action=gate. Call review-against-rules with the relevant domain (e.g. 'Governance & Decision-Making'). **Codebase Coherence Gate (BET-99/FEAT-183):** Compare the Codebase Coherence section from Round 01's orient output against the current state. If orient showed coherence gaps and this implementation touched registry files (collection colors, routes, classification, renderers, etc.), check whether gaps increased or decreased. Gaps increased \u2192 flag as 'Coherence: WARN \u2014 N new gaps introduced' and list them. Gaps decreased \u2192 celebrate as 'Coherence: PASS \u2014 N gaps fixed.' No change \u2192 note as 'Coherence: PASS \u2014 no change.' If Round 01 did not show a coherence section, skip the gate \u2014 do not block on missing baseline. Present: quality score, gate result, rule compliance, coherence gate verdict.",
3276
3960
  outputSchema: {
3277
3961
  field: "standards",
3278
- description: "Quality score, gate result, rule compliance",
3962
+ description: "Quality score, gate result, rule compliance, coherence gate verdict (improved/degraded/unchanged)",
3279
3963
  format: "structured"
3280
3964
  },
3281
3965
  maxDurationHint: "5 min"
@@ -6468,23 +7152,11 @@ No constellation entries were committed.`
6468
7152
  }
6469
7153
 
6470
7154
  // src/tools/verify.ts
6471
- import { existsSync, readFileSync } from "fs";
6472
- import { resolve } from "path";
7155
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
7156
+ import { resolve as resolve3 } from "path";
6473
7157
  import { z as z13 } from "zod";
6474
- function resolveProjectRoot() {
6475
- const candidates = [
6476
- process.env.WORKSPACE_PATH,
6477
- process.cwd(),
6478
- resolve(process.cwd(), "..")
6479
- ].filter(Boolean);
6480
- for (const dir of candidates) {
6481
- const resolved = resolve(dir);
6482
- if (existsSync(resolve(resolved, "convex/schema.ts"))) return resolved;
6483
- }
6484
- return null;
6485
- }
6486
7158
  function parseConvexSchema(schemaPath) {
6487
- const content = readFileSync(schemaPath, "utf-8");
7159
+ const content = readFileSync2(schemaPath, "utf-8");
6488
7160
  const tables = /* @__PURE__ */ new Map();
6489
7161
  let currentTable = null;
6490
7162
  for (const line of content.split("\n")) {
@@ -6523,8 +7195,8 @@ function classifyRef(cleaned) {
6523
7195
  }
6524
7196
  function checkFileRef(ref, root) {
6525
7197
  const filePart = ref.split(/\s+/)[0];
6526
- const fullPath = resolve(root, filePart);
6527
- if (existsSync(fullPath)) return { result: "verified", reason: "exists" };
7198
+ const fullPath = resolve3(root, filePart);
7199
+ if (existsSync2(fullPath)) return { result: "verified", reason: "exists" };
6528
7200
  return { result: "drifted", reason: `not found: ${filePart}` };
6529
7201
  }
6530
7202
  function checkSchemaRef(ref, schema) {
@@ -6636,7 +7308,7 @@ function registerVerifyTools(server) {
6636
7308
  "Cannot find project root. Set WORKSPACE_PATH in .env.mcp."
6637
7309
  );
6638
7310
  }
6639
- const schema = parseConvexSchema(resolve(projectRoot, "convex/schema.ts"));
7311
+ const schema = parseConvexSchema(resolve3(projectRoot, "convex/schema.ts"));
6640
7312
  await server.sendLoggingMessage({
6641
7313
  level: "info",
6642
7314
  data: `Verifying "${collection}" against ${schema.size} schema tables at ${projectRoot}`,
@@ -7011,6 +7683,14 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
7011
7683
  }
7012
7684
 
7013
7685
  // src/tools/orient-shared.ts
7686
+ function buildCoherenceSection(projectRoot) {
7687
+ try {
7688
+ const root = projectRoot ?? resolveProjectRoot() ?? process.cwd();
7689
+ return checkAndRender(root);
7690
+ } catch {
7691
+ return null;
7692
+ }
7693
+ }
7014
7694
  function runAlignmentCheck(task, activeBets, taskContextHits) {
7015
7695
  const betNames = activeBets.map((b) => b.name);
7016
7696
  const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
@@ -7283,10 +7963,13 @@ function buildInterviewResponse(workspaceName) {
7283
7963
  }
7284
7964
 
7285
7965
  // src/tools/start.ts
7286
- async function tryMarkOriented(agentSessionId) {
7966
+ async function tryMarkOriented(agentSessionId, coherenceSnapshot) {
7287
7967
  if (!agentSessionId) return { oriented: false, orientationStatus: "no_session" };
7288
7968
  try {
7289
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
7969
+ await mcpCall("agent.markOriented", {
7970
+ sessionId: agentSessionId,
7971
+ ...coherenceSnapshot ? { coherenceSnapshot } : {}
7972
+ });
7290
7973
  setSessionOriented(true);
7291
7974
  return { oriented: true, orientationStatus: "complete" };
7292
7975
  } catch {
@@ -7640,6 +8323,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7640
8323
  const isHighReadiness = readiness !== null && readiness.score >= 50;
7641
8324
  const stage = readiness?.stage ?? null;
7642
8325
  const captureBehaviorNote = wsFullCtx.governanceMode === "open" ? "_In Open mode, user-authored captures commit immediately unless you ask me to keep them as drafts._" : "_Everything I capture stays pending until you confirm._";
8326
+ const coherence = buildCoherenceSection();
7643
8327
  lines.push(`# ${wsCtx.workspaceName}`);
7644
8328
  lines.push("_Product Brain is ready._");
7645
8329
  lines.push("");
@@ -7711,6 +8395,9 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7711
8395
  standards: wsStandards.map(mapGovEntry),
7712
8396
  businessRules: wsBusinessRules.map(mapGovEntry)
7713
8397
  }, task));
8398
+ if (coherence) {
8399
+ lines.push(...coherence.lines);
8400
+ }
7714
8401
  const plannedWork = await queryPlannedWork();
7715
8402
  if (hasPlannedWork(plannedWork)) {
7716
8403
  lines.push("");
@@ -7746,6 +8433,9 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7746
8433
  lines.push("");
7747
8434
  }
7748
8435
  lines.push(...buildOperatingProtocol());
8436
+ if (coherence) {
8437
+ lines.push(...coherence.lines);
8438
+ }
7749
8439
  if (priorSessions.length > 0 && !recoveryBlock) {
7750
8440
  const last = priorSessions[0];
7751
8441
  const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
@@ -7767,7 +8457,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
7767
8457
  for (const err of errors) lines.push(`- ${err}`);
7768
8458
  lines.push("");
7769
8459
  }
7770
- const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
8460
+ const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId, coherence?.snapshot);
7771
8461
  const stageLabel = stage ?? "active";
7772
8462
  const gapCount = readiness?.gaps?.length ?? 0;
7773
8463
  const summaryParts = [`Product Brain ready. Stage: ${stageLabel}.`];
@@ -8888,8 +9578,8 @@ Use \`map-slot action=add mapEntryId="${mapEntryId}" slotId="..." ingredientEntr
8888
9578
  }
8889
9579
 
8890
9580
  // src/tools/architecture.ts
8891
- import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
8892
- import { resolve as resolve2, relative, dirname, normalize } from "path";
9581
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
9582
+ import { resolve as resolve4, relative, dirname, normalize } from "path";
8893
9583
  import { z as z19 } from "zod";
8894
9584
  var COLLECTION_SLUG = "architecture";
8895
9585
  var COLLECTION_FIELDS = [
@@ -9224,7 +9914,7 @@ Use \`architecture action=show\` to view the map.`
9224
9914
  };
9225
9915
  }
9226
9916
  if (action === "check") {
9227
- const projectRoot = resolveProjectRoot2();
9917
+ const projectRoot = resolveProjectRoot();
9228
9918
  if (!projectRoot) {
9229
9919
  return {
9230
9920
  content: [{
@@ -9261,18 +9951,6 @@ Use \`architecture action=show\` to view the map.`
9261
9951
  );
9262
9952
  trackWriteTool(archAdminTool);
9263
9953
  }
9264
- function resolveProjectRoot2() {
9265
- const candidates = [
9266
- process.env.WORKSPACE_PATH,
9267
- process.cwd(),
9268
- resolve2(process.cwd(), "..")
9269
- ].filter(Boolean);
9270
- for (const dir of candidates) {
9271
- const resolved = resolve2(dir);
9272
- if (existsSync2(resolve2(resolved, "convex/schema.ts"))) return resolved;
9273
- }
9274
- return null;
9275
- }
9276
9954
  function scanDependencies(projectRoot, layers, nodes) {
9277
9955
  const layerMap = /* @__PURE__ */ new Map();
9278
9956
  for (const l of layers) layerMap.set(l.entryId, l);
@@ -9291,7 +9969,7 @@ function scanDependencies(projectRoot, layers, nodes) {
9291
9969
  const nodeViolations = [];
9292
9970
  let nodeFileCount = 0;
9293
9971
  for (const fp of filePaths) {
9294
- const absPath = resolve2(projectRoot, fp);
9972
+ const absPath = resolve4(projectRoot, fp);
9295
9973
  const files = collectFiles(absPath);
9296
9974
  for (const file of files) {
9297
9975
  nodeFileCount++;
@@ -9368,7 +10046,7 @@ function parseFilePaths(node) {
9368
10046
  return raw.split(",").map((p) => p.trim()).filter(Boolean);
9369
10047
  }
9370
10048
  function collectFiles(absPath) {
9371
- if (!existsSync2(absPath)) return [];
10049
+ if (!existsSync3(absPath)) return [];
9372
10050
  const stat = statSync(absPath);
9373
10051
  if (stat.isFile()) {
9374
10052
  return isScannableFile(absPath) ? [absPath] : [];
@@ -9378,7 +10056,7 @@ function collectFiles(absPath) {
9378
10056
  const walk = (dir) => {
9379
10057
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
9380
10058
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
9381
- const full = resolve2(dir, entry.name);
10059
+ const full = resolve4(dir, entry.name);
9382
10060
  if (entry.isDirectory()) walk(full);
9383
10061
  else if (isScannableFile(full)) results.push(full);
9384
10062
  }
@@ -9391,7 +10069,7 @@ function isScannableFile(p) {
9391
10069
  }
9392
10070
  function parseImports(filePath) {
9393
10071
  try {
9394
- const content = readFileSync2(filePath, "utf-8");
10072
+ const content = readFileSync3(filePath, "utf-8");
9395
10073
  const re = /(?:^|\n)\s*import\s+(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
9396
10074
  const imports = [];
9397
10075
  let match;
@@ -9405,10 +10083,10 @@ function parseImports(filePath) {
9405
10083
  }
9406
10084
  var EXTENSIONS = [".ts", ".js", ".svelte", "/index.ts", "/index.js", "/index.svelte"];
9407
10085
  function tryResolveWithExtension(absPath) {
9408
- if (existsSync2(absPath) && statSync(absPath).isFile()) return absPath;
10086
+ if (existsSync3(absPath) && statSync(absPath).isFile()) return absPath;
9409
10087
  for (const ext of EXTENSIONS) {
9410
10088
  const withExt = absPath + ext;
9411
- if (existsSync2(withExt)) return withExt;
10089
+ if (existsSync3(withExt)) return withExt;
9412
10090
  }
9413
10091
  return null;
9414
10092
  }
@@ -9419,11 +10097,11 @@ function resolveImport(imp, fromFile, root) {
9419
10097
  else if (imp.startsWith("$env/") || imp.startsWith("$app/")) return null;
9420
10098
  else if (imp.startsWith("./") || imp.startsWith("../")) {
9421
10099
  const fromDir = dirname(fromFile);
9422
- const abs2 = resolve2(fromDir, imp);
10100
+ const abs2 = resolve4(fromDir, imp);
9423
10101
  rel = relative(root, abs2);
9424
10102
  }
9425
10103
  if (!rel) return null;
9426
- const abs = resolve2(root, rel);
10104
+ const abs = resolve4(root, rel);
9427
10105
  const actual = tryResolveWithExtension(abs);
9428
10106
  return actual ? relative(root, actual) : rel;
9429
10107
  }
@@ -10125,6 +10803,13 @@ function registerHealthTools(server) {
10125
10803
  const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
10126
10804
  lines.push(`Last session (${date}): ${created} created, ${modified} modified`);
10127
10805
  }
10806
+ let coherenceSnapshot;
10807
+ const coherence = buildCoherenceSection();
10808
+ if (coherence) {
10809
+ lines.push("");
10810
+ lines.push(...coherence.lines);
10811
+ coherenceSnapshot = coherence.snapshot;
10812
+ }
10128
10813
  if (orientEntries) {
10129
10814
  lines.push("");
10130
10815
  if (task) {
@@ -10144,7 +10829,10 @@ function registerHealthTools(server) {
10144
10829
  }
10145
10830
  if (agentSessionId) {
10146
10831
  try {
10147
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
10832
+ await mcpCall("agent.markOriented", {
10833
+ sessionId: agentSessionId,
10834
+ ...coherenceSnapshot ? { coherenceSnapshot } : {}
10835
+ });
10148
10836
  setSessionOriented(true);
10149
10837
  } catch {
10150
10838
  }
@@ -10205,6 +10893,11 @@ function registerHealthTools(server) {
10205
10893
  const stratum = e.stratum ?? "?";
10206
10894
  return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
10207
10895
  };
10896
+ let fullCoherenceSnapshot2;
10897
+ const fullCoherence = buildCoherenceSection();
10898
+ if (fullCoherence) {
10899
+ fullCoherenceSnapshot2 = fullCoherence.snapshot;
10900
+ }
10208
10901
  if (task) {
10209
10902
  lines.push(`**Brain stage: ${orientStage}.** Working on: **${task}**`);
10210
10903
  lines.push("");
@@ -10265,6 +10958,9 @@ function registerHealthTools(server) {
10265
10958
  );
10266
10959
  lines.push(...buildAlignmentCheckLines(result));
10267
10960
  }
10961
+ if (fullCoherence) {
10962
+ lines.push(...fullCoherence.lines);
10963
+ }
10268
10964
  if (orientEntries) {
10269
10965
  if (orientEntries.activeBets?.length > 0) {
10270
10966
  lines.push("## Active bets \u2014 current scope");
@@ -10344,6 +11040,9 @@ function registerHealthTools(server) {
10344
11040
  lines.push("");
10345
11041
  }
10346
11042
  lines.push(...buildOperatingProtocol());
11043
+ if (fullCoherence) {
11044
+ lines.push(...fullCoherence.lines);
11045
+ }
10347
11046
  if (priorSessions.length > 0 && !recoveryBlock) {
10348
11047
  const last = priorSessions[0];
10349
11048
  const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
@@ -10366,7 +11065,10 @@ function registerHealthTools(server) {
10366
11065
  }
10367
11066
  if (agentSessionId) {
10368
11067
  try {
10369
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
11068
+ await mcpCall("agent.markOriented", {
11069
+ sessionId: agentSessionId,
11070
+ ...fullCoherenceSnapshot ? { coherenceSnapshot: fullCoherenceSnapshot } : {}
11071
+ });
10370
11072
  setSessionOriented(true);
10371
11073
  lines.push("---");
10372
11074
  lines.push(`Orientation complete. Session ${agentSessionId}. Write tools available.`);
@@ -10400,16 +11102,16 @@ function registerHealthTools(server) {
10400
11102
  }
10401
11103
 
10402
11104
  // src/resources/index.ts
10403
- import { existsSync as existsSync3 } from "fs";
10404
- import { dirname as dirname2, join, resolve as resolve3 } from "path";
11105
+ import { existsSync as existsSync4 } from "fs";
11106
+ import { dirname as dirname2, join, resolve as resolve5 } from "path";
10405
11107
  import { fileURLToPath } from "url";
10406
11108
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
10407
11109
  var MODULE_DIR = dirname2(fileURLToPath(import.meta.url));
10408
11110
  var UI_VIEW_BASE_CANDIDATES = [
10409
- resolve3(MODULE_DIR, "views"),
10410
- resolve3(MODULE_DIR, "..", "views"),
10411
- resolve3(MODULE_DIR, "..", "..", "dist", "views"),
10412
- resolve3(MODULE_DIR, "..", "..", "..", "mcp-views", "dist")
11111
+ resolve5(MODULE_DIR, "views"),
11112
+ resolve5(MODULE_DIR, "..", "views"),
11113
+ resolve5(MODULE_DIR, "..", "..", "dist", "views"),
11114
+ resolve5(MODULE_DIR, "..", "..", "..", "mcp-views", "dist")
10413
11115
  ];
10414
11116
  function formatEntryMarkdown(entry) {
10415
11117
  const id = entry.entryId ? `${entry.entryId}: ` : "";
@@ -11755,4 +12457,4 @@ export {
11755
12457
  SERVER_VERSION,
11756
12458
  createProductBrainServer
11757
12459
  };
11758
- //# sourceMappingURL=chunk-3HM3XRC7.js.map
12460
+ //# sourceMappingURL=chunk-JOJWCU7A.js.map