@useorgx/wizard 0.1.32 → 0.1.37

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.
Files changed (3) hide show
  1. package/dist/cli.js +1746 -104
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli.ts
4
4
  import * as clack from "@clack/prompts";
5
5
  import { spawnSync as spawnSync3 } from "child_process";
6
- import { readFileSync as readFileSync5 } from "fs";
6
+ import { readFileSync as readFileSync6 } from "fs";
7
7
  import { hostname } from "os";
8
8
  import { resolve } from "path";
9
9
  import { Command } from "commander";
@@ -6235,6 +6235,7 @@ var INSTRUCTION_BOILERPLATE_PATTERN = /^(?:#{1,6}\s*)?(?:agents\.md|instructions
6235
6235
  var IMPERATIVE_BOILERPLATE_PATTERN = /^[-*]\s+(?:if the user|when the user|if mcp|when agents|never|always|do not|don'?t|default to|treat|produce|pick|verify|avoid|match|use exactly|keep system boundaries|quote bracketed|for orgx|when capture tooling|if no meaningful|do \*\*not\*\*)\b/i;
6236
6236
  var CODE_OR_DOC_NOISE_PATTERN = /^(?:[-*]\s*)?(?:https?:\/\/|\/|\.{0,2}\/|[A-Za-z]:\/|`{1,3}|"{1,2}[A-Za-z0-9:_-]+":|\d+\s*$|\d+\t|import\s|export\s|const\s|let\s|var\s|function\s|type\s|interface\s|CREATE\s+TABLE|ALTER\s+TABLE|SELECT\s+|INSERT\s+|UPDATE\s+|DELETE\s+)/i;
6237
6237
  var EMPTY_AUDIT_LABEL_PATTERN = /^(?:[-*]\s*)?(?:decision|artifact|receipt|proof|commitment|next action|follow[- ]?up|outcome|result|impact|roi|economics|open loop|gap|blocker|risk|owner|dri|rollback|quality score)\s*:\s*[\[{(;,]*$/i;
6238
+ var LABEL_VALUE_NOISE_PATTERN = /^(?:[-*]\s*)?(?:decision|artifact|receipt|proof|commitment|next action|follow[- ]?up|outcome|result|impact|roi|economics|open loop|gap|blocker|risk|owner|dri|rollback|quality score)\s*:\s*(?:["']?[\w-]+["']?\s*\|\s*["']?[\w-]+["']?|[\d\s.,%$]+)[,;)]*$/i;
6238
6239
  var TYPE_SIGNATURE_NOISE_PATTERN = /^(?:[-*]\s*)?(?:decision|artifact|receipt|proof|commitment|next action|follow[- ]?up|outcome|result|impact|roi|economics|open loop|gap|blocker|risk|owner|dri|rollback|quality score)\s*:\s*[A-Za-z_$][\w$]*(?:<[^>]+>)?[\s,;)]*$/i;
6239
6240
  var TOOL_CATALOG_NOISE_PATTERN = /^(?:[-*]\s*)?(?:[a-z][\w.:-]*\s*\([^)]+\),?\s*){2,}$/i;
6240
6241
  var BARE_TOOL_SIGNAL_PATTERN = /^(?:\d+\.\s*|[-*]\s*)?(?:mcp__orgx__[\w-]*|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative|get_task_with_context|ship_batch|record_outcome|orgx_free_audit)\s*$/i;
@@ -6340,6 +6341,7 @@ function normalizeAuditRelevantLine(line) {
6340
6341
  if (IMPERATIVE_BOILERPLATE_PATTERN.test(trimmed)) return null;
6341
6342
  if (CODE_OR_DOC_NOISE_PATTERN.test(trimmed)) return null;
6342
6343
  if (EMPTY_AUDIT_LABEL_PATTERN.test(trimmed)) return null;
6344
+ if (LABEL_VALUE_NOISE_PATTERN.test(trimmed)) return null;
6343
6345
  if (TYPE_SIGNATURE_NOISE_PATTERN.test(trimmed)) return null;
6344
6346
  if (TOOL_CATALOG_NOISE_PATTERN.test(trimmed)) return null;
6345
6347
  if ((trimmed.match(/\b[a-z][\w.:-]*\s*\([^)]+\)/g) ?? []).length >= 2) return null;
@@ -6416,19 +6418,32 @@ function readSessionImport(candidate, root, options) {
6416
6418
  const extractor = candidate.source === "codex" ? extractCodexMessageText : extractClaudeMessageText;
6417
6419
  const lines = readFileSync3(candidate.path, "utf8").split(/\r?\n/);
6418
6420
  const relevantLines = [];
6421
+ let messageCount = 0;
6419
6422
  for (const line of lines) {
6420
6423
  const record = parseJsonLine(line);
6421
6424
  const text2 = extractor(record);
6422
6425
  if (!text2) continue;
6426
+ messageCount += 1;
6423
6427
  relevantLines.push(...keepAuditRelevantLines(text2));
6424
6428
  }
6425
6429
  const deduped = [...new Set(relevantLines)].slice(0, 80);
6426
6430
  if (deduped.length === 0) return null;
6427
6431
  const relativePath = relative2(root, candidate.path);
6428
6432
  return {
6429
- sourceId: `${candidate.source}:${basename2(candidate.path, ".jsonl")}`,
6430
- sourceLabel: `${candidate.source === "codex" ? "Codex" : "Claude"} session ${relativePath}`,
6431
- text: deduped.join("\n")
6433
+ import: {
6434
+ sourceId: `${candidate.source}:${basename2(candidate.path, ".jsonl")}`,
6435
+ sourceLabel: `${candidate.source === "codex" ? "Codex" : "Claude"} session ${relativePath}`,
6436
+ metadata: {
6437
+ bytes: stats.size,
6438
+ message_count: messageCount,
6439
+ relative_path: relativePath,
6440
+ retained_line_count: deduped.length,
6441
+ source_client: candidate.source
6442
+ },
6443
+ text: deduped.join("\n")
6444
+ },
6445
+ messageCount,
6446
+ retainedLineCount: deduped.length
6432
6447
  };
6433
6448
  }
6434
6449
  function loadAiSessionImports(options) {
@@ -6443,7 +6458,9 @@ function loadAiSessionImports(options) {
6443
6458
  const imports = [];
6444
6459
  const connectedSources = [];
6445
6460
  const missingSources = [];
6461
+ let retainedLines = 0;
6446
6462
  let scannedFiles = 0;
6463
+ let scannedMessages = 0;
6447
6464
  let skippedFiles = 0;
6448
6465
  for (const source of options.sources) {
6449
6466
  const root = roots[source];
@@ -6456,12 +6473,14 @@ function loadAiSessionImports(options) {
6456
6473
  for (const candidate of candidates) {
6457
6474
  if (importedForSource >= limitPerSource) break;
6458
6475
  scannedFiles += 1;
6459
- const imported = readSessionImport(candidate, root, { maxBytesPerFile });
6460
- if (!imported) {
6476
+ const result = readSessionImport(candidate, root, { maxBytesPerFile });
6477
+ if (!result) {
6461
6478
  skippedFiles += 1;
6462
6479
  continue;
6463
6480
  }
6464
- imports.push(imported);
6481
+ imports.push(result.import);
6482
+ retainedLines += result.retainedLineCount;
6483
+ scannedMessages += result.messageCount;
6465
6484
  importedForSource += 1;
6466
6485
  }
6467
6486
  if (importedForSource > 0) {
@@ -6474,7 +6493,9 @@ function loadAiSessionImports(options) {
6474
6493
  connectedSources,
6475
6494
  imports,
6476
6495
  missingSources,
6496
+ retainedLines,
6477
6497
  scannedFiles,
6498
+ scannedMessages,
6478
6499
  skippedFiles
6479
6500
  };
6480
6501
  }
@@ -6990,6 +7011,7 @@ var WORK_GRAPH_FINDING_TYPES = [
6990
7011
  "business",
6991
7012
  "product_surface",
6992
7013
  "goal",
7014
+ "outcome",
6993
7015
  "initiative_candidate",
6994
7016
  "missed_orchestration_opportunity"
6995
7017
  ];
@@ -7022,6 +7044,10 @@ function sourceClientForImport(source) {
7022
7044
  if (raw.includes("slack")) return "slack";
7023
7045
  if (raw.includes("github")) return "github";
7024
7046
  if (raw.includes("linear")) return "linear";
7047
+ if (raw.includes("gmail") || raw.includes("email")) return "gmail";
7048
+ if (raw.includes("calendar") || raw.includes("meeting")) return "calendar";
7049
+ if (raw.includes("notion")) return "notion";
7050
+ if (raw.includes("doc")) return "docs";
7025
7051
  if (raw.includes("mcp")) return "mcp";
7026
7052
  if (raw.includes("api")) return "api";
7027
7053
  if (raw.includes("manual") || raw.includes("wizard-audit-input")) return "manual";
@@ -7030,7 +7056,7 @@ function sourceClientForImport(source) {
7030
7056
  function normalizeSourceClient(value) {
7031
7057
  if (typeof value !== "string") return "unknown";
7032
7058
  const normalized = value.trim().toLowerCase();
7033
- if (normalized === "codex" || normalized === "claude" || normalized === "claude-code" || normalized === "cursor" || normalized === "openclaw" || normalized === "slack" || normalized === "mcp" || normalized === "github" || normalized === "linear" || normalized === "manual" || normalized === "wizard" || normalized === "api") {
7059
+ if (normalized === "codex" || normalized === "claude" || normalized === "claude-code" || normalized === "cursor" || normalized === "openclaw" || normalized === "slack" || normalized === "mcp" || normalized === "github" || normalized === "linear" || normalized === "gmail" || normalized === "calendar" || normalized === "notion" || normalized === "docs" || normalized === "manual" || normalized === "wizard" || normalized === "api") {
7034
7060
  return normalized;
7035
7061
  }
7036
7062
  if (normalized.includes("claude")) return "claude";
@@ -7039,9 +7065,46 @@ function normalizeSourceClient(value) {
7039
7065
  if (normalized.includes("slack")) return "slack";
7040
7066
  if (normalized.includes("github")) return "github";
7041
7067
  if (normalized.includes("linear")) return "linear";
7068
+ if (normalized.includes("gmail") || normalized.includes("email")) return "gmail";
7069
+ if (normalized.includes("calendar") || normalized.includes("meeting")) return "calendar";
7070
+ if (normalized.includes("notion")) return "notion";
7071
+ if (normalized.includes("doc")) return "docs";
7042
7072
  if (normalized.includes("mcp")) return "mcp";
7043
7073
  return "unknown";
7044
7074
  }
7075
+ function sourceClientFromText(value) {
7076
+ const normalized = value.toLowerCase();
7077
+ if (/\bclaude(?:[- ]code)?\b|\.claude\/projects|claude:/.test(normalized)) return "claude";
7078
+ if (/\bcodex\b|\.codex\/sessions|rollout-/.test(normalized)) return "codex";
7079
+ if (/\bcursor\b/.test(normalized)) return "cursor";
7080
+ if (/\bopenclaw\b/.test(normalized)) return "openclaw";
7081
+ if (/\bslack\b/.test(normalized)) return "slack";
7082
+ if (/\bgithub\b|\bgit:|pull request|commit\b/.test(normalized)) return "github";
7083
+ if (/\blinear\b/.test(normalized)) return "linear";
7084
+ if (/\bgmail\b|\bemail\b/.test(normalized)) return "gmail";
7085
+ if (/\bcalendar\b|\bmeeting\b/.test(normalized)) return "calendar";
7086
+ if (/\bnotion\b/.test(normalized)) return "notion";
7087
+ if (/\bdocs?\b|google drive/.test(normalized)) return "docs";
7088
+ if (/\bmcp\b|mcp__|orgx_emit|scaffold_initiative|ship_batch/.test(normalized)) return "mcp";
7089
+ if (/\borgx api\b|\bapi\b/.test(normalized)) return "api";
7090
+ return "unknown";
7091
+ }
7092
+ function sourceClientForExtractionFinding(extraction, finding) {
7093
+ const explicit = normalizeSourceClient(finding.source_client);
7094
+ if (explicit !== "unknown") return explicit;
7095
+ const inferred = sourceClientFromText([
7096
+ finding.source_id ?? "",
7097
+ finding.source_label ?? "",
7098
+ finding.evidence_ref ?? "",
7099
+ finding.summary ?? ""
7100
+ ].join("\n"));
7101
+ if (inferred !== "unknown") return inferred;
7102
+ return normalizeSourceClient(extraction.source_client);
7103
+ }
7104
+ function arrayOfStrings(value) {
7105
+ if (!Array.isArray(value)) return [];
7106
+ return value.filter((item) => typeof item === "string" && item.trim().length > 0);
7107
+ }
7045
7108
  function normalizeFindingType(value) {
7046
7109
  if (typeof value !== "string") return null;
7047
7110
  const normalized = value.trim().toLowerCase();
@@ -7052,9 +7115,23 @@ function normalizeConfidence(value, fallback = 0.72) {
7052
7115
  return Math.max(0.1, Math.min(0.99, Number(value.toFixed(2))));
7053
7116
  }
7054
7117
  function titleFromText(text2, fallback) {
7055
- const normalized = text2.replace(/^\s*(decision|artifact|commitment|next action|follow[- ]?up|outcome|roi|economics|open loop|gap|blocker|risk|goal)\s*:\s*/i, "").trim();
7118
+ const lower = text2.toLowerCase();
7119
+ if (lower.includes("mcp__orgx__list_entities") && lower.includes("zod")) {
7120
+ return "OrgX MCP list_entities is failing schema validation";
7121
+ }
7122
+ if (lower.includes("scaffold_initiative") && lower.includes("auto_continue") && lower.includes("dispatch")) {
7123
+ return "scaffold_initiative creates ready streams without dispatching agent runs";
7124
+ }
7125
+ if (lower.includes("operation qa loop") && lower.includes("0/") && lower.includes("entities")) {
7126
+ return "OrgX scaffold fails to create Operation QA Loop entities";
7127
+ }
7128
+ if (lower.includes("production-only redirect") || lower.includes("livedemopageclient")) {
7129
+ return "Production route behavior changed without durable approval";
7130
+ }
7131
+ const normalized = text2.replace(/[`*_>#]/g, "").replace(/^\s*[-*\d.)]+\s*/, "").replace(/^\s*(decision|artifact|commitment|next action|follow[- ]?up|outcome|roi|economics|open loop|gap|blocker|risk|goal|summary)\s*:\s*/i, "").replace(/^\s*(fix|fixing|debug|investigate|continue|todo)\s+(the\s+)?(bug|issue|problem)\s*[:.-]?\s*/i, "").replace(/^\s*one\s+bug\s+surfaced\s*:\s*/i, "").replace(/\s+/g, " ").trim();
7056
7132
  const firstSentence = normalized.split(/[.!?]\s/)[0]?.trim() || normalized;
7057
- return (firstSentence || fallback).slice(0, 160);
7133
+ const firstClause = firstSentence.split(/\s[-:;]\s/)[0]?.trim() || firstSentence;
7134
+ return (firstClause || fallback).slice(0, 110);
7058
7135
  }
7059
7136
  function normalizeClientExtractionId(extraction, index) {
7060
7137
  return extraction.extraction_id?.trim() || `${extraction.source_client || "unknown"}:client-extraction:${index + 1}`;
@@ -7074,43 +7151,49 @@ function buildWorkGraphExtractionProtocol() {
7074
7151
  source_search_strategy: [
7075
7152
  {
7076
7153
  id: "decisions",
7077
- lens: "Decision trails",
7154
+ lens: "Decision evidence",
7078
7155
  query: "Find choices, tradeoffs, approvals, rejected options, contradictions, architecture calls, product calls, and decisions that were restated later.",
7079
7156
  return_when: "A choice shaped future work, blocked work, or should become durable organizational memory."
7080
7157
  },
7081
7158
  {
7082
7159
  id: "artifacts",
7083
- lens: "Artifact trails",
7160
+ lens: "Artifact evidence",
7084
7161
  query: "Find created or modified artifacts: PRs, files, docs, designs, prompts, plans, reports, screenshots, deployed routes, tests, and verification receipts.",
7085
7162
  return_when: "The artifact has a source event, proof reference, downstream use, owner, or missing verification."
7086
7163
  },
7087
7164
  {
7088
7165
  id: "blockers",
7089
- lens: "Blocker trails",
7166
+ lens: "Blocker evidence",
7090
7167
  query: "Find failed tool calls, timeouts, rejected validators, missing auth, missing source coverage, repeated unresolved questions, stalled owners, and external blockers.",
7091
7168
  return_when: "The blocker explains why work did not become durable, verified, assigned, or shipped."
7092
7169
  },
7093
7170
  {
7094
7171
  id: "people_businesses",
7095
- lens: "People and business trails",
7096
- query: "Find users, customers, buyers, stakeholders, reviewers, teams, businesses, accounts, and market signals connected to work or decisions.",
7172
+ lens: "People and business context",
7173
+ query: "Find users, customers, buyers, stakeholders, reviewers, teams, businesses, accounts, deal context, support signals, and market signals connected to work or decisions.",
7097
7174
  return_when: "A person or business changes priority, ownership, revenue potential, customer pain, or follow-up urgency."
7098
7175
  },
7176
+ {
7177
+ id: "coordination_sources",
7178
+ lens: "Coordination and calendar/email context",
7179
+ query: "Find Slack threads, emails, meetings, docs, handoffs, approvals, owner changes, follow-ups, and coordination gaps that explain how work moved or stalled outside the AI client.",
7180
+ return_when: "The coordination source proves ownership, urgency, approval, customer context, or a missing source needed for attribution."
7181
+ },
7099
7182
  {
7100
7183
  id: "product_surfaces",
7101
- lens: "Product surface trails",
7184
+ lens: "Product surface coverage",
7102
7185
  query: "Find surfaces such as live rooms, command center, widgets, plugins, wizard flows, public pages, APIs, MCP tools, Slack/GitHub/Linear integrations, and signup/claim flows.",
7103
7186
  return_when: "A surface was changed, verified, blocked, requested, or connected to a goal or artifact."
7104
7187
  },
7105
7188
  {
7106
7189
  id: "agents_tools_sources",
7107
- lens: "Agent/tool/source trails",
7190
+ lens: "Agent, skill, tool, and source usage",
7108
7191
  query: "Find agent runs, subagents, MCP calls, hook lifecycle events, tool availability, tool misses, source coverage, and runtime writeback behavior.",
7109
7192
  return_when: "The event proves whether OrgX was called, skipped, unavailable, or only mentioned in instructions."
7110
7193
  },
7111
7194
  {
7112
7195
  id: "outcomes_roi",
7113
- lens: "Outcome and ROI trails",
7196
+ lens: "Outcome and ROI evidence",
7114
7197
  query: "Find shipped/completed work, test/browser/deploy verification, outcomes, time saved, cost, revenue, customer impact, attribution, and expected lift.",
7115
7198
  return_when: "The outcome can be tied to evidence and a prior decision, artifact, blocker, or source."
7116
7199
  },
@@ -7119,18 +7202,30 @@ function buildWorkGraphExtractionProtocol() {
7119
7202
  lens: "Recurring patterns",
7120
7203
  query: "Find repeated shapes across sessions: trapped decisions, orphaned artifacts, missing owner, repeated work, source gaps, tooling mismatch, unverified outcomes, handoff friction, and unclaimed business signals.",
7121
7204
  return_when: "The same pattern appears across multiple sessions, days, tools, actors, or product surfaces."
7205
+ },
7206
+ {
7207
+ id: "domains",
7208
+ lens: "Domain coverage",
7209
+ query: "Find which domains the work spans: product/UX, agents/runtime, MCP/platform, wizard/CLI, plugin distribution, source integrations, quality verification, GTM, sales, operations, and business leverage.",
7210
+ return_when: "A domain has concrete work evidence, not just a generic mention."
7211
+ },
7212
+ {
7213
+ id: "skills_tools",
7214
+ lens: "Commonly invoked skills, agents, and tools",
7215
+ query: "Find named skills, OrgX domain agents, MCP tools, client tools, commands, test tools, and source systems that shaped the work.",
7216
+ return_when: "A skill/tool/source was used, requested, blocked, missed, or repeatedly referenced as part of execution."
7122
7217
  }
7123
7218
  ],
7124
7219
  required_output: {
7125
7220
  schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
7126
7221
  extraction_id: "stable id for this extraction run",
7127
- source_client: "codex | claude | claude-code | cursor | openclaw | slack | mcp | github | linear | manual | api | unknown",
7222
+ source_client: "codex | claude | claude-code | cursor | openclaw | slack | mcp | github | linear | gmail | calendar | notion | docs | manual | api | unknown",
7128
7223
  source_label: "human-readable source label",
7129
7224
  searched_sources: ["session/log/source group names searched"],
7130
7225
  search_queries: [
7131
7226
  {
7132
7227
  id: "decisions",
7133
- lens: "Decision trails",
7228
+ lens: "Decision evidence",
7134
7229
  query: "query actually used",
7135
7230
  result_count: 0
7136
7231
  }
@@ -7143,9 +7238,10 @@ function buildWorkGraphExtractionProtocol() {
7143
7238
  },
7144
7239
  findings: [
7145
7240
  {
7146
- type: "decision | artifact | blocker | person | business | product_surface | goal | action | initiative_candidate | missed_orchestration_opportunity",
7241
+ type: "decision | artifact | blocker | person | business | product_surface | goal | outcome | action | initiative_candidate | missed_orchestration_opportunity",
7147
7242
  title: "short durable title, not a raw line",
7148
7243
  summary: "one sentence explaining why this matters",
7244
+ source_client: "optional override when a blended extractor found evidence from another client",
7149
7245
  source_id: "session/tool/source id",
7150
7246
  source_label: "source label",
7151
7247
  evidence_ref: "stable evidence pointer",
@@ -7164,12 +7260,13 @@ function buildWorkGraphExtractionProtocol() {
7164
7260
  ]
7165
7261
  },
7166
7262
  quality_bar: [
7167
- "Search broadly before summarizing; do not stop at the newest session if older sessions contain recurrence.",
7263
+ "Search broadly through message turns before summarizing; do not stop at the newest session if older sessions contain recurrence.",
7168
7264
  "Every finding must include a source id or evidence ref.",
7169
- "A trail starts only from evidence-bearing work signals, not empty labels, code type signatures, tool catalogs, or guardrail text.",
7265
+ "A durable finding starts only from evidence-bearing work signals, not empty labels, code type signatures, tool catalogs, or guardrail text.",
7170
7266
  "Prefer fewer, higher-confidence findings over many shallow lines.",
7171
7267
  "Include negative evidence when OrgX/MCP was available but not called.",
7172
7268
  "Separate actual tool calls from textual mentions of tool names.",
7269
+ "Extract the range of work domains and common skills/tools so the reader can see what kinds of work the audit actually understood.",
7173
7270
  "Mark confidence below 0.6 when the finding is inferred from weak or single-source evidence.",
7174
7271
  "Do not emit raw JSON tool payloads; summarize the tool result into a blocker/artifact/outcome finding."
7175
7272
  ],
@@ -7184,10 +7281,10 @@ function buildWorkGraphExtractionProtocol() {
7184
7281
  ]
7185
7282
  };
7186
7283
  const prompt = [
7187
- "You are generating an OrgX Work Graph extraction from your own AI-client session/search logs.",
7188
- "Search across available local sessions, transcripts, tool-call logs, hook events, and source indexes using every lens in the schema.",
7284
+ "You are running the OrgX AI-client audit skill from your own session/search logs.",
7285
+ "Search across available local message turns, transcripts, tool-call logs, hook events, and source indexes using every lens in the schema.",
7189
7286
  "Return JSON only. Match the required_output shape exactly. Do not return raw transcripts.",
7190
- "The output will initiate Work Graph Trails, so every finding must be evidence-bearing and useful enough for a human to inspect."
7287
+ "The output will create public-safe OrgX evidence paths, so every finding must be evidence-bearing and useful enough for a human to inspect."
7191
7288
  ].join(" ");
7192
7289
  return {
7193
7290
  schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
@@ -7219,6 +7316,12 @@ function includesAny2(text2, patterns) {
7219
7316
  return patterns.some((pattern) => pattern.test(text2));
7220
7317
  }
7221
7318
  function buildCoverage(imports, connectedSources, missingSources, clientExtractions = []) {
7319
+ const findings = buildClientExtractionFindings(clientExtractions);
7320
+ const sourceClients = sortedUnique([
7321
+ ...imports.map(sourceClientForImport),
7322
+ ...findings.map((finding) => finding.source_client),
7323
+ ...clientExtractions.map((extraction) => normalizeSourceClient(extraction.source_client))
7324
+ ].filter((source) => source !== "unknown"));
7222
7325
  const extractionText = clientExtractions.flatMap((extraction) => [
7223
7326
  extraction.source_client,
7224
7327
  extraction.source_label ?? "",
@@ -7235,15 +7338,170 @@ ${extractionText}`.toLowerCase();
7235
7338
  const mcpObserved = /\bmcp\b|mcp__|tool call|tools\/call|call_tool|orgx_emit_activity/i.test(allText);
7236
7339
  const orgxMcpCalled = /mcp__orgx__|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative/i.test(allText);
7237
7340
  const skillOnlySignal = orgxObserved && !orgxMcpCalled && /\bskill|instructions|agent|workflow\b/i.test(allText);
7341
+ const normalizedConnectedSources = sortedUnique(
7342
+ connectedSources.map((source) => {
7343
+ const client = sourceClientFromText(source);
7344
+ return client === "unknown" ? source : labelForSourceClient(client);
7345
+ })
7346
+ );
7347
+ const inferredConnected = [
7348
+ ...normalizedConnectedSources,
7349
+ ...sourceClients.includes("codex") ? ["Codex sessions"] : [],
7350
+ ...sourceClients.includes("claude") || sourceClients.includes("claude-code") ? ["Claude Code sessions"] : [],
7351
+ ...sourceClients.includes("github") ? ["Git/GitHub proof"] : [],
7352
+ ...sourceClients.includes("mcp") ? ["MCP tool telemetry"] : [],
7353
+ ...sourceClients.includes("slack") ? ["Slack coordination"] : []
7354
+ ];
7355
+ const inferredMissing = [
7356
+ ...missingSources,
7357
+ ...sourceClients.includes("slack") ? [] : ["Slack coordination"],
7358
+ ...sourceClients.includes("github") ? [] : ["GitHub PR/commit proof"],
7359
+ ...allText.includes("hook") || allText.includes("outbox") ? [] : ["Runtime hook outbox replay"]
7360
+ ];
7361
+ const connected = sortedUnique(inferredConnected.filter(Boolean));
7362
+ const missing = sortedUnique(inferredMissing.filter((source) => !connected.includes(source)));
7363
+ const manifests = buildSourceCoverageManifests({
7364
+ clientExtractions,
7365
+ connectedSources: connected,
7366
+ findings,
7367
+ imports,
7368
+ missingSources: missing
7369
+ });
7370
+ const partialCount = manifests.filter((manifest) => manifest.status === "partial").length;
7371
+ const coverageScore = clampScore2(
7372
+ 28 + Math.min(34, connected.length * 7) + Math.min(18, findings.length * 2) + (sourceClients.includes("codex") ? 6 : 0) + (sourceClients.includes("claude") || sourceClients.includes("claude-code") ? 6 : 0) - missing.length * 7 - partialCount * 3
7373
+ );
7238
7374
  return {
7239
- connected: [...connectedSources],
7240
- missing: [...missingSources],
7375
+ connected,
7376
+ missing,
7241
7377
  mcpObserved,
7242
7378
  orgxObserved,
7243
7379
  orgxMcpCalled,
7244
- skillOnlySignal
7380
+ skillOnlySignal,
7381
+ coverage_score: coverageScore,
7382
+ manifests,
7383
+ notes: [
7384
+ "OrgX/MCP writeback is one coverage signal; it is not required for the audit to find useful work.",
7385
+ missing.length > 0 ? `Coverage is limited by missing sources: ${missing.join(", ")}.` : "Connected sources are sufficient for a first-pass operating profile, but still require user review."
7386
+ ]
7245
7387
  };
7246
7388
  }
7389
+ function buildSourceCoverageManifests(input) {
7390
+ const manifests = /* @__PURE__ */ new Map();
7391
+ const addManifest = (manifest) => {
7392
+ const sourceLabel2 = canonicalSourceManifestLabel(manifest);
7393
+ const key = `${manifest.source_client}:${sourceLabel2}`;
7394
+ const previous = manifests.get(key);
7395
+ if (!previous) {
7396
+ manifests.set(key, { ...manifest, source_label: sourceLabel2 });
7397
+ return;
7398
+ }
7399
+ manifests.set(key, {
7400
+ ...previous,
7401
+ source_label: sourceLabel2,
7402
+ status: previous.status === "connected" || manifest.status === "connected" ? "connected" : previous.status === "partial" || manifest.status === "partial" ? "partial" : "missing",
7403
+ searched_sources: sortedUnique([...previous.searched_sources, ...manifest.searched_sources]),
7404
+ searched_session_count: previous.searched_session_count + manifest.searched_session_count,
7405
+ skipped_session_count: previous.skipped_session_count + manifest.skipped_session_count,
7406
+ query_count: previous.query_count + manifest.query_count,
7407
+ finding_count: previous.finding_count + manifest.finding_count,
7408
+ confidence: Math.max(previous.confidence, manifest.confidence),
7409
+ notes: sortedUnique([...previous.notes, ...manifest.notes])
7410
+ });
7411
+ };
7412
+ for (const extraction of input.clientExtractions) {
7413
+ const extractionFindings = buildClientExtractionFindings([extraction]);
7414
+ const byClient = /* @__PURE__ */ new Map();
7415
+ for (const finding of extractionFindings) {
7416
+ byClient.set(finding.source_client, [...byClient.get(finding.source_client) ?? [], finding]);
7417
+ }
7418
+ for (const [sourceClient, findings] of byClient) {
7419
+ const searchedSources = extraction.searched_sources ?? [];
7420
+ addManifest({
7421
+ source_client: sourceClient,
7422
+ source_label: labelForSourceClient(sourceClient),
7423
+ status: findings.length > 0 ? "connected" : "partial",
7424
+ searched_sources: searchedSources,
7425
+ searched_session_count: extraction.extraction_quality?.searched_session_count ?? 0,
7426
+ skipped_session_count: extraction.extraction_quality?.skipped_session_count ?? 0,
7427
+ query_count: extraction.search_queries?.length ?? 0,
7428
+ finding_count: findings.length,
7429
+ confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74),
7430
+ notes: extraction.extraction_quality?.notes ?? []
7431
+ });
7432
+ }
7433
+ }
7434
+ for (const source of input.imports) {
7435
+ const sourceClient = sourceClientForImport(source);
7436
+ addManifest({
7437
+ source_client: sourceClient,
7438
+ source_label: source.sourceLabel,
7439
+ status: "connected",
7440
+ searched_sources: [source.sourceId],
7441
+ searched_session_count: 1,
7442
+ skipped_session_count: 0,
7443
+ query_count: 0,
7444
+ finding_count: input.findings.filter((finding) => finding.source_id === source.sourceId).length,
7445
+ confidence: 0.62,
7446
+ notes: ["Fallback line-level import; lower confidence than client-native extraction."]
7447
+ });
7448
+ }
7449
+ for (const source of input.missingSources) {
7450
+ addManifest({
7451
+ source_client: sourceClientFromText(source),
7452
+ source_label: source,
7453
+ status: "missing",
7454
+ searched_sources: [],
7455
+ searched_session_count: 0,
7456
+ skipped_session_count: 0,
7457
+ query_count: 0,
7458
+ finding_count: 0,
7459
+ confidence: 0,
7460
+ notes: ["Not connected or not searched in this audit pass."]
7461
+ });
7462
+ }
7463
+ return [...manifests.values()].sort((left, right) => {
7464
+ const rank = { connected: 0, partial: 1, missing: 2 };
7465
+ return rank[left.status] - rank[right.status] || right.finding_count - left.finding_count;
7466
+ });
7467
+ }
7468
+ function canonicalSourceManifestLabel(manifest) {
7469
+ if (manifest.source_client === "codex" || manifest.source_client === "claude" || manifest.source_client === "claude-code") {
7470
+ return labelForSourceClient(manifest.source_client);
7471
+ }
7472
+ if (manifest.source_client === "github" || manifest.source_client === "slack" || manifest.source_client === "mcp") {
7473
+ return labelForSourceClient(manifest.source_client);
7474
+ }
7475
+ return manifest.source_label;
7476
+ }
7477
+ function labelForSourceClient(sourceClient) {
7478
+ switch (sourceClient) {
7479
+ case "codex":
7480
+ return "Codex sessions";
7481
+ case "claude":
7482
+ case "claude-code":
7483
+ return "Claude Code sessions";
7484
+ case "mcp":
7485
+ return "MCP tool telemetry";
7486
+ case "github":
7487
+ return "Git/GitHub proof";
7488
+ case "slack":
7489
+ return "Slack coordination";
7490
+ case "linear":
7491
+ return "Linear issues";
7492
+ case "gmail":
7493
+ return "Email coordination";
7494
+ case "calendar":
7495
+ return "Calendar/meeting context";
7496
+ case "wizard":
7497
+ return "Wizard audit layer";
7498
+ case "notion":
7499
+ case "docs":
7500
+ return "Docs";
7501
+ default:
7502
+ return sourceClient.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
7503
+ }
7504
+ }
7247
7505
  function summarizeClientExtractions(clientExtractions) {
7248
7506
  return clientExtractions.map((extraction, index) => {
7249
7507
  const extractionId = normalizeClientExtractionId(extraction, index);
@@ -7252,9 +7510,12 @@ function summarizeClientExtractions(clientExtractions) {
7252
7510
  source_client: normalizeSourceClient(extraction.source_client),
7253
7511
  source_label: extraction.source_label?.trim() || `${extraction.source_client || "Unknown"} AI-client extraction`,
7254
7512
  searched_source_count: extraction.searched_sources?.length ?? 0,
7513
+ searched_session_count: extraction.extraction_quality?.searched_session_count ?? 0,
7514
+ skipped_session_count: extraction.extraction_quality?.skipped_session_count ?? 0,
7255
7515
  query_count: extraction.search_queries?.length ?? 0,
7256
7516
  finding_count: extraction.findings.length,
7257
- confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74)
7517
+ confidence: normalizeConfidence(extraction.extraction_quality?.confidence, 0.74),
7518
+ notes: extraction.extraction_quality?.notes ?? []
7258
7519
  };
7259
7520
  });
7260
7521
  }
@@ -7275,21 +7536,78 @@ function buildClientExtractionEvents(clientExtractions) {
7275
7536
  finding_count: summary.finding_count,
7276
7537
  query_count: summary.query_count,
7277
7538
  searched_source_count: summary.searched_source_count,
7539
+ searched_session_count: summary.searched_session_count,
7540
+ skipped_session_count: summary.skipped_session_count,
7278
7541
  raw_transcript_sent: false
7279
7542
  }
7280
7543
  }));
7281
7544
  }
7545
+ function numberFromImportMetadata(source, key) {
7546
+ const value = source.metadata?.[key];
7547
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
7548
+ }
7549
+ function buildAuditMethod(input) {
7550
+ const extractionSummaries = summarizeClientExtractions(input.clientExtractions);
7551
+ const nativePacks = extractionSummaries.filter((summary) => summary.source_client === "codex" || summary.source_client === "claude" || summary.source_client === "claude-code").map((summary) => ({
7552
+ source_client: summary.source_client,
7553
+ source_label: summary.source_label,
7554
+ searched_session_count: summary.searched_session_count,
7555
+ finding_count: summary.finding_count,
7556
+ confidence: summary.confidence
7557
+ }));
7558
+ const searchedMessages = input.imports.reduce(
7559
+ (total, source) => total + numberFromImportMetadata(source, "message_count"),
7560
+ 0
7561
+ );
7562
+ const retainedLines = input.imports.reduce((total, source) => {
7563
+ const explicit = numberFromImportMetadata(source, "retained_line_count");
7564
+ return total + (explicit || source.text.split(/\r?\n/).filter(Boolean).length);
7565
+ }, 0);
7566
+ const skippedFiles = input.imports.reduce(
7567
+ (total, source) => total + numberFromImportMetadata(source, "skipped_file_count"),
7568
+ 0
7569
+ );
7570
+ const extractionSearchedSessions = extractionSummaries.reduce(
7571
+ (total, summary) => total + summary.searched_session_count,
7572
+ 0
7573
+ );
7574
+ const extractionSkippedSessions = extractionSummaries.reduce(
7575
+ (total, summary) => total + summary.skipped_session_count,
7576
+ 0
7577
+ );
7578
+ const sourceGroups = sortedUnique([
7579
+ ...input.imports.map((source) => source.sourceLabel),
7580
+ ...input.clientExtractions.flatMap((extraction) => extraction.searched_sources ?? [])
7581
+ ]);
7582
+ return {
7583
+ mode: "ai_client_session_search",
7584
+ searched_session_files: input.imports.length + extractionSearchedSessions,
7585
+ skipped_session_files: skippedFiles + extractionSkippedSessions,
7586
+ searched_message_count: searchedMessages,
7587
+ retained_evidence_lines: retainedLines,
7588
+ searched_source_groups: sourceGroups.length,
7589
+ extraction_lenses: input.extractionProtocol.schema.source_search_strategy.map((lens) => lens.lens),
7590
+ client_native_packs: nativePacks,
7591
+ privacy_contract: input.extractionProtocol.schema.privacy_contract,
7592
+ notes: [
7593
+ "The wizard reads local Codex/Claude message turns, filters out guardrails/code noise, then runs native extraction packs over the retained evidence lines.",
7594
+ "Counts describe local files/message turns inspected before redaction; raw transcripts are not stored in the report.",
7595
+ nativePacks.length > 0 ? "Codex/Claude native packs classified session evidence into decisions, artifacts, blockers, domains, skills/tools, and missed writeback signals." : "No native AI-client pack produced findings; this report is fallback-import only."
7596
+ ]
7597
+ };
7598
+ }
7282
7599
  function buildClientExtractionFindings(clientExtractions) {
7283
7600
  const findings = [];
7284
7601
  clientExtractions.forEach((extraction, extractionIndex) => {
7285
7602
  const extractionId = normalizeClientExtractionId(extraction, extractionIndex);
7286
- const sourceClient = normalizeSourceClient(extraction.source_client);
7287
- const sourceLabel = extraction.source_label?.trim() || `${sourceClient} AI-client extraction`;
7603
+ const extractionSourceClient = normalizeSourceClient(extraction.source_client);
7604
+ const sourceLabel2 = extraction.source_label?.trim() || `${extractionSourceClient} AI-client extraction`;
7288
7605
  extraction.findings.forEach((finding, findingIndex) => {
7289
7606
  const type = normalizeFindingType(finding.type);
7290
7607
  const title = finding.title?.trim();
7291
7608
  const summary = finding.summary?.trim();
7292
7609
  if (!type || !title || !summary) return;
7610
+ const sourceClient = sourceClientForExtractionFinding(extraction, finding);
7293
7611
  findings.push({
7294
7612
  type,
7295
7613
  title: title.slice(0, 180),
@@ -7299,7 +7617,7 @@ function buildClientExtractionFindings(clientExtractions) {
7299
7617
  evidence_ref: finding.evidence_ref?.trim() || `${extractionId}:F${findingIndex + 1}`,
7300
7618
  confidence: normalizeConfidence(finding.confidence, normalizeConfidence(extraction.extraction_quality?.confidence, 0.76)),
7301
7619
  metadata: {
7302
- source_label: finding.source_label?.trim() || sourceLabel,
7620
+ source_label: finding.source_label?.trim() || sourceLabel2,
7303
7621
  extraction_id: extractionId,
7304
7622
  schema_version: extraction.schema_version ?? null,
7305
7623
  occurred_at: finding.occurred_at ?? null,
@@ -7313,6 +7631,135 @@ function buildClientExtractionFindings(clientExtractions) {
7313
7631
  });
7314
7632
  return findings;
7315
7633
  }
7634
+ function nativeExtractionFindingType(line) {
7635
+ const lower = line.toLowerCase();
7636
+ if (/^\s*(?:decision|decided|choice|approved|rejected)\s*:/i.test(line)) return "decision";
7637
+ if (/^\s*(?:artifact|receipt|proof)\s*:/i.test(line)) return "artifact";
7638
+ if (/^\s*(?:outcome|result|impact)\s*:/i.test(line)) return "outcome";
7639
+ if (/^\s*(?:roi|economics)\s*:/i.test(line)) return "business";
7640
+ if (/^\s*(?:next action|follow[- ]?up|commitment)\s*:/i.test(line)) return "action";
7641
+ if (/^\s*(?:blocker|risk|gap|open loop|rollback)\s*:/i.test(line)) return lower.includes("source") ? "missed_orchestration_opportunity" : "blocker";
7642
+ if (/\b(?:failed|fails|error|invalid|unavailable|timeout|timed out|not called|not dispatched|missing|broken)\b/i.test(line)) {
7643
+ if (/\b(?:tool|mcp|orgx|writeback|source|hook|outbox)\b/i.test(line)) return "missed_orchestration_opportunity";
7644
+ return "blocker";
7645
+ }
7646
+ if (/\b(?:created|generated|implemented|shipped|verified|passed)\b/i.test(line)) return "artifact";
7647
+ if (/\b(?:owner|dri|assigned)\b/i.test(line)) return "person";
7648
+ if (/\b(?:initiative|workstream|milestone|launch|goal)\b/i.test(line)) return "goal";
7649
+ return null;
7650
+ }
7651
+ function nativeEpisodeType(line) {
7652
+ if (/\b(?:plan|decide|decision|choice|tradeoff|approve|reject)\b/i.test(line)) return "planning";
7653
+ if (/\b(?:implemented|created|generated|edited|file|diff|commit|artifact)\b/i.test(line)) return "implementation";
7654
+ if (/\b(?:failed|fails|error|timeout|invalid|debug|retry|blocked|blocker)\b/i.test(line)) return "debugging";
7655
+ if (/\b(?:verified|passed|test|browser|qa|proof|outcome)\b/i.test(line)) return "verification";
7656
+ return "handoff";
7657
+ }
7658
+ function nativeFindingConfidence(type, line) {
7659
+ let confidence = type === "decision" || type === "artifact" || type === "outcome" ? 0.78 : 0.72;
7660
+ if (/^\s*(?:decision|artifact|blocker|outcome|proof|result|next action|open loop|risk|gap)\s*:/i.test(line)) confidence += 0.08;
7661
+ if (/\b(?:mcp__orgx__|scaffold_initiative|orgx_emit_activity|complete_with_proof|github|codex|claude)\b/i.test(line)) confidence += 0.05;
7662
+ if (line.length < 32) confidence -= 0.1;
7663
+ return normalizeConfidence(confidence, 0.72);
7664
+ }
7665
+ function nativeRelatedEntityNames(line) {
7666
+ const candidates = [
7667
+ ...Array.from(line.matchAll(/`([^`]{3,80})`/g)).map((match) => match[1] ?? ""),
7668
+ ...Array.from(line.matchAll(/\b(?:OrgX|MCP|Codex|Claude|Slack|GitHub|Cursor|ChatGPT|Work Graph|Profile|Scaffold Initiative|Operation QA Loop|Runtime Hook|Eject Bay|Trail Inspector)\b/gi)).map((match) => match[0] ?? "")
7669
+ ];
7670
+ return sortedUnique(
7671
+ candidates.map((candidate) => candidate.trim()).filter((candidate) => candidate.length >= 3).slice(0, 8)
7672
+ );
7673
+ }
7674
+ function supportsNativeExtraction(sourceClient) {
7675
+ return sourceClient === "codex" || sourceClient === "claude" || sourceClient === "claude-code";
7676
+ }
7677
+ function buildNativeAiClientExtractions(imports, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
7678
+ const grouped = /* @__PURE__ */ new Map();
7679
+ for (const source of imports) {
7680
+ const sourceClient = sourceClientForImport(source);
7681
+ if (!supportsNativeExtraction(sourceClient)) continue;
7682
+ const bucket = grouped.get(sourceClient) ?? {
7683
+ findings: [],
7684
+ searchedSources: [],
7685
+ searchedSessionCount: 0,
7686
+ skippedSessionCount: 0
7687
+ };
7688
+ bucket.searchedSources.push(source.sourceId);
7689
+ bucket.searchedSessionCount += 1;
7690
+ source.text.split(/\r?\n/).forEach((line, lineIndex) => {
7691
+ const trimmed = line.trim();
7692
+ if (!trimmed) return;
7693
+ const type = nativeExtractionFindingType(trimmed);
7694
+ if (!type) {
7695
+ bucket.skippedSessionCount += 1;
7696
+ return;
7697
+ }
7698
+ bucket.findings.push({
7699
+ type,
7700
+ title: titleFromText(trimmed, type.replace(/_/g, " ")),
7701
+ summary: trimmed,
7702
+ source_client: sourceClient,
7703
+ source_id: source.sourceId,
7704
+ source_label: source.sourceLabel,
7705
+ evidence_ref: `${source.sourceId}:native:${lineIndex + 1}`,
7706
+ confidence: nativeFindingConfidence(type, trimmed),
7707
+ occurred_at: generatedAt,
7708
+ redacted_verbatim: trimmed.slice(0, 420),
7709
+ privacy_state: "redacted",
7710
+ metadata: {
7711
+ episode_type: nativeEpisodeType(trimmed),
7712
+ related_entity_names: nativeRelatedEntityNames(trimmed),
7713
+ related_source_ids: [source.sourceId],
7714
+ state: type === "blocker" || type === "missed_orchestration_opportunity" ? "blocked" : type === "outcome" || type === "artifact" ? "verified" : "observed"
7715
+ }
7716
+ });
7717
+ });
7718
+ grouped.set(sourceClient, bucket);
7719
+ }
7720
+ return [...grouped.entries()].filter(([, bucket]) => bucket.findings.length > 0).map(([sourceClient, bucket]) => ({
7721
+ schema_version: WORK_GRAPH_EXTRACTION_SCHEMA_VERSION,
7722
+ extraction_id: `${sourceClient}:native-extraction:${shortHash({
7723
+ sources: bucket.searchedSources,
7724
+ findings: bucket.findings.map((finding) => finding.evidence_ref)
7725
+ }, 12)}`,
7726
+ source_client: sourceClient,
7727
+ source_label: `${labelForSourceClient(sourceClient)} native extraction`,
7728
+ searched_sources: sortedUnique(bucket.searchedSources),
7729
+ search_queries: buildWorkGraphExtractionProtocol().schema.source_search_strategy.map((lens) => ({
7730
+ id: lens.id,
7731
+ lens: lens.lens,
7732
+ query: lens.query,
7733
+ result_count: bucket.findings.filter((finding) => {
7734
+ const text2 = `${finding.title ?? ""}
7735
+ ${finding.summary ?? ""}`;
7736
+ if (lens.id === "decisions") return finding.type === "decision";
7737
+ if (lens.id === "artifacts") return finding.type === "artifact";
7738
+ if (lens.id === "blockers") return finding.type === "blocker" || finding.type === "missed_orchestration_opportunity";
7739
+ if (lens.id === "people_businesses") return finding.type === "person" || finding.type === "business";
7740
+ if (lens.id === "product_surfaces") return finding.type === "product_surface" || /\b(surface|profile|wizard|plugin|mcp)\b/i.test(text2);
7741
+ if (lens.id === "agents_tools_sources") return /\b(agent|skill|tool|mcp|codex|claude|github|slack)\b/i.test(text2);
7742
+ if (lens.id === "outcomes_roi") return finding.type === "outcome" || finding.type === "business";
7743
+ if (lens.id === "domains") return nativeRelatedEntityNames(text2).length > 0;
7744
+ if (lens.id === "skills_tools") return /\b(orgx-design|agent|skill|tool|mcp|codex|claude|github|slack)\b/i.test(text2);
7745
+ return bucket.findings.length > 1 ? 1 : 0;
7746
+ }).length
7747
+ })),
7748
+ extraction_quality: {
7749
+ confidence: normalizeConfidence(
7750
+ bucket.findings.reduce((total, finding) => total + (finding.confidence ?? 0.72), 0) / bucket.findings.length,
7751
+ 0.74
7752
+ ),
7753
+ searched_session_count: bucket.searchedSessionCount,
7754
+ skipped_session_count: bucket.skippedSessionCount,
7755
+ notes: [
7756
+ "Auto-generated by orgx-wizard from local AI-client session imports.",
7757
+ "Raw transcripts are excluded; only redacted evidence snippets and source refs are retained."
7758
+ ]
7759
+ },
7760
+ findings: bucket.findings
7761
+ }));
7762
+ }
7316
7763
  function buildDerivedFindings(imports) {
7317
7764
  const findings = [];
7318
7765
  let derivedIndex = 0;
@@ -7361,6 +7808,101 @@ function buildDerivedFindings(imports) {
7361
7808
  }
7362
7809
  return findings;
7363
7810
  }
7811
+ var WORK_GRAPH_DOMAIN_RULES = [
7812
+ {
7813
+ id: "domain:orgx-agents-runtime",
7814
+ label: "OrgX agents and runtime",
7815
+ summary: "Agent orchestration, MCP writeback, hooks, approvals, and runtime governance.",
7816
+ patterns: [/\bagent\b/i, /\bsubagent\b/i, /\bmcp\b/i, /\bhook\b/i, /\bwriteback\b/i, /\borgx_emit\b/i, /\bscaffold_initiative\b/i]
7817
+ },
7818
+ {
7819
+ id: "domain:wizard-cli",
7820
+ label: "Wizard and CLI",
7821
+ summary: "OrgX Wizard setup, audit/profile generation, local release, and onboarding commands.",
7822
+ patterns: [/\bwizard\b/i, /\bcli\b/i, /\bnpm\b/i, /\brelease\b/i, /\bpublish\b/i, /\bwork-graph\b/i, /\bsessions reconcile\b/i]
7823
+ },
7824
+ {
7825
+ id: "domain:public-profile-ux",
7826
+ label: "Public profile and UX",
7827
+ summary: "Shareable readout, claim flow, mobile/desktop layout, evidence inspection, and profile copy.",
7828
+ patterns: [/\bprofile\b/i, /\bpublic readout\b/i, /\bclaim\b/i, /\bmobile\b/i, /\bdesktop\b/i, /\bux\b/i, /\bmirror\b/i, /\btrail inspector\b/i]
7829
+ },
7830
+ {
7831
+ id: "domain:plugins-integrations",
7832
+ label: "Plugins and integrations",
7833
+ summary: "Codex, Claude Code, Cursor/OpenClaw, Slack, GitHub, Linear, and source coverage work.",
7834
+ patterns: [/\bplugin\b/i, /\bcodex\b/i, /\bclaude\b/i, /\bcursor\b/i, /\bopenclaw\b/i, /\bslack\b/i, /\bgithub\b/i, /\blinear\b/i]
7835
+ },
7836
+ {
7837
+ id: "domain:quality-verification",
7838
+ label: "Quality and verification",
7839
+ summary: "Tests, browser QA, type checks, release proof, and stale/fixed verification.",
7840
+ patterns: [/\btest\b/i, /\btypecheck\b/i, /\bqa\b/i, /\bverified\b/i, /\bproof\b/i, /\bplaywright\b/i, /\bbrowser\b/i, /\bci\b/i]
7841
+ },
7842
+ {
7843
+ id: "domain:gtm-business",
7844
+ label: "GTM and business leverage",
7845
+ summary: "ICP framing, pricing/value estimates, outreach, buyers, revenue, and market-facing proof.",
7846
+ patterns: [/\bicp\b/i, /\bbuyer\b/i, /\bpricing\b/i, /\brevenue\b/i, /\bsales\b/i, /\bmarketing\b/i, /\bgtm\b/i, /\boperator leverage\b/i]
7847
+ }
7848
+ ];
7849
+ function buildDomainCoverage(findings) {
7850
+ const signals = WORK_GRAPH_DOMAIN_RULES.map((rule) => {
7851
+ const matched = findings.filter((finding) => {
7852
+ const text2 = `${finding.title}
7853
+ ${finding.summary}
7854
+ ${finding.metadata.redacted_verbatim ?? ""}`;
7855
+ return rule.patterns.some((pattern) => pattern.test(text2));
7856
+ });
7857
+ if (matched.length === 0) return null;
7858
+ return {
7859
+ id: rule.id,
7860
+ label: rule.label,
7861
+ summary: rule.summary,
7862
+ finding_count: matched.length,
7863
+ source_clients: sortedUnique(matched.map((finding) => finding.source_client)),
7864
+ evidence_refs: sortedUnique(matched.map((finding) => finding.evidence_ref)).slice(0, 12),
7865
+ confidence: Number((matched.reduce((total, finding) => total + finding.confidence, 0) / matched.length).toFixed(2))
7866
+ };
7867
+ }).filter((signal) => Boolean(signal));
7868
+ return signals.sort((left, right) => right.finding_count - left.finding_count || right.confidence - left.confidence).slice(0, 8);
7869
+ }
7870
+ var SKILL_TOOL_RULES = [
7871
+ { id: "skill:orgx-design", kind: "skill", label: "orgx-design", pattern: /\borgx-design\b|\$orgx-design/i },
7872
+ { id: "skill:runtime-reporting", kind: "skill", label: "orgx-runtime-reporting", pattern: /\borgx-runtime-reporting\b|runtime reporting/i },
7873
+ { id: "skill:initiative-ops", kind: "skill", label: "orgx-initiative-ops", pattern: /\borgx-initiative-ops\b|initiative ops/i },
7874
+ { id: "agent:engineering", kind: "agent", label: "engineering-agent", pattern: /\bengineering-agent\b|engineering agent/i },
7875
+ { id: "agent:design", kind: "agent", label: "design-agent", pattern: /\bdesign-agent\b|design agent/i },
7876
+ { id: "agent:product", kind: "agent", label: "product-agent", pattern: /\bproduct-agent\b|product agent/i },
7877
+ { id: "agent:orchestrator", kind: "agent", label: "orchestrator-agent", pattern: /\borchestrator-agent\b|orchestrator agent/i },
7878
+ { id: "tool:orgx-list-entities", kind: "mcp_tool", label: "mcp__orgx__list_entities", pattern: /\bmcp__orgx__list_entities\b/i },
7879
+ { id: "tool:scaffold", kind: "mcp_tool", label: "scaffold_initiative", pattern: /\bscaffold_initiative\b/i },
7880
+ { id: "tool:complete-proof", kind: "mcp_tool", label: "complete_with_proof", pattern: /\bcomplete_with_proof\b/i },
7881
+ { id: "tool:ship-batch", kind: "mcp_tool", label: "ship_batch", pattern: /\bship_batch\b/i },
7882
+ { id: "client:codex", kind: "client_tool", label: "Codex", pattern: /\bcodex\b/i },
7883
+ { id: "client:claude", kind: "client_tool", label: "Claude Code", pattern: /\bclaude(?: code)?\b/i },
7884
+ { id: "source:github", kind: "source", label: "GitHub", pattern: /\bgithub\b|\bpull request\b|\bcommit\b/i },
7885
+ { id: "source:slack", kind: "source", label: "Slack", pattern: /\bslack\b/i }
7886
+ ];
7887
+ function buildSkillToolSignals(findings) {
7888
+ return SKILL_TOOL_RULES.map((rule) => {
7889
+ const matched = findings.filter(
7890
+ (finding) => rule.pattern.test(`${finding.title}
7891
+ ${finding.summary}
7892
+ ${finding.metadata.redacted_verbatim ?? ""}`)
7893
+ );
7894
+ if (matched.length === 0) return null;
7895
+ return {
7896
+ id: rule.id,
7897
+ label: rule.label,
7898
+ kind: rule.kind,
7899
+ mention_count: matched.length,
7900
+ source_clients: sortedUnique(matched.map((finding) => finding.source_client)),
7901
+ evidence_refs: sortedUnique(matched.map((finding) => finding.evidence_ref)).slice(0, 12),
7902
+ confidence: Number((matched.reduce((total, finding) => total + finding.confidence, 0) / matched.length).toFixed(2))
7903
+ };
7904
+ }).filter((signal) => Boolean(signal)).sort((left, right) => right.mention_count - left.mention_count || right.confidence - left.confidence).slice(0, 12);
7905
+ }
7364
7906
  function buildWorkGraphEvents(imports) {
7365
7907
  return imports.map((source, index) => ({
7366
7908
  schema_version: WORK_GRAPH_SCHEMA_VERSION,
@@ -7373,6 +7915,7 @@ function buildWorkGraphEvents(imports) {
7373
7915
  metadata: {
7374
7916
  import_index: index,
7375
7917
  line_count: source.text.split(/\r?\n/).filter(Boolean).length,
7918
+ ...source.metadata ?? {},
7376
7919
  raw_transcript_sent: false
7377
7920
  }
7378
7921
  }));
@@ -7466,9 +8009,84 @@ function scoreOpportunity(coverage, findings) {
7466
8009
  orgx_fit: orgxFit
7467
8010
  };
7468
8011
  }
8012
+ function estimateImpactProjection(input) {
8013
+ const { coverage, findings, opportunityScore, patterns, trails } = input;
8014
+ const blockerEvents = trails.filter((trail) => trail.subject_entity_type === "blocker").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
8015
+ const decisionEvents = trails.filter((trail) => trail.subject_entity_type === "decision").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
8016
+ const artifactEvents = trails.filter((trail) => trail.subject_entity_type === "artifact").reduce((total, trail) => total + Math.max(1, trail.events.length), 0);
8017
+ const missedCount = findings.filter((finding) => finding.type === "missed_orchestration_opportunity").length;
8018
+ const repeatedEvents = trails.filter((trail) => trail.events.length > 1).reduce((total, trail) => total + trail.events.length, 0);
8019
+ const rawHours = blockerEvents * 0.75 + decisionEvents * 0.45 + artifactEvents * 0.25 + missedCount * 0.65 + repeatedEvents * 0.35 + coverage.missing.length * 0.4;
8020
+ const hours = Number(Math.max(0.5, Math.min(40, rawHours)).toFixed(1));
8021
+ const acceleration = clampScore2(
8022
+ 6 + Math.round(opportunityScore.automation_potential * 0.22) + patterns.filter((pattern) => pattern.severity === "critical" || pattern.severity === "high").length * 5 + (coverage.manifests?.filter((manifest) => manifest.status === "connected").length ?? 0) * 2
8023
+ );
8024
+ const confidenceInputs = [
8025
+ ...findings.map((finding) => finding.confidence),
8026
+ ...patterns.map((pattern) => pattern.confidence),
8027
+ coverage.coverage_score ? coverage.coverage_score / 100 : 0.55
8028
+ ];
8029
+ const confidence = Number((confidenceInputs.reduce((total, value) => total + value, 0) / Math.max(1, confidenceInputs.length)).toFixed(2));
8030
+ return {
8031
+ time_saved_hours_per_week: hours,
8032
+ acceleration_percent: Math.min(70, acceleration),
8033
+ estimated_monthly_value_usd: Math.round(hours * 4.33 * 200),
8034
+ confidence,
8035
+ basis: [
8036
+ `${blockerEvents} blocker event${blockerEvents === 1 ? "" : "s"} at 45 minutes of reconstruction/coordination each.`,
8037
+ `${decisionEvents} decision event${decisionEvents === 1 ? "" : "s"} at 27 minutes of rediscovery/promotion each.`,
8038
+ `${missedCount} missed orchestration signal${missedCount === 1 ? "" : "s"} at 39 minutes of manual writeback each.`,
8039
+ `${coverage.missing.length} missing source${coverage.missing.length === 1 ? "" : "s"} reducing attribution confidence.`
8040
+ ],
8041
+ assumptions: [
8042
+ "Uses a conservative $200/hour blended founder/operator cost for public-safe value estimates.",
8043
+ "Acceleration estimates the execution lift from turning repeated evidence into automated attribution, decisions, and source connections.",
8044
+ "Impact is directional until the user claims the fingerprint and confirms or corrects the evidence."
8045
+ ]
8046
+ };
8047
+ }
8048
+ function scoreExecutionQuality(input) {
8049
+ const { coverage, findings, impact, patterns, recommendations, trails } = input;
8050
+ const uniqueClients = sortedUnique(findings.map((finding) => finding.source_client));
8051
+ const unknownCount = findings.filter((finding) => finding.source_client === "unknown").length;
8052
+ const multiEventTrails = trails.filter((trail) => trail.events.length > 1).length;
8053
+ const evidenceCoverage = clampScore2(
8054
+ (coverage.coverage_score ?? 50) + Math.min(18, coverage.connected.length * 3) - coverage.missing.length * 4
8055
+ );
8056
+ const sourceAttribution = clampScore2(
8057
+ 34 + uniqueClients.filter((client) => client !== "unknown").length * 10 + (coverage.manifests?.filter((manifest) => manifest.status === "connected").length ?? 0) * 6 - unknownCount * 8
8058
+ );
8059
+ const trailDepth = clampScore2(
8060
+ 25 + (trails.length > 0 ? Math.round(multiEventTrails / trails.length * 45) : 0) + Math.min(20, trails.reduce((total, trail) => total + trail.events.length, 0) * 2)
8061
+ );
8062
+ const insightDepth = clampScore2(
8063
+ 30 + Math.min(28, patterns.length * 8) + Math.min(18, findings.filter((finding) => ["blocker", "decision", "outcome", "missed_orchestration_opportunity"].includes(finding.type)).length * 3)
8064
+ );
8065
+ const actionability = clampScore2(
8066
+ 30 + Math.min(35, recommendations.length * 10) + Math.min(20, recommendations.filter((recommendation) => recommendation.priority === "p0").length * 10)
8067
+ );
8068
+ const impactConfidence = clampScore2(impact.confidence * 100);
8069
+ const overall = clampScore2(
8070
+ evidenceCoverage * 0.22 + sourceAttribution * 0.18 + trailDepth * 0.18 + insightDepth * 0.18 + actionability * 0.14 + impactConfidence * 0.1
8071
+ );
8072
+ return {
8073
+ overall,
8074
+ evidence_coverage: evidenceCoverage,
8075
+ source_attribution: sourceAttribution,
8076
+ trail_depth: trailDepth,
8077
+ insight_depth: insightDepth,
8078
+ actionability,
8079
+ impact_confidence: impactConfidence,
8080
+ notes: [
8081
+ "10/10 requires client-native extraction per source, multi-event chronology, source-specific evidence, and action recommendations tied to impact.",
8082
+ multiEventTrails === trails.length ? "Every evidence path has multiple events." : `${trails.length - multiEventTrails} finding${trails.length - multiEventTrails === 1 ? "" : "s"} still need more chronology before they should be called recurring.`,
8083
+ coverage.missing.length > 0 ? `Missing source coverage: ${coverage.missing.join(", ")}.` : "No required source gaps were declared for this run."
8084
+ ]
8085
+ };
8086
+ }
7469
8087
  function inferFinalState(findings) {
7470
8088
  if (findings.some((finding) => finding.type === "blocker")) return "blocked";
7471
- if (findings.some((finding) => finding.type === "artifact" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
8089
+ if (findings.some((finding) => finding.type === "artifact" || finding.type === "outcome" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
7472
8090
  return "completed";
7473
8091
  }
7474
8092
  if (findings.some((finding) => finding.type === "action" || finding.type === "decision")) return "in_progress";
@@ -7534,6 +8152,8 @@ function entityTypeForFinding(finding) {
7534
8152
  case "goal":
7535
8153
  case "initiative_candidate":
7536
8154
  return "initiative";
8155
+ case "outcome":
8156
+ return "outcome";
7537
8157
  case "action":
7538
8158
  return /\b(outcome|shipped|completed|verified)\b/i.test(finding.summary) ? "outcome" : "task";
7539
8159
  case "missed_orchestration_opportunity":
@@ -7586,6 +8206,8 @@ function eventTypeForFinding(finding) {
7586
8206
  case "goal":
7587
8207
  case "initiative_candidate":
7588
8208
  return "initiative_created";
8209
+ case "outcome":
8210
+ return "outcome_recorded";
7589
8211
  case "action":
7590
8212
  return /\b(outcome|result|impact|roi)\b/i.test(finding.summary) ? "outcome_recorded" : "recommendation_generated";
7591
8213
  case "missed_orchestration_opportunity":
@@ -7596,7 +8218,7 @@ function trailStateForFindings(findings) {
7596
8218
  if (findings.some((finding) => finding.type === "blocker")) return "blocked";
7597
8219
  if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "missing_evidence";
7598
8220
  if (findings.some((finding) => /\b(contradict|conflict)\b/i.test(finding.summary))) return "contradicted";
7599
- if (findings.some((finding) => /\b(verified|proof|passed|complete_with_proof)\b/i.test(finding.summary))) return "verified";
8221
+ if (findings.some((finding) => finding.type === "outcome" || /\b(verified|proof|passed|complete_with_proof)\b/i.test(finding.summary))) return "verified";
7600
8222
  if (findings.some((finding) => finding.type === "decision")) return "inferred";
7601
8223
  return "observed";
7602
8224
  }
@@ -7604,6 +8226,7 @@ function trailValenceForFindings(findings, recurrence) {
7604
8226
  if (findings.some((finding) => finding.type === "blocker")) return recurrence > 1 ? "escalating" : "risk";
7605
8227
  if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "leak";
7606
8228
  if (findings.some((finding) => finding.type === "decision") && recurrence > 1) return "wasteful_recurrence";
8229
+ if (findings.some((finding) => finding.type === "outcome")) return "healthy";
7607
8230
  if (findings.some((finding) => finding.type === "business")) return "opportunity";
7608
8231
  if (findings.some((finding) => finding.type === "artifact")) return "healthy";
7609
8232
  return recurrence > 1 ? "useful_recurrence" : "opportunity";
@@ -7611,35 +8234,93 @@ function trailValenceForFindings(findings, recurrence) {
7611
8234
  function trailShapeForFindings(findings, recurrence) {
7612
8235
  const decisions = findings.filter((finding) => finding.type === "decision").length;
7613
8236
  const artifacts = findings.filter((finding) => finding.type === "artifact").length;
8237
+ if (recurrence <= 1) return "single_signal";
7614
8238
  if (decisions > artifacts && decisions > 0) return "decision_heavy_artifact_light";
7615
8239
  if (artifacts > decisions && artifacts > 0 && decisions === 0) return "artifact_heavy_decision_light";
7616
- if (findings.some((finding) => finding.type === "blocker")) return "accelerating_issue";
8240
+ if (findings.some((finding) => finding.type === "blocker")) return recurrence >= 3 ? "accelerating_issue" : "dense_recent_cluster";
7617
8241
  if (recurrence >= 3) return "chronic_recurrence";
7618
8242
  if (findings.some((finding) => /\b(resurface|again|back|revived|reappeared)\b/i.test(finding.summary))) return "zombie_revival";
7619
- if (findings.some((finding) => /\b(verified|passed|shipped|completed)\b/i.test(finding.summary))) return "healthy_execution";
8243
+ if (findings.some((finding) => finding.type === "outcome" || /\b(verified|passed|shipped|completed)\b/i.test(finding.summary))) return "healthy_execution";
7620
8244
  return "dense_recent_cluster";
7621
8245
  }
8246
+ function findingTimestamp(finding, fallback) {
8247
+ const occurredAt = finding.metadata.occurred_at;
8248
+ return isIsoTimestamp(occurredAt) ? occurredAt : fallback;
8249
+ }
8250
+ function canonicalRelatedToken(finding) {
8251
+ const candidates = [
8252
+ ...arrayOfStrings(finding.metadata.related_source_ids),
8253
+ ...arrayOfStrings(finding.metadata.related_entity_names),
8254
+ finding.source_id,
8255
+ finding.title
8256
+ ];
8257
+ const scored = candidates.map((candidate) => candidate.trim()).filter(Boolean).map((candidate) => {
8258
+ const normalized = candidate.toLowerCase();
8259
+ let score = 0;
8260
+ if (/_/.test(candidate)) score += 6;
8261
+ if (/orgx|mcp|wizard|plugin|runtime|hook|work graph|profile|trace|scaffold|ship|spawn|record|submit|list/i.test(candidate)) score += 5;
8262
+ if (/session|jsonl|rollout|line|current-release/i.test(normalized)) score -= 4;
8263
+ if (candidate.length > 80) score -= 2;
8264
+ return { candidate, score };
8265
+ }).filter((item) => item.score > 0).sort((left, right) => right.score - left.score);
8266
+ return scored[0]?.candidate ?? null;
8267
+ }
8268
+ function groupingKeyForFinding(finding) {
8269
+ const entityType = entityTypeForFinding(finding);
8270
+ const token = canonicalRelatedToken(finding);
8271
+ if (token && ["blocker", "source", "surface", "initiative", "outcome", "task"].includes(entityType)) {
8272
+ return `${entityType}:${slugPart(token)}`;
8273
+ }
8274
+ return `${entityType}:${slugPart(finding.title)}`;
8275
+ }
8276
+ function trailTitleForGroup(entityType, group) {
8277
+ const token = canonicalRelatedToken(group[0]);
8278
+ if (group.length > 1 && token) {
8279
+ const label = token.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
8280
+ if (entityType === "blocker") return `${label} is recurring as an execution blocker`;
8281
+ if (entityType === "source") return `${label} is an attribution coverage gap`;
8282
+ if (entityType === "surface") return `${label} is becoming a product surface evidence path`;
8283
+ if (entityType === "outcome") return `${label} is becoming outcome evidence`;
8284
+ return `${label} is becoming durable work`;
8285
+ }
8286
+ return group[0].title;
8287
+ }
8288
+ function trailSummaryForGroup(group) {
8289
+ if (group.length === 1) return group[0].summary;
8290
+ const sources = sortedUnique(group.map((finding) => labelForSourceClient(finding.source_client)));
8291
+ const states = sortedUnique(
8292
+ group.map((finding) => typeof finding.metadata.state === "string" ? finding.metadata.state : "").filter(Boolean)
8293
+ );
8294
+ return [
8295
+ `${group.length} evidence events connect this finding across ${sources.join(", ")}.`,
8296
+ states.length > 0 ? `Observed states: ${states.join(", ")}.` : "",
8297
+ group[0]?.summary ?? ""
8298
+ ].filter(Boolean).join(" ");
8299
+ }
7622
8300
  function buildWorkGraphTrails(findings, generatedAt) {
7623
8301
  const grouped = /* @__PURE__ */ new Map();
7624
8302
  for (const finding of findings) {
7625
- const entityType = entityTypeForFinding(finding);
7626
- const key = `${entityType}:${slugPart(finding.title)}`;
8303
+ const key = groupingKeyForFinding(finding);
7627
8304
  grouped.set(key, [...grouped.get(key) ?? [], finding]);
7628
8305
  }
7629
8306
  return [...grouped.entries()].map(([key, group], index) => {
7630
- const first = group[0];
8307
+ const sortedGroup = [...group].sort(
8308
+ (left, right) => findingTimestamp(left, generatedAt).localeCompare(findingTimestamp(right, generatedAt))
8309
+ );
8310
+ const first = sortedGroup[0];
7631
8311
  const entityType = entityTypeForFinding(first);
7632
8312
  const entityId = `${entityType}:${shortHash(key, 12)}`;
7633
- const trailId = `trail:${shortHash({ key, evidence: group.map((finding) => finding.evidence_ref) }, 14)}`;
7634
- const recurrence = group.length;
7635
- const evidenceRefs = sortedUnique(group.map((finding) => finding.evidence_ref));
7636
- const events = group.map((finding, eventIndex) => ({
8313
+ const trailId = `trail:${shortHash({ key, evidence: sortedGroup.map((finding) => finding.evidence_ref) }, 14)}`;
8314
+ const recurrence = sortedGroup.length;
8315
+ const evidenceRefs = sortedUnique(sortedGroup.map((finding) => finding.evidence_ref));
8316
+ const events = sortedGroup.map((finding, eventIndex) => ({
7637
8317
  id: `${trailId}:event:${eventIndex + 1}`,
7638
8318
  trail_id: trailId,
7639
8319
  event_type: eventTypeForFinding(finding),
7640
8320
  entity_id: entityId,
7641
8321
  entity_type: entityType,
7642
- timestamp: generatedAt,
8322
+ timestamp: findingTimestamp(finding, generatedAt),
8323
+ ...typeof finding.metadata.actor_id === "string" ? { actor_id: finding.metadata.actor_id } : {},
7643
8324
  source_id: finding.source_id,
7644
8325
  source_type: finding.source_client,
7645
8326
  redacted_verbatim: finding.summary.slice(0, 320),
@@ -7656,28 +8337,30 @@ function buildWorkGraphTrails(findings, generatedAt) {
7656
8337
  confidence: Math.min(0.92, Math.max(0.58, event.confidence - 0.04)),
7657
8338
  evidence_refs: event.evidence_refs
7658
8339
  }));
7659
- const confidence = group.reduce((total, finding) => total + finding.confidence, 0) / group.length;
8340
+ const confidence = sortedGroup.reduce((total, finding) => total + finding.confidence, 0) / sortedGroup.length;
8341
+ const createdAt = events[0]?.timestamp ?? generatedAt;
8342
+ const updatedAt = events[events.length - 1]?.timestamp ?? generatedAt;
7660
8343
  return {
7661
8344
  id: trailId,
7662
8345
  kind: trailKindForEntity(entityType),
7663
- title: first.title,
7664
- summary: first.summary,
8346
+ title: trailTitleForGroup(entityType, sortedGroup),
8347
+ summary: trailSummaryForGroup(sortedGroup),
7665
8348
  subject_entity_id: entityId,
7666
8349
  subject_entity_type: entityType,
7667
- state: trailStateForFindings(group),
7668
- valence: trailValenceForFindings(group, recurrence),
8350
+ state: trailStateForFindings(sortedGroup),
8351
+ valence: trailValenceForFindings(sortedGroup, recurrence),
7669
8352
  confidence: Number(confidence.toFixed(2)),
7670
- recurrence_score: Math.min(100, recurrence * 28 + evidenceRefs.length * 6),
8353
+ recurrence_score: recurrence <= 1 ? Math.min(24, evidenceRefs.length * 8 + 10) : Math.min(100, recurrence * 24 + evidenceRefs.length * 7),
7671
8354
  impact_score: Math.min(100, Math.round(first.confidence * 70) + recurrence * 8 + (index < 4 ? 10 : 0)),
7672
8355
  privacy_state: "redacted",
7673
8356
  events,
7674
8357
  edges,
7675
8358
  evidence_refs: evidenceRefs,
7676
- blocker_ids: group.filter((finding) => finding.type === "blocker").map((finding) => finding.evidence_ref),
8359
+ blocker_ids: sortedGroup.filter((finding) => finding.type === "blocker").map((finding) => finding.evidence_ref),
7677
8360
  recommendation_ids: [],
7678
- created_at: generatedAt,
7679
- updated_at: generatedAt,
7680
- shape: trailShapeForFindings(group, recurrence)
8361
+ created_at: createdAt,
8362
+ updated_at: updatedAt,
8363
+ shape: trailShapeForFindings(sortedGroup, recurrence)
7681
8364
  };
7682
8365
  });
7683
8366
  }
@@ -7702,7 +8385,7 @@ function buildRecurringPatterns(coverage, findings, trails) {
7702
8385
  patterns.push({
7703
8386
  id: "pattern:trapped-decision",
7704
8387
  title: "Decisions are being made without durable OrgX writeback",
7705
- description: `${decisionTrails.length} decision trail${decisionTrails.length === 1 ? "" : "s"} appeared while no OrgX MCP write was detected.`,
8388
+ description: `${decisionTrails.length} decision finding${decisionTrails.length === 1 ? "" : "s"} appeared while no OrgX MCP write was detected.`,
7706
8389
  pattern_type: "trapped_decision",
7707
8390
  affected_trail_ids: decisionTrails.map((trail) => trail.id),
7708
8391
  affected_entity_ids: decisionTrails.map((trail) => trail.subject_entity_id),
@@ -7718,7 +8401,7 @@ function buildRecurringPatterns(coverage, findings, trails) {
7718
8401
  patterns.push({
7719
8402
  id: "pattern:orphaned-artifact",
7720
8403
  title: "Artifacts do not have visible ownership",
7721
- description: `${artifactTrails.length} artifact trail${artifactTrails.length === 1 ? "" : "s"} appeared without a clear owner trail.`,
8404
+ description: `${artifactTrails.length} artifact finding${artifactTrails.length === 1 ? "" : "s"} appeared without a clear owner-visible record.`,
7722
8405
  pattern_type: "orphaned_artifact",
7723
8406
  affected_trail_ids: artifactTrails.map((trail) => trail.id),
7724
8407
  affected_entity_ids: artifactTrails.map((trail) => trail.subject_entity_id),
@@ -7767,7 +8450,7 @@ function buildRecurringPatterns(coverage, findings, trails) {
7767
8450
  patterns.push({
7768
8451
  id: "pattern:repeated-work",
7769
8452
  title: "The same work shape is recurring",
7770
- description: `${highRecurrence.length} trail${highRecurrence.length === 1 ? "" : "s"} repeat strongly enough to deserve durable operating memory.`,
8453
+ description: `${highRecurrence.length} finding${highRecurrence.length === 1 ? "" : "s"} repeat strongly enough to deserve durable operating memory.`,
7771
8454
  pattern_type: "repeated_work",
7772
8455
  affected_trail_ids: highRecurrence.map((trail) => trail.id),
7773
8456
  affected_entity_ids: highRecurrence.map((trail) => trail.subject_entity_id),
@@ -7783,7 +8466,7 @@ function buildRecurringPatterns(coverage, findings, trails) {
7783
8466
  patterns.push({
7784
8467
  id: "pattern:business-signal-unclaimed",
7785
8468
  title: "Business signal has not become a launchable initiative",
7786
- description: `${businessTrails.length} business trail${businessTrails.length === 1 ? "" : "s"} appeared without a matching initiative candidate.`,
8469
+ description: `${businessTrails.length} business finding${businessTrails.length === 1 ? "" : "s"} appeared without a matching initiative candidate.`,
7787
8470
  pattern_type: "business_signal_unclaimed",
7788
8471
  affected_trail_ids: businessTrails.map((trail) => trail.id),
7789
8472
  affected_entity_ids: businessTrails.map((trail) => trail.subject_entity_id),
@@ -7796,10 +8479,13 @@ function buildRecurringPatterns(coverage, findings, trails) {
7796
8479
  });
7797
8480
  }
7798
8481
  if (blockerTrails.length > 0) {
8482
+ const topBlockerTrail = [...blockerTrails].sort(
8483
+ (left, right) => right.events.length - left.events.length || right.impact_score - left.impact_score
8484
+ )[0];
7799
8485
  patterns.push({
7800
8486
  id: "pattern:handoff-friction",
7801
- title: "Blockers are becoming handoff friction",
7802
- description: `${blockerTrails.length} blocker trail${blockerTrails.length === 1 ? "" : "s"} need owner-visible resolution.`,
8487
+ title: topBlockerTrail && topBlockerTrail.events.length > 1 ? `${topBlockerTrail.title} and needs owner-visible resolution` : "Blockers are becoming handoff friction",
8488
+ description: `${blockerTrails.length} blocker finding${blockerTrails.length === 1 ? "" : "s"} need owner-visible resolution.`,
7803
8489
  pattern_type: "handoff_friction",
7804
8490
  affected_trail_ids: blockerTrails.map((trail) => trail.id),
7805
8491
  affected_entity_ids: blockerTrails.map((trail) => trail.subject_entity_id),
@@ -7852,7 +8538,7 @@ function buildTrailRecommendations(patterns, trails) {
7852
8538
  add({
7853
8539
  id: "recommendation:connect-source",
7854
8540
  title: "Connect missing source coverage",
7855
- summary: "Close the evidence gap by connecting the coordination or proof sources where trails terminate.",
8541
+ summary: "Close the evidence gap by connecting the coordination or proof sources where findings lose ownership or verification context.",
7856
8542
  action_type: "connect_source",
7857
8543
  trail_ids: pattern.affected_trail_ids,
7858
8544
  evidence_refs: evidenceRefs,
@@ -7872,10 +8558,22 @@ function buildTrailRecommendations(patterns, trails) {
7872
8558
  expected_lift: "+continuous attribution",
7873
8559
  confidence: pattern.confidence
7874
8560
  });
8561
+ } else if (pattern.pattern_type === "handoff_friction") {
8562
+ add({
8563
+ id: "recommendation:resolve-blocker-handoff",
8564
+ title: "Resolve the blocker handoff",
8565
+ summary: "Promote the recurring blocker path into assigned work with proof requirements and a clear owner.",
8566
+ action_type: "assign_owner",
8567
+ trail_ids: pattern.affected_trail_ids,
8568
+ evidence_refs: evidenceRefs,
8569
+ priority: pattern.severity === "critical" || pattern.severity === "high" ? "p0" : "p1",
8570
+ expected_lift: "+execution continuity",
8571
+ confidence: pattern.confidence
8572
+ });
7875
8573
  } else if (pattern.pattern_type === "business_signal_unclaimed" || pattern.pattern_type === "repeated_work") {
7876
8574
  add({
7877
8575
  id: "recommendation:launch-initiative",
7878
- title: "Launch from this trail",
8576
+ title: "Launch from this evidence",
7879
8577
  summary: "Convert the highest-recurring evidence path into an OrgX initiative with proof requirements.",
7880
8578
  action_type: "launch_initiative",
7881
8579
  trail_ids: pattern.affected_trail_ids,
@@ -7890,7 +8588,7 @@ function buildTrailRecommendations(patterns, trails) {
7890
8588
  const topTrail = [...trails].sort((a, b) => b.impact_score - a.impact_score)[0];
7891
8589
  add({
7892
8590
  id: "recommendation:inspect-top-trail",
7893
- title: "Inspect the strongest trail",
8591
+ title: "Inspect the strongest finding",
7894
8592
  summary: "Review the highest-confidence evidence path and decide whether it should become durable OrgX memory.",
7895
8593
  action_type: "launch_initiative",
7896
8594
  trail_ids: [topTrail.id],
@@ -7900,44 +8598,64 @@ function buildTrailRecommendations(patterns, trails) {
7900
8598
  confidence: topTrail.confidence
7901
8599
  });
7902
8600
  }
7903
- return recommendations.slice(0, 5);
8601
+ const priorityRank = { p0: 0, p1: 1, p2: 2 };
8602
+ return recommendations.sort(
8603
+ (left, right) => priorityRank[left.priority] - priorityRank[right.priority] || right.confidence - left.confidence
8604
+ ).slice(0, 5);
7904
8605
  }
7905
8606
  function buildWorkGraphMirror(input) {
7906
- const { coverage, generatedAt, patterns, recommendations, trails } = input;
7907
- const topTrail = [...trails].sort((a, b) => b.impact_score - a.impact_score)[0];
8607
+ const { auditMethod, coverage, domains = [], generatedAt, impact, patterns, recommendations, skillToolSignals = [], trails } = input;
8608
+ const topTrail = selectPublicTopTrail(trails);
7908
8609
  const topPattern = [...patterns].sort((a, b) => b.recurrence_count - a.recurrence_count)[0];
7909
8610
  const decisionCount = trailsForType(trails, "decision").length;
7910
8611
  const artifactCount = trailsForType(trails, "artifact").length;
7911
8612
  const blockerCount = trailsForType(trails, "blocker").length;
7912
8613
  const sourceGapCount = coverage.missing.length;
7913
- const headline = topPattern ? topPattern.title : topTrail ? `${topTrail.title} is the clearest work trail` : "Your work is leaving an operating trail";
8614
+ const sourceClients = sortedUnique(
8615
+ trails.flatMap((trail) => trail.events.map((event) => event.source_type))
8616
+ );
8617
+ const sourcePhrase = sourceClients.length > 0 ? sourceClients.map(labelForSourceClient).join(", ") : coverage.connected.join(", ") || "local sources";
8618
+ const headline = blockerCount > 0 ? "Your AI work is shipping, but the execution record is incomplete" : sourceGapCount > 0 ? "Your AI work is visible, but the proof chain is incomplete" : topPattern ? "Your work has a recurring operating pattern worth preserving" : topTrail ? "Your work is becoming an operating profile" : "Your work is leaving evidence OrgX can organize";
8619
+ const domainPhrase = domains.length > 0 ? domains.slice(0, 4).map((domain) => domain.label).join(", ") : "the connected work surface";
8620
+ const toolPhrase = skillToolSignals.length > 0 ? skillToolSignals.slice(0, 4).map((signal) => signal.label).join(", ") : "client sessions and available tools";
7914
8621
  const primaryClaimRefs = topTrail?.evidence_refs ?? [];
7915
8622
  const claims = [
7916
8623
  {
7917
8624
  id: "mirror:trail-count",
7918
- text: `${trails.length} trails were detected across ${coverage.connected.length} connected source${coverage.connected.length === 1 ? "" : "s"}.`,
8625
+ text: `${trails.length} evidence-backed finding${trails.length === 1 ? "" : "s"} were detected across ${coverage.connected.length} connected source${coverage.connected.length === 1 ? "" : "s"}.`,
7919
8626
  evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 6),
7920
8627
  confidence: trails.length > 0 ? 0.82 : 0.55
7921
8628
  },
7922
8629
  {
7923
8630
  id: "mirror:decision-artifact-balance",
7924
- text: `${decisionCount} decision trail${decisionCount === 1 ? "" : "s"} and ${artifactCount} artifact trail${artifactCount === 1 ? "" : "s"} were found.`,
8631
+ text: `${decisionCount} decision finding${decisionCount === 1 ? "" : "s"} and ${artifactCount} artifact finding${artifactCount === 1 ? "" : "s"} were found.`,
7925
8632
  evidence_refs: trails.filter((trail) => trail.subject_entity_type === "decision" || trail.subject_entity_type === "artifact").flatMap((trail) => trail.evidence_refs).slice(0, 6),
7926
8633
  confidence: 0.78
7927
8634
  },
7928
8635
  {
7929
8636
  id: "mirror:missing-sources",
7930
- text: sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit attribution depth.` : "The connected sources are enough for a first operating profile.",
8637
+ text: sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit attribution depth.` : "The connected sources are enough for a first operating profile, pending human review.",
7931
8638
  evidence_refs: trailsForType(trails, "source").flatMap((trail) => trail.evidence_refs).slice(0, 4),
7932
8639
  confidence: sourceGapCount > 0 ? 0.76 : 0.66
7933
- }
8640
+ },
8641
+ ...impact ? [{
8642
+ id: "mirror:impact",
8643
+ text: `${impact.time_saved_hours_per_week} hours/week and +${impact.acceleration_percent}% execution acceleration are recoverable if the top findings get owner, proof, and writeback.`,
8644
+ evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 6),
8645
+ confidence: impact.confidence
8646
+ }] : []
7934
8647
  ];
8648
+ const searchClaim = auditMethod ? `OrgX searched ${auditMethod.searched_session_files} AI-client session files and ${auditMethod.searched_message_count} message turns across ${sourcePhrase}; it retained ${auditMethod.retained_evidence_lines} evidence lines and excluded raw transcripts.` : `OrgX searched your AI-client session evidence across ${sourcePhrase} without requiring OrgX tool calls to be present.`;
8649
+ const topIssue = topTrail ? `The highest-risk issue is ${topTrail.title.toLowerCase()}.` : topPattern ? `The highest-risk pattern is ${topPattern.title.toLowerCase()}.` : "The first profile is forming from sparse evidence and should be reviewed before promotion.";
7935
8650
  const body = [
7936
- `OrgX found ${trails.length} evidence trail${trails.length === 1 ? "" : "s"} across ${coverage.connected.join(", ") || "local sources"}.`,
7937
- `The strongest signal is ${topPattern ? topPattern.title.toLowerCase() : topTrail?.title ?? "still forming"}.`,
7938
- blockerCount > 0 ? `${blockerCount} blocker trail${blockerCount === 1 ? "" : "s"} need promotion into owner-visible work.` : "The healthiest trails already connect evidence to action.",
7939
- recommendations[0] ? `The next durable move is: ${recommendations[0].title}.` : "The next move is to inspect the highest-confidence trail before publishing it."
7940
- ].join(" ");
8651
+ searchClaim,
8652
+ `It found ${trails.length} evidence-backed finding${trails.length === 1 ? "" : "s"} across ${domainPhrase}, with ${toolPhrase} showing up as the strongest skills, tools, or sources.`,
8653
+ topIssue,
8654
+ blockerCount > 0 ? `${blockerCount} blocker finding${blockerCount === 1 ? "" : "s"} show work returning as new work instead of becoming owner-visible resolution.` : `${decisionCount} decision finding${decisionCount === 1 ? "" : "s"} and ${artifactCount} artifact finding${artifactCount === 1 ? "" : "s"} show where work can become durable.`,
8655
+ sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit trust: ownership, handoffs, GitHub proof, Slack coordination, or later verification.` : "The connected sources are enough for a first public-safe profile, pending human review.",
8656
+ impact ? `Left unresolved, the profile estimates ${impact.time_saved_hours_per_week} recoverable hours/week and about $${impact.estimated_monthly_value_usd.toLocaleString("en-US")}/month in operator leverage.` : "",
8657
+ recommendations[0] ? `The next repair is concrete: ${recommendations[0].title}.` : "The next move is to inspect the highest-confidence finding before publishing it."
8658
+ ].filter(Boolean).join(" ");
7941
8659
  return {
7942
8660
  headline,
7943
8661
  body,
@@ -7947,14 +8665,58 @@ function buildWorkGraphMirror(input) {
7947
8665
  generated_at: generatedAt
7948
8666
  };
7949
8667
  }
8668
+ function selectPublicTopTrail(trails) {
8669
+ const ranked = [...trails].sort((left, right) => publicTrailRank(right) - publicTrailRank(left));
8670
+ return ranked[0];
8671
+ }
8672
+ function publicTrailRank(trail) {
8673
+ const title = trail.title.trim();
8674
+ let score = trail.impact_score;
8675
+ if (trail.events.length > 1) score += 10;
8676
+ if (trail.subject_entity_type === "blocker") score += 6;
8677
+ if (/\b(?:scaffold|ship_batch|mcp__orgx__list_entities|list entities|schema validation|route behavior|dispatching agent runs|operation qa loop)\b/i.test(title)) {
8678
+ score += 14;
8679
+ }
8680
+ if (/^(?:[-*✓{`'"]|\d+[,.]?$)/.test(title)) score -= 32;
8681
+ if (/[{}|;]/.test(title)) score -= 28;
8682
+ if (title.length < 22) score -= 26;
8683
+ if (/^(?:result|summary)\s*:/i.test(title)) score -= 18;
8684
+ return score;
8685
+ }
7950
8686
  function buildTensionMetrics(input) {
7951
- const { coverage, patterns, trails } = input;
8687
+ const { coverage, impact, patterns, quality, trails } = input;
7952
8688
  const decisionTrails = trailsForType(trails, "decision");
7953
8689
  const blockerTrails = trailsForType(trails, "blocker");
7954
8690
  const artifactTrails = trailsForType(trails, "artifact");
7955
8691
  const missingSourceTrails = trailsForType(trails, "source");
7956
8692
  const topReady = trails.filter((trail) => trail.impact_score >= 70 && trail.confidence >= 0.75);
7957
8693
  return [
8694
+ ...quality ? [{
8695
+ id: "tension:quality-score",
8696
+ label: "quality score",
8697
+ value: `${quality.overall}/100`,
8698
+ tone: quality.overall >= 82 ? "good" : quality.overall >= 65 ? "warning" : "danger",
8699
+ trail_ids: trails.slice(0, 8).map((trail) => trail.id),
8700
+ evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
8701
+ explanation: "Weighted audit quality across evidence coverage, attribution, chronology depth, insight depth, actionability, and impact confidence."
8702
+ }] : [],
8703
+ ...impact ? [{
8704
+ id: "tension:time-saved",
8705
+ label: "hours recoverable",
8706
+ value: `${impact.time_saved_hours_per_week}h/wk`,
8707
+ tone: impact.time_saved_hours_per_week >= 4 ? "warning" : "muted",
8708
+ trail_ids: patterns.flatMap((pattern) => pattern.affected_trail_ids).slice(0, 8),
8709
+ evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
8710
+ explanation: "Estimated time recovered by promoting repeated decisions, blockers, artifacts, and writeback gaps into durable operating memory."
8711
+ }, {
8712
+ id: "tension:acceleration",
8713
+ label: "acceleration",
8714
+ value: `+${impact.acceleration_percent}%`,
8715
+ tone: impact.acceleration_percent >= 30 ? "good" : "muted",
8716
+ trail_ids: patterns.flatMap((pattern) => pattern.affected_trail_ids).slice(0, 8),
8717
+ evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
8718
+ explanation: "Directional execution lift from connecting source evidence, writeback, owners, and action recommendations."
8719
+ }] : [],
7958
8720
  {
7959
8721
  id: "tension:work-leaks",
7960
8722
  label: "work leaks",
@@ -7971,7 +8733,7 @@ function buildTensionMetrics(input) {
7971
8733
  tone: decisionTrails.length > 0 && !coverage.orgxMcpCalled ? "danger" : "muted",
7972
8734
  trail_ids: decisionTrails.map((trail) => trail.id),
7973
8735
  evidence_refs: decisionTrails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
7974
- explanation: "Decision trails that have not been promoted into durable OrgX records."
8736
+ explanation: "Decision evidence that has not been promoted into durable OrgX records."
7975
8737
  },
7976
8738
  {
7977
8739
  id: "tension:artifacts-orphaned",
@@ -7998,7 +8760,7 @@ function buildTensionMetrics(input) {
7998
8760
  tone: topReady.length > 0 && blockerTrails.length === 0 ? "good" : "muted",
7999
8761
  trail_ids: topReady.map((trail) => trail.id).slice(0, 6),
8000
8762
  evidence_refs: topReady.flatMap((trail) => trail.evidence_refs).slice(0, 8),
8001
- explanation: "High-confidence trails that can become initiatives, decisions, artifacts, or owner-visible follow-ups."
8763
+ explanation: "High-confidence findings that can become initiatives, decisions, artifacts, or owner-visible follow-ups."
8002
8764
  }
8003
8765
  ];
8004
8766
  }
@@ -8011,6 +8773,317 @@ function countFindingsByType(findings) {
8011
8773
  Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
8012
8774
  );
8013
8775
  }
8776
+ function isIsoTimestamp(value) {
8777
+ return typeof value === "string" && !Number.isNaN(Date.parse(value));
8778
+ }
8779
+ function redactionStateForFinding(finding) {
8780
+ const privacy = typeof finding.metadata.privacy_state === "string" ? finding.metadata.privacy_state : "";
8781
+ if (privacy === "private") return "private";
8782
+ if (privacy === "redacted") return "redacted";
8783
+ return "public_summary";
8784
+ }
8785
+ function attributionKindForFinding(type) {
8786
+ switch (type) {
8787
+ case "action":
8788
+ case "blocker":
8789
+ case "outcome":
8790
+ return "action";
8791
+ case "decision":
8792
+ case "artifact":
8793
+ case "person":
8794
+ case "business":
8795
+ case "product_surface":
8796
+ case "goal":
8797
+ return type;
8798
+ case "initiative_candidate":
8799
+ return "goal";
8800
+ case "missed_orchestration_opportunity":
8801
+ return "source";
8802
+ default:
8803
+ return null;
8804
+ }
8805
+ }
8806
+ function attributionEventTypeForFinding(finding) {
8807
+ if (finding.source_client === "slack") return "slack_message";
8808
+ if (finding.source_client === "github") return "github_event";
8809
+ if (finding.source_client === "linear") return "linear_event";
8810
+ if (finding.source_client === "gmail") return "gmail_message";
8811
+ if (finding.source_client === "calendar") return "calendar_event";
8812
+ if (finding.source_client === "notion" || finding.source_client === "docs") return "doc_signal";
8813
+ if (finding.source_client === "mcp") return "mcp_call";
8814
+ if (finding.type === "artifact") return "repo_artifact";
8815
+ if (finding.type === "missed_orchestration_opportunity") return "source_coverage";
8816
+ if (/\b(mcp|tool|hook|runtime)\b/i.test(`${finding.title} ${finding.summary}`)) return "tool_signal";
8817
+ return "client_extraction";
8818
+ }
8819
+ function nodeBucketForKind(kind) {
8820
+ switch (kind) {
8821
+ case "decision":
8822
+ return "decisions";
8823
+ case "artifact":
8824
+ return "artifacts";
8825
+ case "person":
8826
+ return "people";
8827
+ case "agent":
8828
+ return "agents";
8829
+ case "tool":
8830
+ return "tools";
8831
+ case "business":
8832
+ return "businesses";
8833
+ case "product_surface":
8834
+ return "product_surfaces";
8835
+ case "goal":
8836
+ return "goals";
8837
+ case "source":
8838
+ return "sources";
8839
+ case "action":
8840
+ default:
8841
+ return "actions";
8842
+ }
8843
+ }
8844
+ function nodeIdForFinding(kind, finding) {
8845
+ const dedupeSource = finding.metadata.dedupe_key ?? finding.evidence_ref ?? finding.title;
8846
+ return `${kind}:${slugPart(finding.title)}:${shortHash(dedupeSource, 8)}`;
8847
+ }
8848
+ function buildFindingNode(finding, kind) {
8849
+ const id = nodeIdForFinding(kind, finding);
8850
+ return {
8851
+ id,
8852
+ kind,
8853
+ label: finding.title,
8854
+ summary: finding.summary,
8855
+ source_client: finding.source_client,
8856
+ evidence_refs: [finding.evidence_ref],
8857
+ linked_node_ids: [],
8858
+ confidence: finding.confidence,
8859
+ weight: clampScore2(finding.confidence * 100),
8860
+ review_state: "unreviewed",
8861
+ dedupe_key: `${kind}:${slugPart(finding.title)}`,
8862
+ metadata: {
8863
+ source_id: finding.source_id,
8864
+ finding_type: finding.type,
8865
+ ...finding.metadata
8866
+ }
8867
+ };
8868
+ }
8869
+ function mergeNodes(nodes) {
8870
+ const merged = /* @__PURE__ */ new Map();
8871
+ for (const node of nodes) {
8872
+ const key = node.dedupe_key ?? node.id;
8873
+ const previous = merged.get(key);
8874
+ if (!previous) {
8875
+ merged.set(key, node);
8876
+ continue;
8877
+ }
8878
+ merged.set(key, {
8879
+ ...previous,
8880
+ confidence: Math.max(previous.confidence, node.confidence),
8881
+ evidence_refs: sortedUnique([...previous.evidence_refs, ...node.evidence_refs]),
8882
+ linked_node_ids: sortedUnique([...previous.linked_node_ids, ...node.linked_node_ids]),
8883
+ weight: Math.max(previous.weight, node.weight)
8884
+ });
8885
+ }
8886
+ return [...merged.values()].sort((left, right) => right.weight - left.weight);
8887
+ }
8888
+ function sourceNode(source, connected) {
8889
+ const label = source.trim() || "Unknown source";
8890
+ return {
8891
+ id: `source:${slugPart(label)}:${connected ? "connected" : "missing"}`,
8892
+ kind: "source",
8893
+ label,
8894
+ summary: connected ? `${label} contributed evidence to this Work Graph.` : `${label} was not connected, so related attribution remains incomplete.`,
8895
+ evidence_refs: [],
8896
+ linked_node_ids: [],
8897
+ confidence: connected ? 0.8 : 0.52,
8898
+ weight: connected ? 70 : 62,
8899
+ review_state: connected ? "unreviewed" : "important",
8900
+ dedupe_key: `source:${slugPart(label)}`,
8901
+ metadata: { connected }
8902
+ };
8903
+ }
8904
+ function buildToolNodes(coverage, findings) {
8905
+ const mentionsOrgxTool = coverage.mcpObserved || findings.some((finding) => /\b(orgx[_-]?mcp|orgx_emit|mcp__orgx|tool call)\b/i.test(`${finding.title} ${finding.summary}`));
8906
+ if (!mentionsOrgxTool) return [];
8907
+ return [
8908
+ {
8909
+ id: "tool:orgx-mcp",
8910
+ kind: "tool",
8911
+ label: "OrgX MCP",
8912
+ summary: coverage.orgxMcpCalled ? "OrgX MCP was observed as a durable writeback surface in the audited work." : "OrgX MCP was available or mentioned, but the audit found incomplete durable writeback.",
8913
+ source_client: "mcp",
8914
+ evidence_refs: findings.filter((finding) => /\b(orgx|mcp|tool)\b/i.test(`${finding.title} ${finding.summary}`)).map((finding) => finding.evidence_ref).slice(0, 12),
8915
+ linked_node_ids: [],
8916
+ confidence: coverage.orgxMcpCalled ? 0.86 : 0.72,
8917
+ weight: coverage.orgxMcpCalled ? 88 : 76,
8918
+ review_state: "unreviewed",
8919
+ dedupe_key: "tool:orgx-mcp",
8920
+ metadata: {
8921
+ mcp_observed: coverage.mcpObserved,
8922
+ orgx_observed: coverage.orgxObserved,
8923
+ orgx_mcp_called: coverage.orgxMcpCalled,
8924
+ skill_only_signal: coverage.skillOnlySignal
8925
+ }
8926
+ }
8927
+ ];
8928
+ }
8929
+ function buildAgentNodes(findings) {
8930
+ const actorIds = sortedUnique(
8931
+ findings.map((finding) => typeof finding.metadata.actor_id === "string" ? finding.metadata.actor_id.trim() : "").filter(Boolean)
8932
+ );
8933
+ const sourceAgents = sortedUnique(
8934
+ findings.map(
8935
+ (finding) => ["codex", "claude", "claude-code", "cursor", "openclaw"].includes(finding.source_client) ? finding.source_client : ""
8936
+ ).filter(Boolean)
8937
+ );
8938
+ return [...actorIds, ...sourceAgents].slice(0, 20).map((agent) => ({
8939
+ id: `agent:${slugPart(agent)}`,
8940
+ kind: "agent",
8941
+ label: agent,
8942
+ summary: `${agent} contributed evidence to the Work Graph audit.`,
8943
+ source_client: sourceAgents.includes(agent) ? agent : "manual",
8944
+ evidence_refs: findings.filter((finding) => finding.metadata.actor_id === agent || finding.source_client === agent).map((finding) => finding.evidence_ref).slice(0, 12),
8945
+ linked_node_ids: [],
8946
+ confidence: 0.72,
8947
+ weight: 64,
8948
+ review_state: "unreviewed",
8949
+ dedupe_key: `agent:${slugPart(agent)}`,
8950
+ metadata: {}
8951
+ }));
8952
+ }
8953
+ function buildWorkGraphAttributionSpine(input) {
8954
+ const evidence_refs = input.findings.map((finding) => ({
8955
+ id: finding.evidence_ref,
8956
+ source_client: finding.source_client,
8957
+ source_id: finding.source_id,
8958
+ label: finding.title,
8959
+ summary: finding.summary.slice(0, 1200),
8960
+ ...isIsoTimestamp(finding.metadata.occurred_at) ? { occurred_at: finding.metadata.occurred_at } : {},
8961
+ confidence: finding.confidence,
8962
+ redaction_state: redactionStateForFinding(finding),
8963
+ metadata: {
8964
+ raw_transcript_sent: false,
8965
+ source_label: finding.metadata.source_label ?? null
8966
+ }
8967
+ }));
8968
+ const source_events = [
8969
+ ...input.events.map((event) => ({
8970
+ source_client: event.source_client,
8971
+ source_id: event.source_id,
8972
+ source_label: event.source_label,
8973
+ event_type: event.event_type === "client_extraction" ? "client_extraction" : event.event_type,
8974
+ evidence_ref: event.evidence_ref,
8975
+ confidence: 0.7,
8976
+ metadata: {
8977
+ ...event.metadata,
8978
+ raw_transcript_sent: false
8979
+ }
8980
+ })),
8981
+ ...input.findings.map((finding) => ({
8982
+ source_client: finding.source_client,
8983
+ source_id: finding.source_id,
8984
+ source_label: typeof finding.metadata.source_label === "string" ? finding.metadata.source_label : finding.source_id,
8985
+ event_type: attributionEventTypeForFinding(finding),
8986
+ ...isIsoTimestamp(finding.metadata.occurred_at) ? { occurred_at: finding.metadata.occurred_at } : {},
8987
+ evidence_ref: finding.evidence_ref,
8988
+ ...typeof finding.metadata.actor_id === "string" ? { actor_ref: finding.metadata.actor_id } : {},
8989
+ confidence: finding.confidence,
8990
+ metadata: {
8991
+ finding_type: finding.type,
8992
+ raw_transcript_sent: false
8993
+ }
8994
+ }))
8995
+ ];
8996
+ const buckets = {
8997
+ actions: [],
8998
+ decisions: [],
8999
+ artifacts: [],
9000
+ people: [],
9001
+ agents: buildAgentNodes(input.findings),
9002
+ tools: buildToolNodes(input.coverage, input.findings),
9003
+ businesses: [],
9004
+ product_surfaces: [],
9005
+ goals: [],
9006
+ sources: [
9007
+ ...input.coverage.connected.map((source) => sourceNode(source, true)),
9008
+ ...input.coverage.missing.map((source) => sourceNode(source, false))
9009
+ ],
9010
+ initiative_candidates: []
9011
+ };
9012
+ for (const finding of input.findings) {
9013
+ const kind = attributionKindForFinding(finding.type);
9014
+ if (!kind) continue;
9015
+ const node = buildFindingNode(finding, kind);
9016
+ const bucket = finding.type === "initiative_candidate" ? "initiative_candidates" : nodeBucketForKind(kind);
9017
+ buckets[bucket].push(node);
9018
+ }
9019
+ const normalized = {
9020
+ source_events: source_events.filter(
9021
+ (event, index, all) => all.findIndex(
9022
+ (candidate) => [
9023
+ candidate.source_client,
9024
+ candidate.source_id,
9025
+ candidate.event_type,
9026
+ candidate.evidence_ref
9027
+ ].join(":") === [
9028
+ event.source_client,
9029
+ event.source_id,
9030
+ event.event_type,
9031
+ event.evidence_ref
9032
+ ].join(":")
9033
+ ) === index
9034
+ ).slice(0, 1e3),
9035
+ actions: mergeNodes(buckets.actions).slice(0, 500),
9036
+ decisions: mergeNodes(buckets.decisions).slice(0, 500),
9037
+ artifacts: mergeNodes(buckets.artifacts).slice(0, 500),
9038
+ people: mergeNodes(buckets.people).slice(0, 500),
9039
+ agents: mergeNodes(buckets.agents).slice(0, 200),
9040
+ tools: mergeNodes(buckets.tools).slice(0, 300),
9041
+ businesses: mergeNodes(buckets.businesses).slice(0, 200),
9042
+ product_surfaces: mergeNodes(buckets.product_surfaces).slice(0, 500),
9043
+ goals: mergeNodes(buckets.goals).slice(0, 500),
9044
+ sources: mergeNodes(buckets.sources).slice(0, 300),
9045
+ initiative_candidates: mergeNodes(buckets.initiative_candidates).slice(0, 50),
9046
+ evidence_refs: mergeByEvidenceId(evidence_refs).slice(0, 1e3)
9047
+ };
9048
+ const nodeCount = normalized.actions.length + normalized.decisions.length + normalized.artifacts.length + normalized.people.length + normalized.agents.length + normalized.tools.length + normalized.businesses.length + normalized.product_surfaces.length + normalized.goals.length + normalized.sources.length + normalized.initiative_candidates.length;
9049
+ const confidenceInputs = [
9050
+ ...normalized.evidence_refs.map((evidence) => evidence.confidence),
9051
+ ...normalized.source_events.map((event) => event.confidence)
9052
+ ];
9053
+ const confidence = confidenceInputs.length ? Number((confidenceInputs.reduce((total, value) => total + value, 0) / confidenceInputs.length).toFixed(2)) : 0;
9054
+ return {
9055
+ ...normalized,
9056
+ confidence,
9057
+ dedupe_keys: sortedUnique([
9058
+ ...Object.values(normalized).flat().map((item) => typeof item === "object" && item && "dedupe_key" in item ? String(item.dedupe_key ?? "") : "").filter(Boolean)
9059
+ ]).slice(0, 1e3),
9060
+ privacy: {
9061
+ redaction_state: "public_summary",
9062
+ raw_transcripts_included: false,
9063
+ public_summary_only: true
9064
+ },
9065
+ review: {
9066
+ pending_count: nodeCount,
9067
+ correction_affordances: [
9068
+ "confirm",
9069
+ "merge",
9070
+ "hide",
9071
+ "mark_important",
9072
+ "launch_or_dismiss"
9073
+ ]
9074
+ }
9075
+ };
9076
+ }
9077
+ function mergeByEvidenceId(evidenceRefs) {
9078
+ const merged = /* @__PURE__ */ new Map();
9079
+ for (const evidence of evidenceRefs) {
9080
+ const previous = merged.get(evidence.id);
9081
+ if (!previous || evidence.confidence > previous.confidence) {
9082
+ merged.set(evidence.id, evidence);
9083
+ }
9084
+ }
9085
+ return [...merged.values()].sort((left, right) => right.confidence - left.confidence);
9086
+ }
8014
9087
  function buildWorkGraphFingerprint(input) {
8015
9088
  const sourceClients = sortedUnique(input.findings.map((finding) => finding.source_client));
8016
9089
  const patternHashes = input.findings.map(
@@ -8088,6 +9161,7 @@ function buildSessionReconciliationReport(input) {
8088
9161
  throw new Error("At least one source import or AI-client extraction is required to build a Work Graph report.");
8089
9162
  }
8090
9163
  const generatedAt = input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
9164
+ const extractionProtocol = buildWorkGraphExtractionProtocol();
8091
9165
  const clientExtractionSummaries = summarizeClientExtractions(clientExtractions);
8092
9166
  const connectedSources = sortedUnique([
8093
9167
  ...input.connectedSources ?? input.imports.map((source) => source.sourceLabel),
@@ -8103,23 +9177,56 @@ function buildSessionReconciliationReport(input) {
8103
9177
  (finding, index, all) => all.findIndex((candidate) => candidate.evidence_ref === finding.evidence_ref) === index
8104
9178
  );
8105
9179
  const allFindings = [...findings, ...autoMissed];
9180
+ const auditMethod = buildAuditMethod({
9181
+ clientExtractions,
9182
+ extractionProtocol,
9183
+ imports: input.imports
9184
+ });
9185
+ const domainCoverage = buildDomainCoverage(allFindings);
9186
+ const skillToolSignals = buildSkillToolSignals(allFindings);
8106
9187
  const trails = buildWorkGraphTrails(allFindings, generatedAt);
8107
9188
  const recurringPatterns = buildRecurringPatterns(coverage, allFindings, trails);
8108
9189
  const opportunityScore = scoreOpportunity(coverage, allFindings);
8109
9190
  const initiativeKickoffs = buildKickoffs(allFindings, missed, opportunityScore);
8110
9191
  const recommendations = buildTrailRecommendations(recurringPatterns, trails);
9192
+ const impactProjection = estimateImpactProjection({
9193
+ coverage,
9194
+ findings: allFindings,
9195
+ opportunityScore,
9196
+ patterns: recurringPatterns,
9197
+ trails
9198
+ });
9199
+ const executionQuality = scoreExecutionQuality({
9200
+ coverage,
9201
+ findings: allFindings,
9202
+ impact: impactProjection,
9203
+ patterns: recurringPatterns,
9204
+ recommendations,
9205
+ trails
9206
+ });
8111
9207
  const mirror = buildWorkGraphMirror({
9208
+ auditMethod,
8112
9209
  coverage,
9210
+ domains: domainCoverage,
8113
9211
  generatedAt,
9212
+ impact: impactProjection,
8114
9213
  patterns: recurringPatterns,
8115
9214
  recommendations,
9215
+ skillToolSignals,
8116
9216
  trails
8117
9217
  });
8118
9218
  const tensionMetrics = buildTensionMetrics({
8119
9219
  coverage,
9220
+ impact: impactProjection,
8120
9221
  patterns: recurringPatterns,
9222
+ quality: executionQuality,
8121
9223
  trails
8122
9224
  });
9225
+ const attributionSpine = buildWorkGraphAttributionSpine({
9226
+ coverage,
9227
+ events,
9228
+ findings: allFindings
9229
+ });
8123
9230
  const fingerprint = buildWorkGraphFingerprint({
8124
9231
  connectedSources,
8125
9232
  findings: allFindings,
@@ -8155,8 +9262,11 @@ function buildSessionReconciliationReport(input) {
8155
9262
  source_client: "wizard",
8156
9263
  session_id: sessionId,
8157
9264
  workspace: input.workspace,
8158
- extraction_protocol: buildWorkGraphExtractionProtocol(),
9265
+ extraction_protocol: extractionProtocol,
9266
+ audit_method: auditMethod,
8159
9267
  client_extractions: clientExtractionSummaries,
9268
+ domain_coverage: domainCoverage,
9269
+ skill_tool_signals: skillToolSignals,
8160
9270
  source_coverage: coverage,
8161
9271
  final_state: inferFinalState(allFindings),
8162
9272
  events,
@@ -8167,7 +9277,10 @@ function buildSessionReconciliationReport(input) {
8167
9277
  recommendations,
8168
9278
  mirror,
8169
9279
  tension_metrics: tensionMetrics,
9280
+ attribution_spine: attributionSpine,
8170
9281
  opportunity_score: opportunityScore,
9282
+ execution_quality: executionQuality,
9283
+ impact_projection: impactProjection,
8171
9284
  initiative_kickoffs: initiativeKickoffs,
8172
9285
  redaction_level: "summary_only",
8173
9286
  raw_transcripts_sent: false
@@ -8234,6 +9347,18 @@ function renderWorkGraphMarkdown(report) {
8234
9347
  lines.push(`- ${lens.lens}: ${lens.return_when}`);
8235
9348
  }
8236
9349
  lines.push("");
9350
+ lines.push("## Audit Method");
9351
+ lines.push("");
9352
+ lines.push(`Mode: ${report.audit_method.mode}`);
9353
+ lines.push(`Session files searched: ${report.audit_method.searched_session_files}`);
9354
+ lines.push(`Message turns searched: ${report.audit_method.searched_message_count}`);
9355
+ lines.push(`Evidence lines retained: ${report.audit_method.retained_evidence_lines}`);
9356
+ lines.push(`Source groups searched: ${report.audit_method.searched_source_groups}`);
9357
+ lines.push(`Extraction lenses: ${report.audit_method.extraction_lenses.join(", ")}`);
9358
+ for (const note of report.audit_method.notes) {
9359
+ lines.push(`- ${note}`);
9360
+ }
9361
+ lines.push("");
8237
9362
  lines.push("## Client Extractions");
8238
9363
  lines.push("");
8239
9364
  if (report.client_extractions.length === 0) {
@@ -8244,6 +9369,26 @@ function renderWorkGraphMarkdown(report) {
8244
9369
  }
8245
9370
  }
8246
9371
  lines.push("");
9372
+ lines.push("## Domain Coverage");
9373
+ lines.push("");
9374
+ if (report.domain_coverage.length === 0) {
9375
+ lines.push("- No domain clusters reached the public-summary threshold.");
9376
+ } else {
9377
+ for (const domain of report.domain_coverage) {
9378
+ lines.push(`- ${domain.label}: ${domain.finding_count} findings across ${domain.source_clients.join(", ")}. ${domain.summary}`);
9379
+ }
9380
+ }
9381
+ lines.push("");
9382
+ lines.push("## Skills, Agents, Tools, and Sources");
9383
+ lines.push("");
9384
+ if (report.skill_tool_signals.length === 0) {
9385
+ lines.push("- No repeated skill/tool signals reached the public-summary threshold.");
9386
+ } else {
9387
+ for (const signal of report.skill_tool_signals) {
9388
+ lines.push(`- [${signal.kind}] ${signal.label}: ${signal.mention_count} mention${signal.mention_count === 1 ? "" : "s"} across ${signal.source_clients.join(", ")}.`);
9389
+ }
9390
+ }
9391
+ lines.push("");
8247
9392
  lines.push("## Opportunity Score");
8248
9393
  lines.push("");
8249
9394
  lines.push(`Overall: ${report.opportunity_score.overall}/100`);
@@ -8254,15 +9399,46 @@ function renderWorkGraphMarkdown(report) {
8254
9399
  lines.push(`Automation potential: ${report.opportunity_score.automation_potential}/100`);
8255
9400
  lines.push(`OrgX fit: ${report.opportunity_score.orgx_fit}/100`);
8256
9401
  lines.push("");
9402
+ lines.push("## Execution Quality");
9403
+ lines.push("");
9404
+ lines.push(`Overall: ${report.execution_quality.overall}/100`);
9405
+ lines.push(`Evidence coverage: ${report.execution_quality.evidence_coverage}/100`);
9406
+ lines.push(`Source attribution: ${report.execution_quality.source_attribution}/100`);
9407
+ lines.push(`Chronology depth: ${report.execution_quality.trail_depth}/100`);
9408
+ lines.push(`Insight depth: ${report.execution_quality.insight_depth}/100`);
9409
+ lines.push(`Actionability: ${report.execution_quality.actionability}/100`);
9410
+ lines.push(`Impact confidence: ${report.execution_quality.impact_confidence}/100`);
9411
+ for (const note of report.execution_quality.notes) {
9412
+ lines.push(`- ${note}`);
9413
+ }
9414
+ lines.push("");
9415
+ lines.push("## Impact Projection");
9416
+ lines.push("");
9417
+ lines.push(`Time saved: ${report.impact_projection.time_saved_hours_per_week}h/week`);
9418
+ lines.push(`Acceleration: +${report.impact_projection.acceleration_percent}%`);
9419
+ lines.push(`Estimated monthly value: $${report.impact_projection.estimated_monthly_value_usd.toLocaleString("en-US")}`);
9420
+ lines.push(`Confidence: ${report.impact_projection.confidence}`);
9421
+ for (const item of report.impact_projection.basis) {
9422
+ lines.push(`- ${item}`);
9423
+ }
9424
+ lines.push("");
8257
9425
  lines.push("## Source Coverage");
8258
9426
  lines.push("");
8259
9427
  lines.push(`Connected: ${report.source_coverage.connected.join(", ") || "none"}`);
8260
9428
  lines.push(`Missing: ${report.source_coverage.missing.join(", ") || "none"}`);
9429
+ lines.push(`Coverage score: ${report.source_coverage.coverage_score ?? 0}/100`);
8261
9430
  lines.push(`MCP observed: ${report.source_coverage.mcpObserved ? "yes" : "no"}`);
8262
9431
  lines.push(`OrgX observed: ${report.source_coverage.orgxObserved ? "yes" : "no"}`);
8263
9432
  lines.push(`OrgX MCP called: ${report.source_coverage.orgxMcpCalled ? "yes" : "no"}`);
9433
+ if (report.source_coverage.manifests?.length) {
9434
+ lines.push("");
9435
+ lines.push("Source manifests:");
9436
+ for (const manifest of report.source_coverage.manifests) {
9437
+ lines.push(`- [${manifest.status}] ${manifest.source_label}: ${manifest.finding_count} findings, ${manifest.searched_session_count} searched / ${manifest.skipped_session_count} skipped sessions, confidence ${manifest.confidence}`);
9438
+ }
9439
+ }
8264
9440
  lines.push("");
8265
- lines.push("## Mirror");
9441
+ lines.push("## Operating Readout");
8266
9442
  lines.push("");
8267
9443
  lines.push(`### ${report.mirror.headline}`);
8268
9444
  lines.push("");
@@ -8272,13 +9448,13 @@ function renderWorkGraphMarkdown(report) {
8272
9448
  lines.push(`- ${claim.text} (${claim.evidence_refs.join(", ") || "no evidence refs"})`);
8273
9449
  }
8274
9450
  lines.push("");
8275
- lines.push("## Live Tension");
9451
+ lines.push("## Operating Leakage");
8276
9452
  lines.push("");
8277
9453
  for (const metric of report.tension_metrics) {
8278
9454
  lines.push(`- ${metric.value} ${metric.label}: ${metric.explanation}`);
8279
9455
  }
8280
9456
  lines.push("");
8281
- lines.push("## Work Graph Trails");
9457
+ lines.push("## Evidence Paths");
8282
9458
  lines.push("");
8283
9459
  for (const trail of report.trails.slice(0, 12)) {
8284
9460
  lines.push(`- [${trail.kind}] ${trail.title} \u2014 ${trail.state}, ${trail.valence}, ${trail.shape} (${trail.evidence_refs.join(", ")})`);
@@ -8316,12 +9492,36 @@ function renderWorkGraphMarkdown(report) {
8316
9492
  lines.push(`- [${kickoff.priority}] ${kickoff.title}: ${kickoff.summary}`);
8317
9493
  }
8318
9494
  lines.push("");
8319
- lines.push("## Eject Bay Recommendations");
9495
+ lines.push("## Repair Recommendations");
8320
9496
  lines.push("");
8321
9497
  for (const recommendation of report.recommendations) {
8322
9498
  lines.push(`- [${recommendation.priority}] ${recommendation.title}: ${recommendation.summary}`);
8323
9499
  }
8324
9500
  lines.push("");
9501
+ lines.push("## Attribution Spine");
9502
+ lines.push("");
9503
+ lines.push(`Source events: ${report.attribution_spine.source_events.length}`);
9504
+ lines.push(`Evidence refs: ${report.attribution_spine.evidence_refs.length}`);
9505
+ lines.push(`Review nodes: ${report.attribution_spine.review.pending_count}`);
9506
+ lines.push(`Confidence: ${report.attribution_spine.confidence}`);
9507
+ lines.push("");
9508
+ const attributionGroups = [
9509
+ ["Actions", report.attribution_spine.actions],
9510
+ ["Decisions", report.attribution_spine.decisions],
9511
+ ["Artifacts", report.attribution_spine.artifacts],
9512
+ ["People", report.attribution_spine.people],
9513
+ ["Businesses", report.attribution_spine.businesses],
9514
+ ["Surfaces", report.attribution_spine.product_surfaces],
9515
+ ["Sources", report.attribution_spine.sources]
9516
+ ];
9517
+ for (const [label, nodes] of attributionGroups) {
9518
+ if (nodes.length === 0) continue;
9519
+ lines.push(`${label}:`);
9520
+ for (const node of nodes.slice(0, 5)) {
9521
+ lines.push(`- ${node.label} (${node.evidence_refs.join(", ") || "source coverage"})`);
9522
+ }
9523
+ lines.push("");
9524
+ }
8325
9525
  lines.push("## Signup Hydration");
8326
9526
  lines.push("");
8327
9527
  lines.push(`- Strategy: ${report.signup_hydration.strategy}`);
@@ -8337,8 +9537,314 @@ function renderWorkGraphMarkdown(report) {
8337
9537
  return lines.join("\n");
8338
9538
  }
8339
9539
 
9540
+ // src/lib/work-graph-publish.ts
9541
+ async function parseResponse(response) {
9542
+ const text2 = await response.text();
9543
+ if (!text2) return null;
9544
+ try {
9545
+ return JSON.parse(text2);
9546
+ } catch {
9547
+ return text2;
9548
+ }
9549
+ }
9550
+ async function publishWorkGraphReport(report, options = {}) {
9551
+ const auth = await resolveOrgxAuth();
9552
+ if (!auth) {
9553
+ throw new Error("OrgX auth is required to publish a Work Graph. Run `orgx-wizard auth login` or set ORGX_API_KEY.");
9554
+ }
9555
+ const url = buildOrgxApiUrl("/client/work-graph/reports", auth.baseUrl);
9556
+ const response = await fetch(url, {
9557
+ method: "POST",
9558
+ headers: {
9559
+ Authorization: `Bearer ${auth.apiKey}`,
9560
+ "Content-Type": "application/json"
9561
+ },
9562
+ body: JSON.stringify({
9563
+ report,
9564
+ ...options.workspaceId ? { workspace_id: options.workspaceId } : {},
9565
+ ...options.initiativeId ? { initiative_id: options.initiativeId } : {},
9566
+ ...options.entityType ? { entity_type: options.entityType } : {},
9567
+ ...options.entityId ? { entity_id: options.entityId } : {},
9568
+ ...options.artifactUrl ? { artifact_url: options.artifactUrl } : {},
9569
+ attach_artifact: Boolean(options.attachArtifact),
9570
+ public_share: Boolean(options.publicShare)
9571
+ }),
9572
+ signal: options.signal ?? AbortSignal.timeout(15e3)
9573
+ });
9574
+ return {
9575
+ ok: response.ok,
9576
+ status: response.status,
9577
+ url,
9578
+ data: await parseResponse(response)
9579
+ };
9580
+ }
9581
+ async function publishWorkGraphEvents(fingerprint, patch, options = {}) {
9582
+ const auth = await resolveOrgxAuth();
9583
+ if (!auth) {
9584
+ throw new Error("OrgX auth is required to replay Work Graph hook events. Run `orgx-wizard auth login` or set ORGX_API_KEY.");
9585
+ }
9586
+ const url = buildOrgxApiUrl("/client/work-graph/events", auth.baseUrl);
9587
+ const response = await fetch(url, {
9588
+ method: "POST",
9589
+ headers: {
9590
+ Authorization: `Bearer ${auth.apiKey}`,
9591
+ "Content-Type": "application/json"
9592
+ },
9593
+ body: JSON.stringify({
9594
+ work_graph_fingerprint: fingerprint,
9595
+ patch
9596
+ }),
9597
+ signal: options.signal ?? AbortSignal.timeout(15e3)
9598
+ });
9599
+ return {
9600
+ ok: response.ok,
9601
+ status: response.status,
9602
+ url,
9603
+ data: await parseResponse(response)
9604
+ };
9605
+ }
9606
+
9607
+ // src/lib/work-graph-hook-events.ts
9608
+ import { createHash as createHash5 } from "crypto";
9609
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
9610
+ var SOURCE_CLIENTS = [
9611
+ "codex",
9612
+ "claude",
9613
+ "claude-code",
9614
+ "cursor",
9615
+ "openclaw",
9616
+ "slack",
9617
+ "mcp",
9618
+ "github",
9619
+ "linear",
9620
+ "gmail",
9621
+ "calendar",
9622
+ "notion",
9623
+ "docs",
9624
+ "manual",
9625
+ "wizard",
9626
+ "api",
9627
+ "unknown"
9628
+ ];
9629
+ function isRecord2(value) {
9630
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
9631
+ }
9632
+ function asString2(value) {
9633
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
9634
+ }
9635
+ function asNumber(value) {
9636
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
9637
+ }
9638
+ function asStringArray(value) {
9639
+ if (!Array.isArray(value)) return void 0;
9640
+ return value.filter((item) => typeof item === "string" && item.trim().length > 0);
9641
+ }
9642
+ function stableHash(value) {
9643
+ return createHash5("sha256").update(value).digest("hex").slice(0, 20);
9644
+ }
9645
+ function normalizeSourceClient2(value) {
9646
+ const raw = asString2(value)?.toLowerCase();
9647
+ if (!raw) return "unknown";
9648
+ if (SOURCE_CLIENTS.includes(raw)) return raw;
9649
+ if (raw === "claude_code") return "claude-code";
9650
+ return "unknown";
9651
+ }
9652
+ function safeTimestamp(value) {
9653
+ const raw = asString2(value);
9654
+ if (raw && !Number.isNaN(Date.parse(raw))) return new Date(raw).toISOString();
9655
+ return (/* @__PURE__ */ new Date(0)).toISOString();
9656
+ }
9657
+ function sourceLabel(sourceClient) {
9658
+ switch (sourceClient) {
9659
+ case "claude-code":
9660
+ return "Claude Code";
9661
+ case "codex":
9662
+ return "Codex";
9663
+ case "openclaw":
9664
+ return "OpenClaw";
9665
+ case "cursor":
9666
+ return "Cursor";
9667
+ default:
9668
+ return sourceClient.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
9669
+ }
9670
+ }
9671
+ function readHookRecord(line) {
9672
+ try {
9673
+ const parsed = JSON.parse(line);
9674
+ if (!isRecord2(parsed)) return null;
9675
+ const record = {};
9676
+ const schemaVersion = asString2(parsed.schema_version);
9677
+ const source = asString2(parsed.source);
9678
+ const sourceClient = asString2(parsed.source_client);
9679
+ const event = asString2(parsed.event);
9680
+ const sessionId = asString2(parsed.session_id);
9681
+ const turnId = asString2(parsed.turn_id);
9682
+ const cwd = asString2(parsed.cwd);
9683
+ const transcriptPath = asString2(parsed.transcript_path);
9684
+ const timestamp = asString2(parsed.timestamp);
9685
+ if (schemaVersion) record.schema_version = schemaVersion;
9686
+ if (source) record.source = source;
9687
+ if (sourceClient) record.source_client = sourceClient;
9688
+ if (event) record.event = event;
9689
+ if (sessionId) record.session_id = sessionId;
9690
+ if (turnId) record.turn_id = turnId;
9691
+ if (cwd) record.cwd = cwd;
9692
+ if (transcriptPath) record.transcript_path = transcriptPath;
9693
+ if (timestamp) record.timestamp = timestamp;
9694
+ if (isRecord2(parsed.summary)) {
9695
+ const summary = {};
9696
+ const toolName = asString2(parsed.summary.tool_name);
9697
+ const promptChars = asNumber(parsed.summary.prompt_chars);
9698
+ const payloadKeys = asStringArray(parsed.summary.payload_keys);
9699
+ if (toolName) summary.tool_name = toolName;
9700
+ if (promptChars !== void 0) summary.prompt_chars = promptChars;
9701
+ if (payloadKeys) summary.payload_keys = payloadKeys;
9702
+ record.summary = summary;
9703
+ }
9704
+ return record;
9705
+ } catch {
9706
+ return null;
9707
+ }
9708
+ }
9709
+ function readRuntimeHookOutbox(path, limit = 200) {
9710
+ if (!existsSync6(path)) return { path, records: [], skipped: 0 };
9711
+ const lines = readFileSync4(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
9712
+ const selected = lines.slice(Math.max(0, lines.length - Math.max(1, limit)));
9713
+ const records = [];
9714
+ let skipped = Math.max(0, lines.length - selected.length);
9715
+ for (const line of selected) {
9716
+ const record = readHookRecord(line);
9717
+ if (record) {
9718
+ records.push(record);
9719
+ } else {
9720
+ skipped += 1;
9721
+ }
9722
+ }
9723
+ return { path, records, skipped };
9724
+ }
9725
+ function buildWorkGraphHookReplayPatch(readResult) {
9726
+ const sourceEvents = [];
9727
+ const evidenceRefs = [];
9728
+ const sources = /* @__PURE__ */ new Map();
9729
+ const agents = /* @__PURE__ */ new Map();
9730
+ const tools = /* @__PURE__ */ new Map();
9731
+ const dedupeKeys = /* @__PURE__ */ new Set();
9732
+ readResult.records.forEach((record, index) => {
9733
+ const sourceClient = normalizeSourceClient2(record.source_client);
9734
+ const event = record.event ?? "runtime_hook";
9735
+ const timestamp = safeTimestamp(record.timestamp);
9736
+ const sourceId = record.session_id ?? record.transcript_path ?? record.cwd ?? `${sourceClient}:hook:${index}`;
9737
+ const evidenceId = `hook:${stableHash(`${sourceClient}:${sourceId}:${event}:${timestamp}:${index}`)}`;
9738
+ const toolName = record.summary?.tool_name;
9739
+ const hasMcpTool = Boolean(toolName && /(mcp|orgx)/i.test(toolName));
9740
+ const label = `${sourceLabel(sourceClient)} ${event}`;
9741
+ const summary = hasMcpTool ? `${sourceLabel(sourceClient)} hook observed tool activity through ${toolName}.` : `${sourceLabel(sourceClient)} hook observed ${event} in the local runtime.`;
9742
+ evidenceRefs.push({
9743
+ id: evidenceId,
9744
+ source_client: sourceClient,
9745
+ source_id: sourceId,
9746
+ label,
9747
+ summary,
9748
+ occurred_at: timestamp,
9749
+ confidence: hasMcpTool ? 0.82 : 0.68,
9750
+ redaction_state: "public_summary",
9751
+ metadata: {
9752
+ raw_transcript_sent: false,
9753
+ hook_source: record.source ?? "runtime_hook",
9754
+ cwd: record.cwd ?? null,
9755
+ transcript_path: record.transcript_path ?? null
9756
+ }
9757
+ });
9758
+ sourceEvents.push({
9759
+ source_client: sourceClient,
9760
+ source_id: sourceId,
9761
+ source_label: sourceLabel(sourceClient),
9762
+ event_type: hasMcpTool ? "mcp_call" : "runtime_hook",
9763
+ occurred_at: timestamp,
9764
+ evidence_ref: evidenceId,
9765
+ confidence: hasMcpTool ? 0.82 : 0.68,
9766
+ metadata: {
9767
+ hook_event: event,
9768
+ raw_transcript_sent: false,
9769
+ prompt_chars: record.summary?.prompt_chars ?? null,
9770
+ payload_keys: record.summary?.payload_keys ?? []
9771
+ }
9772
+ });
9773
+ const sourceEvidence = sources.get(sourceClient) ?? [];
9774
+ sourceEvidence.push(evidenceId);
9775
+ sources.set(sourceClient, sourceEvidence.slice(0, 30));
9776
+ dedupeKeys.add(`hook:${sourceClient}:${sourceId}:${event}`);
9777
+ if (sourceClient !== "unknown") {
9778
+ const agentId = `agent:${sourceClient}`;
9779
+ agents.set(agentId, {
9780
+ id: agentId,
9781
+ kind: "agent",
9782
+ label: sourceLabel(sourceClient),
9783
+ summary: `${sourceLabel(sourceClient)} is emitting passive runtime hook evidence into this Work Graph.`,
9784
+ source_client: sourceClient,
9785
+ evidence_refs: sourceEvidence.slice(0, 30),
9786
+ linked_node_ids: [`source:${sourceClient}`],
9787
+ confidence: 0.7,
9788
+ weight: 62,
9789
+ review_state: "unreviewed",
9790
+ dedupe_key: agentId,
9791
+ metadata: { runtime_hook_replay: true }
9792
+ });
9793
+ }
9794
+ if (toolName) {
9795
+ const toolId = `tool:${stableHash(toolName.toLowerCase())}`;
9796
+ const previous = tools.get(toolId);
9797
+ const previousEvidence = Array.isArray(previous?.evidence_refs) ? previous.evidence_refs.filter((item) => typeof item === "string") : [];
9798
+ tools.set(toolId, {
9799
+ id: toolId,
9800
+ kind: "tool",
9801
+ label: toolName,
9802
+ summary: hasMcpTool ? `Runtime hook evidence shows ${toolName} participating in OrgX/MCP work.` : `Runtime hook evidence shows ${toolName} participating in local work.`,
9803
+ source_client: sourceClient,
9804
+ evidence_refs: [...previousEvidence, evidenceId].slice(0, 30),
9805
+ linked_node_ids: [`source:${sourceClient}`],
9806
+ confidence: hasMcpTool ? 0.82 : 0.66,
9807
+ weight: hasMcpTool ? 78 : 54,
9808
+ review_state: "unreviewed",
9809
+ dedupe_key: toolId,
9810
+ metadata: { runtime_hook_replay: true }
9811
+ });
9812
+ }
9813
+ });
9814
+ const sourceNodes = [...sources.entries()].map(([sourceClient, refs]) => ({
9815
+ id: `source:${sourceClient}`,
9816
+ kind: "source",
9817
+ label: sourceLabel(sourceClient),
9818
+ summary: `${sourceLabel(sourceClient)} hook events have been replayed into this Work Graph profile.`,
9819
+ source_client: sourceClient,
9820
+ evidence_refs: refs.slice(0, 30),
9821
+ linked_node_ids: [],
9822
+ confidence: 0.7,
9823
+ weight: 65,
9824
+ review_state: "unreviewed",
9825
+ dedupe_key: `source:${sourceClient}`,
9826
+ metadata: { runtime_hook_replay: true }
9827
+ }));
9828
+ const patch = {
9829
+ source_events: sourceEvents,
9830
+ evidence_refs: evidenceRefs,
9831
+ sources: sourceNodes,
9832
+ agents: [...agents.values()],
9833
+ tools: [...tools.values()],
9834
+ dedupe_keys: [...dedupeKeys].slice(0, 200),
9835
+ confidence: evidenceRefs.length > 0 ? 0.7 : 0
9836
+ };
9837
+ return {
9838
+ patch,
9839
+ records: readResult.records.length,
9840
+ skipped: readResult.skipped,
9841
+ sources: [...sources.keys()],
9842
+ evidenceRefs: evidenceRefs.length
9843
+ };
9844
+ }
9845
+
8340
9846
  // src/lib/runtime-hooks.ts
8341
- import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
9847
+ import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
8342
9848
  import { homedir as homedir2 } from "os";
8343
9849
  import { dirname as dirname4, join as join5 } from "path";
8344
9850
  var HOOK_MARKER = "orgx-session-hook.mjs";
@@ -8364,7 +9870,7 @@ function backupPath(path, now) {
8364
9870
  return `${path}.bak.${timestamp}`;
8365
9871
  }
8366
9872
  function backupExisting(path, now) {
8367
- if (!existsSync6(path)) return null;
9873
+ if (!existsSync7(path)) return null;
8368
9874
  const backup = backupPath(path, now);
8369
9875
  copyFileSync(path, backup);
8370
9876
  return backup;
@@ -8562,7 +10068,7 @@ function inspectRuntimeHooks(options = {}) {
8562
10068
  installed: {
8563
10069
  claudeCode: hasOrgxHook(claudeSettingsRaw),
8564
10070
  codex: hasOrgxHook(codexHooksRaw),
8565
- hookScript: existsSync6(paths.hookScriptPath)
10071
+ hookScript: existsSync7(paths.hookScriptPath)
8566
10072
  },
8567
10073
  codex: {
8568
10074
  configExists: Boolean(codexConfigRaw),
@@ -8762,12 +10268,72 @@ function printRuntimeHookInspection(report) {
8762
10268
  console.log(` ${report.installed.claudeCode ? ICON.ok : ICON.warn} ${pc3.bold("Claude Code ")} ${report.installed.claudeCode ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.claudeSettingsPath)}`);
8763
10269
  console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
8764
10270
  }
10271
+ function requireHookReplayApproval(options, interactive) {
10272
+ if (options.yes) return true;
10273
+ if (!interactive) {
10274
+ throw new Error("Replaying Work Graph hook events requires --yes in non-interactive mode.");
10275
+ }
10276
+ return clack.confirm({
10277
+ message: "Replay passive hook events into this claimed Work Graph profile?"
10278
+ }).then((value) => {
10279
+ if (clack.isCancel(value) || value !== true) {
10280
+ clack.cancel("Hook replay cancelled.");
10281
+ return false;
10282
+ }
10283
+ return true;
10284
+ });
10285
+ }
10286
+ function parsePositiveInt(value, fallback) {
10287
+ if (!value?.trim()) return fallback;
10288
+ const parsed = Number.parseInt(value.trim(), 10);
10289
+ if (!Number.isFinite(parsed) || parsed < 1) {
10290
+ throw new Error(`Expected a positive integer, got ${value}`);
10291
+ }
10292
+ return parsed;
10293
+ }
10294
+ async function runHookReplayCommand(options) {
10295
+ const fingerprint = options.fingerprint?.trim();
10296
+ if (!fingerprint) {
10297
+ throw new Error("Missing --fingerprint <wgf_...> for hook event replay.");
10298
+ }
10299
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
10300
+ const paths = inspectRuntimeHooks().paths;
10301
+ const outboxPath = resolve(options.outbox?.trim() || paths.outboxPath);
10302
+ const readResult = readRuntimeHookOutbox(outboxPath, parsePositiveInt(options.limit, 200));
10303
+ const replay = buildWorkGraphHookReplayPatch(readResult);
10304
+ if (replay.records === 0) {
10305
+ if (options.json) {
10306
+ console.log(JSON.stringify({ ok: true, published: false, reason: "empty_outbox", ...replay }, null, 2));
10307
+ return;
10308
+ }
10309
+ console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`no hook events found at ${outboxPath}`)}`);
10310
+ return;
10311
+ }
10312
+ const approved = await requireHookReplayApproval(options, interactive);
10313
+ if (!approved) return;
10314
+ const result = await publishWorkGraphEvents(fingerprint, replay.patch);
10315
+ if (!result.ok) process.exitCode = 1;
10316
+ await safeTrackWizardTelemetry("hooks_replay_ran", {
10317
+ command: "hooks replay",
10318
+ fingerprint,
10319
+ records: String(replay.records),
10320
+ sources: replay.sources.join(","),
10321
+ status: String(result.status)
10322
+ });
10323
+ if (options.json) {
10324
+ console.log(JSON.stringify({ ...replay, published: result }, null, 2));
10325
+ return;
10326
+ }
10327
+ console.log(` ${result.ok ? ICON.ok : ICON.warn} ${pc3.bold("replayed ")} ${replay.records} hook event${replay.records === 1 ? "" : "s"} ${pc3.dim(`status ${result.status}`)}`);
10328
+ console.log(` ${ICON.skip} ${pc3.bold("sources ")} ${pc3.dim(replay.sources.join(", ") || "none")}`);
10329
+ console.log(` ${ICON.skip} ${pc3.bold("evidence ")} ${pc3.dim(`${replay.evidenceRefs} public-summary ref${replay.evidenceRefs === 1 ? "" : "s"}`)}`);
10330
+ }
8765
10331
  function readAuditInput(options, interactive) {
8766
10332
  if (options.input?.trim()) {
8767
- return readFileSync5(resolve(options.input.trim()), "utf8");
10333
+ return readFileSync6(resolve(options.input.trim()), "utf8");
8768
10334
  }
8769
10335
  if (!process.stdin.isTTY) {
8770
- return readFileSync5(0, "utf8");
10336
+ return readFileSync6(0, "utf8");
8771
10337
  }
8772
10338
  if (!interactive) {
8773
10339
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -8800,7 +10366,7 @@ function collectPathOption(value, previous = []) {
8800
10366
  }
8801
10367
  function parseClientExtractionFile(path) {
8802
10368
  const resolvedPath = resolve(path);
8803
- const parsed = JSON.parse(readFileSync5(resolvedPath, "utf8"));
10369
+ const parsed = JSON.parse(readFileSync6(resolvedPath, "utf8"));
8804
10370
  if (!isRecord(parsed)) {
8805
10371
  throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
8806
10372
  }
@@ -8839,6 +10405,11 @@ async function readAuditImports(options, interactive) {
8839
10405
  imports.push({
8840
10406
  sourceId: "wizard-audit-input",
8841
10407
  sourceLabel: options.sourceLabel?.trim() || "Wizard audit input",
10408
+ metadata: {
10409
+ message_count: 1,
10410
+ retained_line_count: text2.split(/\r?\n/).filter(Boolean).length,
10411
+ source_client: "manual"
10412
+ },
8842
10413
  text: text2
8843
10414
  });
8844
10415
  connectedSources.push(options.sourceLabel?.trim() || "Manual AI-session import");
@@ -8902,6 +10473,22 @@ function requireWriteApproval(options, interactive) {
8902
10473
  return true;
8903
10474
  });
8904
10475
  }
10476
+ function requireWorkGraphPublishApproval(options, interactive) {
10477
+ const wantsPublish = Boolean(options.publish || options.publicShare);
10478
+ if (!wantsPublish || options.yes || options.dryRun) return true;
10479
+ if (!interactive) {
10480
+ throw new Error("Publishing a Work Graph requires --yes in non-interactive mode.");
10481
+ }
10482
+ return clack.confirm({
10483
+ message: "Publish this Work Graph to OrgX and create a shareable profile?"
10484
+ }).then((value) => {
10485
+ if (clack.isCancel(value) || value !== true) {
10486
+ clack.cancel("Work Graph publish cancelled.");
10487
+ return false;
10488
+ }
10489
+ return true;
10490
+ });
10491
+ }
8905
10492
  async function resolveAuditWorkspace(options) {
8906
10493
  const explicitId = options.workspaceId?.trim();
8907
10494
  const explicitName = options.workspaceName?.trim();
@@ -9021,18 +10608,27 @@ async function runWorkGraphCommand(options, defaults = {}) {
9021
10608
  };
9022
10609
  const auditInputs = await readWorkGraphInputs(commandOptions, interactive);
9023
10610
  const workspace = await resolveAuditWorkspace(commandOptions);
10611
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
10612
+ const nativeClientExtractions = buildNativeAiClientExtractions(
10613
+ auditInputs.imports,
10614
+ generatedAt
10615
+ );
10616
+ const clientExtractions = [
10617
+ ...auditInputs.clientExtractions,
10618
+ ...nativeClientExtractions
10619
+ ];
9024
10620
  const report = buildSessionReconciliationReport({
9025
- clientExtractions: auditInputs.clientExtractions,
10621
+ clientExtractions,
9026
10622
  connectedSources: [
9027
10623
  ...auditInputs.connectedSources,
9028
10624
  ...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
9029
10625
  ],
9030
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9031
10626
  imports: auditInputs.imports,
9032
10627
  missingSources: [
9033
10628
  ...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
9034
10629
  ...auditInputs.missingSources
9035
10630
  ],
10631
+ generatedAt,
9036
10632
  workspace
9037
10633
  });
9038
10634
  const markdown = renderWorkGraphMarkdown(report);
@@ -9042,6 +10638,39 @@ async function runWorkGraphCommand(options, defaults = {}) {
9042
10638
  const markdownPath = resolve(outputDir, `work-graph-report-${timestamp}.md`);
9043
10639
  writeJsonFile(jsonPath, report);
9044
10640
  writeTextFile(markdownPath, markdown);
10641
+ let published = null;
10642
+ const shouldPublish = Boolean(commandOptions.publish || commandOptions.publicShare);
10643
+ if (shouldPublish) {
10644
+ const approved = await requireWorkGraphPublishApproval(commandOptions, interactive);
10645
+ if (approved) {
10646
+ if (commandOptions.dryRun) {
10647
+ published = { ok: true, status: 0 };
10648
+ } else {
10649
+ const publishResult = await publishWorkGraphReport(report, {
10650
+ ...commandOptions.artifactUrl?.trim() ? { artifactUrl: commandOptions.artifactUrl.trim() } : {},
10651
+ attachArtifact: Boolean(commandOptions.attachArtifact || commandOptions.attachToInitiative),
10652
+ ...commandOptions.entityId?.trim() ? { entityId: commandOptions.entityId.trim() } : {},
10653
+ ...commandOptions.entityType ? { entityType: commandOptions.entityType } : {},
10654
+ ...commandOptions.attachToInitiative?.trim() ? { initiativeId: commandOptions.attachToInitiative.trim() } : {},
10655
+ publicShare: Boolean(commandOptions.publicShare || commandOptions.publish),
10656
+ ...workspace.id !== "local-workspace" ? { workspaceId: workspace.id } : {}
10657
+ });
10658
+ if (!publishResult.ok) {
10659
+ throw new Error(`Work Graph publish failed with HTTP ${publishResult.status}: ${JSON.stringify(publishResult.data)}`);
10660
+ }
10661
+ const body = isRecord(publishResult.data) ? publishResult.data : {};
10662
+ const publicShare = isRecord(body.public_share) ? body.public_share : null;
10663
+ const publicUrl = typeof publicShare?.url === "string" ? `${normalizeOrgxBaseUrl(process.env.ORGX_BASE_URL || DEFAULT_ORGX_BASE_URL)}${publicShare.url}` : void 0;
10664
+ const readout = isRecord(body.public_readout) ? body.public_readout : null;
10665
+ published = {
10666
+ ok: true,
10667
+ status: publishResult.status,
10668
+ ...publicUrl ? { publicUrl } : {},
10669
+ ...typeof readout?.review_url === "string" ? { reviewUrl: readout.review_url } : {}
10670
+ };
10671
+ }
10672
+ }
10673
+ }
9045
10674
  if (commandOptions.json) {
9046
10675
  console.log(JSON.stringify({
9047
10676
  jsonPath,
@@ -9051,13 +10680,17 @@ async function runWorkGraphCommand(options, defaults = {}) {
9051
10680
  hydrationKey: report.signup_hydration.hydration_key,
9052
10681
  finalState: report.final_state,
9053
10682
  opportunityScore: report.opportunity_score,
10683
+ executionQuality: report.execution_quality,
10684
+ impactProjection: report.impact_projection,
9054
10685
  missedOrchestration: report.missed_orchestration_opportunities.length,
9055
10686
  clientExtractionCount: report.client_extractions.length,
9056
10687
  kickoffCount: report.initiative_kickoffs.length,
9057
- trailCount: report.trails.length,
10688
+ findingCount: report.trails.length,
9058
10689
  recurringPatternCount: report.recurring_patterns.length,
9059
- topTrail: report.mirror.primary_trail_id ?? null,
9060
- mirrorHeadline: report.mirror.headline
10690
+ attributionCount: report.attribution_spine.review.pending_count,
10691
+ topFinding: report.mirror.primary_trail_id ?? null,
10692
+ readoutHeadline: report.mirror.headline,
10693
+ published
9061
10694
  }, null, 2));
9062
10695
  return;
9063
10696
  }
@@ -9067,11 +10700,13 @@ async function runWorkGraphCommand(options, defaults = {}) {
9067
10700
  console.log(` ${ICON.ok} ${pc3.green("state ")} ${pc3.dim(report.final_state)}`);
9068
10701
  console.log(` ${ICON.ok} ${pc3.green("extractions ")} ${pc3.dim(String(report.client_extractions.length))}`);
9069
10702
  console.log(` ${ICON.ok} ${pc3.green("score ")} ${pc3.dim(formatWorkGraphScoreLine(report.opportunity_score))}`);
10703
+ console.log(` ${ICON.ok} ${pc3.green("quality ")} ${pc3.dim(`${report.execution_quality.overall}/100 \xB7 coverage ${report.source_coverage.coverage_score ?? 0}/100`)}`);
10704
+ console.log(` ${ICON.ok} ${pc3.green("impact ")} ${pc3.dim(`${report.impact_projection.time_saved_hours_per_week}h/week \xB7 +${report.impact_projection.acceleration_percent}% acceleration \xB7 ~$${report.impact_projection.estimated_monthly_value_usd.toLocaleString("en-US")}/month`)}`);
9070
10705
  const missed = report.missed_orchestration_opportunities.length;
9071
10706
  const missedColor = missed > 0 ? pc3.yellow : pc3.green;
9072
10707
  console.log(` ${missed > 0 ? ICON.warn : ICON.ok} ${missedColor("missed ")} ${pc3.dim(`${missed} orchestration opportunit${missed === 1 ? "y" : "ies"}`)}`);
9073
- console.log(` ${ICON.ok} ${pc3.green("trails ")} ${pc3.dim(`${report.trails.length} trail${report.trails.length === 1 ? "" : "s"} \xB7 ${report.recurring_patterns.length} recurring pattern${report.recurring_patterns.length === 1 ? "" : "s"}`)}`);
9074
- console.log(` ${ICON.skip} ${pc3.bold("mirror ")} ${report.mirror.headline}`);
10708
+ console.log(` ${ICON.ok} ${pc3.green("findings ")} ${pc3.dim(`${report.trails.length} finding${report.trails.length === 1 ? "" : "s"} \xB7 ${report.recurring_patterns.length} recurring pattern${report.recurring_patterns.length === 1 ? "" : "s"}`)}`);
10709
+ console.log(` ${ICON.skip} ${pc3.bold("readout ")} ${report.mirror.headline}`);
9075
10710
  for (const metric of report.tension_metrics.slice(0, 4)) {
9076
10711
  const tone = metric.tone === "danger" ? pc3.red : metric.tone === "warning" ? pc3.yellow : metric.tone === "good" ? pc3.green : pc3.dim;
9077
10712
  console.log(` ${ICON.skip} ${tone(`${metric.value} ${metric.label}`)} ${pc3.dim(metric.explanation)}`);
@@ -9079,6 +10714,10 @@ async function runWorkGraphCommand(options, defaults = {}) {
9079
10714
  for (const kickoff of report.initiative_kickoffs) {
9080
10715
  console.log(` ${ICON.skip} ${pc3.bold(kickoff.priority.padEnd(3))} ${kickoff.title}`);
9081
10716
  }
10717
+ console.log(` ${ICON.ok} ${pc3.green("attribution ")} ${pc3.dim(`${report.attribution_spine.review.pending_count} nodes \xB7 ${report.attribution_spine.source_events.length} source events`)}`);
10718
+ if (published?.publicUrl) {
10719
+ console.log(` ${ICON.ok} ${pc3.green("profile ")} ${pc3.bold(published.publicUrl)}`);
10720
+ }
9082
10721
  }
9083
10722
  async function checkPluginStatusesCompact() {
9084
10723
  const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
@@ -10038,7 +11677,7 @@ function printDoctorReport(report, assessment) {
10038
11677
  async function main() {
10039
11678
  const program = new Command();
10040
11679
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
10041
- const pkgVersion = true ? "0.1.32" : void 0;
11680
+ const pkgVersion = true ? "0.1.37" : void 0;
10042
11681
  program.version(pkgVersion ?? "unknown", "-V, --version");
10043
11682
  program.hook("preAction", (_thisCommand, actionCommand) => {
10044
11683
  if (Boolean(actionCommand.optsWithGlobals().json)) return;
@@ -10738,22 +12377,22 @@ async function main() {
10738
12377
  });
10739
12378
  await runAuditCommand(options);
10740
12379
  });
10741
- const workGraph = program.command("work-graph").description("Build a redacted OrgX Work Graph report from AI-client, Slack, MCP, or manual context.");
10742
- workGraph.command("extraction-schema").description("Print the AI-client search schema used to extract Work Graph Trails from sessions and logs.").option("--output <path>", "write the schema prompt to a file").option("--json", "emit the protocol as JSON instead of Markdown").action(async (options) => {
12380
+ const workGraph = program.command("work-graph").description("Build a redacted OrgX Profile from AI-client session search, Slack, MCP, or manual context.");
12381
+ workGraph.command("extraction-schema").description("Print the packaged AI-client audit skill used to search sessions, messages, tools, domains, and logs.").option("--output <path>", "write the schema prompt to a file").option("--json", "emit the protocol as JSON instead of Markdown").action(async (options) => {
10743
12382
  await safeTrackWizardTelemetry("work_graph_extraction_schema_started", {
10744
12383
  command: "work-graph extraction-schema",
10745
12384
  json: Boolean(options.json)
10746
12385
  });
10747
12386
  runWorkGraphExtractionSchemaCommand(options);
10748
12387
  });
10749
- workGraph.command("preview").description("Preview the live Work Graph opportunity map without writing to OrgX.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "3").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only previews").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
12388
+ workGraph.command("preview").description("Preview the OrgX Profile evidence findings without writing to OrgX.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "15").option("--session-days <days>", "lookback window for local AI-session imports", "60").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only previews").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
10750
12389
  await safeTrackWizardTelemetry("work_graph_preview_started", {
10751
12390
  command: "work-graph preview",
10752
12391
  from: options.from ?? "manual"
10753
12392
  });
10754
12393
  await runWorkGraphCommand(options);
10755
12394
  });
10756
- workGraph.command("profile").description("Build a local OrgX Profile with Work Graph Trails, Mirror, tensions, and launch recommendations.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only profiles").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
12395
+ workGraph.command("profile").description("Build a local OrgX Profile with evidence findings, domain coverage, source confidence, and repair recommendations.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "25").option("--session-days <days>", "lookback window for local AI-session imports", "60").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only profiles").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--publish", "publish the generated OrgX Profile to OrgX and return a shareable URL").option("--public-share", "create a public redacted /work-graph/<token> share when publishing").option("--attach-artifact", "attach the report as an OrgX artifact when an initiative/entity is provided").option("--attach-to-initiative <id>", "attach the report to an existing OrgX initiative").option("--entity-type <type>", "entity type for artifact attachment: project, initiative, milestone, task, decision").option("--entity-id <id>", "entity id for artifact attachment").option("--artifact-url <url>", "override the artifact URL stored in OrgX").option("--yes", "approve publish/write prompts in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
10757
12396
  await safeTrackWizardTelemetry("work_graph_profile_started", {
10758
12397
  command: "work-graph profile",
10759
12398
  from: options.from ?? "manual"
@@ -10761,7 +12400,7 @@ async function main() {
10761
12400
  await runWorkGraphCommand(options);
10762
12401
  });
10763
12402
  const sessions = program.command("sessions").description("Inspect and reconcile local AI sessions into OrgX-ready Work Graph reports.");
10764
- sessions.command("reconcile").description("Backfill recent Codex and Claude sessions into a redacted Work Graph report.").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").option("--session-days <days>", "lookback window for local AI-session imports", "7").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only reconciliation").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
12403
+ sessions.command("reconcile").description("Backfill recent Codex and Claude sessions into a redacted OrgX Profile report.").option("--extraction <path>", "structured AI-client extraction JSON; repeat or comma-separate for multiple clients", collectPathOption, []).option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "25").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only reconciliation").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--publish", "publish the generated OrgX Profile to OrgX and return a shareable URL").option("--public-share", "create a public redacted /work-graph/<token> share when publishing").option("--attach-artifact", "attach the report as an OrgX artifact when an initiative/entity is provided").option("--attach-to-initiative <id>", "attach the report to an existing OrgX initiative").option("--entity-type <type>", "entity type for artifact attachment: project, initiative, milestone, task, decision").option("--entity-id <id>", "entity id for artifact attachment").option("--artifact-url <url>", "override the artifact URL stored in OrgX").option("--yes", "approve publish/write prompts in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
10765
12404
  await safeTrackWizardTelemetry("sessions_reconcile_started", {
10766
12405
  command: "sessions reconcile",
10767
12406
  from: options.from ?? "all"
@@ -10805,6 +12444,9 @@ async function main() {
10805
12444
  }
10806
12445
  }
10807
12446
  });
12447
+ hooks.command("replay").description("Replay passive hook outbox events into a claimed Work Graph profile.").requiredOption("--fingerprint <fingerprint>", "target Work Graph fingerprint, for example wgf_0123...").option("--outbox <path>", "runtime hook JSONL outbox path").option("--limit <count>", "maximum recent hook events to replay", "200").option("--yes", "approve publishing hook evidence without prompting").option("--json", "emit a JSON command summary").action(async (options) => {
12448
+ await runHookReplayCommand(options);
12449
+ });
10808
12450
  program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
10809
12451
  const spinner = createOrgxSpinner("Running OrgX health check");
10810
12452
  spinner.start();