snipara-companion 3.6.2 → 3.6.4

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  Release notes for `snipara-companion`, newest first.
4
4
 
5
+ ## New In 3.6.4
6
+
7
+ - Correlates hosted Judgment responses with their immutable snapshot, append-only
8
+ exposure, run, and served-Judgment identities.
9
+ - Sends those canonical identities back with advisor influence receipts so the
10
+ backend can reject mismatched client correlation data.
11
+ - Keeps hosted Judgment failures best-effort for local runs while reporting the
12
+ failure boundary explicitly.
13
+
14
+ ## New In 3.6.3
15
+
16
+ - Labels every semantic `confidence` value as an uncalibrated
17
+ `heuristic_prior`, preserving the numeric compatibility field without
18
+ presenting it as a measured probability.
19
+ - Loads bounded, literal project naming terms from
20
+ `.snipara/semantic-rules.json`; teams can extend or replace English defaults
21
+ without enabling arbitrary regular expressions.
22
+ - Keeps historical regression associations strictly shadow-only and marks
23
+ their association strength as uncalibrated.
24
+
5
25
  ## New In 3.6.2
6
26
 
7
27
  - Adds `snipara.semantic.v1` to local code overlays and impact results with
package/dist/index.d.ts CHANGED
@@ -688,6 +688,9 @@ interface AdvisorInfluenceRecommendationInput {
688
688
  }
689
689
  interface RecordAdvisorInfluenceReceiptInput {
690
690
  servedJudgmentId: string;
691
+ judgmentSnapshotId?: string;
692
+ judgmentExposureId?: string;
693
+ runId?: string;
691
694
  recommendation: AdvisorInfluenceRecommendationInput;
692
695
  agentDecision: AdvisorInfluenceAgentDecision;
693
696
  behaviorChange: string;
@@ -725,6 +728,16 @@ interface ProjectIntelligenceBriefRequest {
725
728
  changedFiles?: string[];
726
729
  symbols?: string[];
727
730
  routes?: string[];
731
+ codeContext?: {
732
+ localHeadSha?: string;
733
+ branch?: string;
734
+ workingTreeDirty?: boolean;
735
+ };
736
+ correlation?: {
737
+ surface?: "companion";
738
+ runId?: string;
739
+ sessionId?: string;
740
+ };
728
741
  }
729
742
  interface HostedProjectIntelligenceBriefResult {
730
743
  project: {
@@ -733,6 +746,8 @@ interface HostedProjectIntelligenceBriefResult {
733
746
  slug: string;
734
747
  };
735
748
  servedJudgmentId: string | null;
749
+ judgmentSnapshotId: string | null;
750
+ judgmentExposureId: string | null;
736
751
  persistedShadowSignalCount: number;
737
752
  brief: {
738
753
  version: string;
@@ -1825,6 +1840,8 @@ interface LocalSemanticAssertion {
1825
1840
  predicate: LocalSemanticPredicate;
1826
1841
  value: string | boolean;
1827
1842
  confidence: number;
1843
+ scoreKind: "heuristic_prior";
1844
+ calibrated: false;
1828
1845
  source: "typescript-compiler-api" | "language-parser" | "architecture-pattern-rules" | "criticality-rules" | "graph-criticality-rules";
1829
1846
  extractorVersion: 1;
1830
1847
  evidence: LocalSemanticEvidence[];
@@ -1833,6 +1850,29 @@ interface LocalSemanticModel {
1833
1850
  version: "snipara.semantic.v1";
1834
1851
  extractorVersion: 1;
1835
1852
  source: "deterministic-graph-inference";
1853
+ modelType: "rule-based-heuristic";
1854
+ scoreContract: {
1855
+ kind: "heuristic_prior";
1856
+ calibrated: false;
1857
+ probability: false;
1858
+ comparableAcrossProjects: false;
1859
+ basis: "hand-tuned-v1";
1860
+ rulesetVersion: 1;
1861
+ interpretation: string;
1862
+ };
1863
+ ruleConfig: {
1864
+ version: "snipara.semantic.rules.v1";
1865
+ source: "defaults" | "project-file";
1866
+ path: ".snipara/semantic-rules.json";
1867
+ replaceDefaults: boolean;
1868
+ sensitivePathTermCount: number;
1869
+ contractPathTermCount: number;
1870
+ testPathTermCount: number;
1871
+ architectureRoleTermCount: number;
1872
+ configuredRoles: string[];
1873
+ configHash: string;
1874
+ warnings: string[];
1875
+ };
1836
1876
  scope: "repository" | "impact";
1837
1877
  assertions: LocalSemanticAssertion[];
1838
1878
  publicContracts: LocalSemanticAssertion[];
@@ -2908,6 +2948,8 @@ interface ProjectRunHostedJudgmentLink {
2908
2948
  status: "linked" | "unlinked" | "unavailable";
2909
2949
  timeoutMs: number;
2910
2950
  servedJudgmentId?: string;
2951
+ judgmentSnapshotId?: string;
2952
+ judgmentExposureId?: string;
2911
2953
  error?: string;
2912
2954
  }
2913
2955
  interface ProjectIntelligenceRunResult {
package/dist/index.js CHANGED
@@ -3822,7 +3822,10 @@ function buildLocalCodeOverlay(options = {}) {
3822
3822
  extractedEdges.push(...extracted.edges);
3823
3823
  }
3824
3824
  const edges = resolveLocalGraphEdges(files, symbols, imports, references2, extractedEdges);
3825
- const semantic = buildLocalSemanticModel({ symbols, edges });
3825
+ const semantic = buildLocalSemanticModel(
3826
+ { symbols, edges },
3827
+ { semanticRules: loadLocalSemanticRuleConfig(repoRoot) }
3828
+ );
3826
3829
  for (const file of files) {
3827
3830
  file.edgeCount = edges.filter((edge) => edge.filePath === file.path).length;
3828
3831
  }
@@ -4308,9 +4311,125 @@ function uniqueTraversalFiles(nodes, excluded) {
4308
4311
  )
4309
4312
  ].sort();
4310
4313
  }
4314
+ var LOCAL_SEMANTIC_RULE_CONFIG_PATH = import_path.default.join(".snipara", "semantic-rules.json");
4315
+ var LOCAL_SEMANTIC_RULE_CONFIG_VERSION = "snipara.semantic.rules.v1";
4316
+ var LOCAL_SEMANTIC_MAX_TERMS = 64;
4317
+ var LOCAL_SEMANTIC_MAX_TERM_LENGTH = 80;
4318
+ var LOCAL_SEMANTIC_MAX_ROLES = 24;
4311
4319
  var LOCAL_SEMANTIC_TEST_PATH = /(^|\/)(tests?|__tests__|fixtures?)(\/|$)|\.(test|spec)\./i;
4312
4320
  var LOCAL_SEMANTIC_SENSITIVE_PATH = /(^|\/)(auth|middleware|billing|payments?|stripe|webhooks?|prisma|schema|migrations?|deploy)(\/|\.|$)/i;
4313
4321
  var LOCAL_SEMANTIC_CONTRACT_PATH = /(^|\/)(schemas?|contracts?|protocols?|interfaces?|config|tool_defs)(\/|\.|$)|route\.(ts|tsx|py)$/i;
4322
+ function localSemanticTerms(raw, label, warnings) {
4323
+ if (raw === void 0 || raw === null) return [];
4324
+ if (!Array.isArray(raw)) {
4325
+ warnings.push(`${label} terms must be an array.`);
4326
+ return [];
4327
+ }
4328
+ const terms = [];
4329
+ for (const value of raw.slice(0, LOCAL_SEMANTIC_MAX_TERMS)) {
4330
+ const term = String(value).trim().toLowerCase();
4331
+ if (!term) continue;
4332
+ if (term.length > LOCAL_SEMANTIC_MAX_TERM_LENGTH) {
4333
+ warnings.push(
4334
+ `Ignored a ${label} term longer than ${LOCAL_SEMANTIC_MAX_TERM_LENGTH} characters.`
4335
+ );
4336
+ continue;
4337
+ }
4338
+ terms.push(term);
4339
+ }
4340
+ if (raw.length > LOCAL_SEMANTIC_MAX_TERMS) {
4341
+ warnings.push(`Limited ${label} terms to ${LOCAL_SEMANTIC_MAX_TERMS} entries.`);
4342
+ }
4343
+ return [...new Set(terms)];
4344
+ }
4345
+ function loadLocalSemanticRuleConfig(repoRoot) {
4346
+ const defaults = {
4347
+ replaceDefaults: false,
4348
+ sensitivePathTerms: [],
4349
+ contractPathTerms: [],
4350
+ testPathTerms: [],
4351
+ architectureRoleTerms: {},
4352
+ source: "defaults",
4353
+ warnings: []
4354
+ };
4355
+ const configPath = import_path.default.join(repoRoot, LOCAL_SEMANTIC_RULE_CONFIG_PATH);
4356
+ if (!import_fs.default.existsSync(configPath)) return defaults;
4357
+ try {
4358
+ const parsed = JSON.parse(import_fs.default.readFileSync(configPath, "utf8"));
4359
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4360
+ return {
4361
+ ...defaults,
4362
+ source: "project-file",
4363
+ warnings: ["Semantic rule config must be a JSON object; defaults were retained."]
4364
+ };
4365
+ }
4366
+ const raw = parsed;
4367
+ const warnings = [];
4368
+ const rawRoles = raw.architectureRoleTerms ?? raw.architecture_role_terms ?? {};
4369
+ const architectureRoleTerms = {};
4370
+ if (rawRoles && typeof rawRoles === "object" && !Array.isArray(rawRoles)) {
4371
+ for (const [rawRole, rawTerms] of Object.entries(rawRoles).slice(
4372
+ 0,
4373
+ LOCAL_SEMANTIC_MAX_ROLES
4374
+ )) {
4375
+ const role = rawRole.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
4376
+ if (!role) {
4377
+ warnings.push("Ignored an empty or unsupported architecture role name.");
4378
+ continue;
4379
+ }
4380
+ architectureRoleTerms[role] = localSemanticTerms(
4381
+ rawTerms,
4382
+ `architecture role ${role}`,
4383
+ warnings
4384
+ );
4385
+ }
4386
+ } else if (rawRoles) {
4387
+ warnings.push("architectureRoleTerms must be an object of role-to-term arrays.");
4388
+ }
4389
+ return {
4390
+ replaceDefaults: Boolean(raw.replaceDefaults ?? raw.replace_defaults ?? false),
4391
+ sensitivePathTerms: localSemanticTerms(
4392
+ raw.sensitivePathTerms ?? raw.sensitive_path_terms,
4393
+ "sensitive path",
4394
+ warnings
4395
+ ),
4396
+ contractPathTerms: localSemanticTerms(
4397
+ raw.contractPathTerms ?? raw.contract_path_terms,
4398
+ "contract path",
4399
+ warnings
4400
+ ),
4401
+ testPathTerms: localSemanticTerms(
4402
+ raw.testPathTerms ?? raw.test_path_terms,
4403
+ "test path",
4404
+ warnings
4405
+ ),
4406
+ architectureRoleTerms,
4407
+ source: "project-file",
4408
+ warnings
4409
+ };
4410
+ } catch (error) {
4411
+ return {
4412
+ ...defaults,
4413
+ source: "project-file",
4414
+ warnings: [
4415
+ `Could not parse ${LOCAL_SEMANTIC_RULE_CONFIG_PATH}: ${error instanceof Error ? error.message : "invalid JSON"}`
4416
+ ]
4417
+ };
4418
+ }
4419
+ }
4420
+ function localSemanticMatchesTerms(value, terms) {
4421
+ const normalized = value.replace(/\\/g, "/").toLowerCase();
4422
+ return terms.some((term) => normalized.includes(term));
4423
+ }
4424
+ function localSemanticIsTestPath(filePath, rules) {
4425
+ return !rules.replaceDefaults && LOCAL_SEMANTIC_TEST_PATH.test(filePath) || localSemanticMatchesTerms(filePath, rules.testPathTerms);
4426
+ }
4427
+ function localSemanticIsSensitivePath(filePath, rules) {
4428
+ return !rules.replaceDefaults && LOCAL_SEMANTIC_SENSITIVE_PATH.test(filePath) || localSemanticMatchesTerms(filePath, rules.sensitivePathTerms);
4429
+ }
4430
+ function localSemanticIsContractPath(filePath, rules) {
4431
+ return !rules.replaceDefaults && LOCAL_SEMANTIC_CONTRACT_PATH.test(filePath) || localSemanticMatchesTerms(filePath, rules.contractPathTerms);
4432
+ }
4314
4433
  function localSemanticEvidence(kind, detail, metadata = {}) {
4315
4434
  return { kind, detail, ...metadata };
4316
4435
  }
@@ -4322,14 +4441,16 @@ function localSemanticAssertion(subject, predicate, value, confidence, source2,
4322
4441
  predicate,
4323
4442
  value,
4324
4443
  confidence: Math.round(Math.max(0, Math.min(1, confidence)) * 1e3) / 1e3,
4444
+ scoreKind: "heuristic_prior",
4445
+ calibrated: false,
4325
4446
  source: source2,
4326
4447
  extractorVersion: 1,
4327
4448
  evidence
4328
4449
  };
4329
4450
  }
4330
- function localArchitectureRoles(symbol) {
4451
+ function localArchitectureRoles(symbol, rules) {
4331
4452
  const haystack = `${symbol.filePath} ${symbol.name}`;
4332
- const candidates = [
4453
+ const defaultCandidates = [
4333
4454
  { role: "repository", pattern: /repository|(^|[_.])repo($|[_.])/i, confidence: 0.9 },
4334
4455
  { role: "adapter", pattern: /adapter/i, confidence: 0.9 },
4335
4456
  { role: "facade", pattern: /facade/i, confidence: 0.9 },
@@ -4356,7 +4477,7 @@ function localArchitectureRoles(symbol) {
4356
4477
  )
4357
4478
  );
4358
4479
  }
4359
- if (LOCAL_SEMANTIC_TEST_PATH.test(symbol.filePath)) {
4480
+ if (localSemanticIsTestPath(symbol.filePath, rules)) {
4360
4481
  roles.push(
4361
4482
  localSemanticAssertion(
4362
4483
  symbol.localKey,
@@ -4368,7 +4489,7 @@ function localArchitectureRoles(symbol) {
4368
4489
  )
4369
4490
  );
4370
4491
  }
4371
- for (const candidate of candidates) {
4492
+ for (const candidate of rules.replaceDefaults ? [] : defaultCandidates) {
4372
4493
  if (!candidate.pattern.test(haystack)) continue;
4373
4494
  roles.push(
4374
4495
  localSemanticAssertion(
@@ -4387,9 +4508,29 @@ function localArchitectureRoles(symbol) {
4387
4508
  )
4388
4509
  );
4389
4510
  }
4511
+ const existingRoles = new Set(roles.map((assertion) => String(assertion.value)));
4512
+ for (const [role, terms] of Object.entries(rules.architectureRoleTerms)) {
4513
+ if (existingRoles.has(role) || !localSemanticMatchesTerms(haystack, terms)) continue;
4514
+ roles.push(
4515
+ localSemanticAssertion(
4516
+ symbol.localKey,
4517
+ "architecture_role",
4518
+ role,
4519
+ 0.7,
4520
+ "architecture-pattern-rules",
4521
+ [
4522
+ localSemanticEvidence(
4523
+ "project_role_term",
4524
+ `Name or path matches a configured ${role} project term.`,
4525
+ { filePath: symbol.filePath, line: symbol.line }
4526
+ )
4527
+ ]
4528
+ )
4529
+ );
4530
+ }
4390
4531
  return roles;
4391
4532
  }
4392
- function localSymbolSemanticAssertions(symbol) {
4533
+ function localSymbolSemanticAssertions(symbol, rules) {
4393
4534
  const assertions = [];
4394
4535
  const routeSurface = /\/app\/api\//i.test(symbol.filePath) || /\/route\.(ts|tsx|py)$/i.test(symbol.filePath);
4395
4536
  const toolSurface = /(^|\/)(mcp|tool_defs)(\/|\.|$)/i.test(symbol.filePath);
@@ -4445,7 +4586,7 @@ function localSymbolSemanticAssertions(symbol) {
4445
4586
  contractEvidence.push(
4446
4587
  localSemanticEvidence("symbol_kind", `Exported ${symbol.kind} defines a structural contract.`)
4447
4588
  );
4448
- } else if (LOCAL_SEMANTIC_CONTRACT_PATH.test(symbol.filePath)) {
4589
+ } else if (localSemanticIsContractPath(symbol.filePath, rules)) {
4449
4590
  contractKind = "repository_boundary_contract";
4450
4591
  contractEvidence.push(
4451
4592
  localSemanticEvidence(
@@ -4469,17 +4610,17 @@ function localSymbolSemanticAssertions(symbol) {
4469
4610
  )
4470
4611
  );
4471
4612
  }
4472
- assertions.push(...localArchitectureRoles(symbol));
4613
+ assertions.push(...localArchitectureRoles(symbol, rules));
4473
4614
  let criticality = "ordinary";
4474
4615
  let criticalityConfidence = 0.58;
4475
4616
  let criticalityEvidence = [
4476
4617
  localSemanticEvidence("default", "No critical or incidental evidence matched.")
4477
4618
  ];
4478
- if (LOCAL_SEMANTIC_TEST_PATH.test(symbol.filePath)) {
4619
+ if (localSemanticIsTestPath(symbol.filePath, rules)) {
4479
4620
  criticality = "incidental";
4480
4621
  criticalityConfidence = 0.95;
4481
4622
  criticalityEvidence = [localSemanticEvidence("test_path", "Test-only dependency.")];
4482
- } else if (LOCAL_SEMANTIC_SENSITIVE_PATH.test(symbol.filePath) || routeSurface || toolSurface) {
4623
+ } else if (localSemanticIsSensitivePath(symbol.filePath, rules) || routeSurface || toolSurface) {
4483
4624
  criticality = "critical";
4484
4625
  criticalityConfidence = 0.92;
4485
4626
  criticalityEvidence = [
@@ -4517,8 +4658,9 @@ function localSymbolSemanticAssertions(symbol) {
4517
4658
  return assertions;
4518
4659
  }
4519
4660
  function buildLocalSemanticModel(manifest, options = {}) {
4661
+ const rules = options.semanticRules ?? loadLocalSemanticRuleConfig(process.cwd());
4520
4662
  const symbols = options.symbolKeys ? manifest.symbols.filter((symbol) => options.symbolKeys?.has(symbol.localKey)) : manifest.symbols;
4521
- const assertions = symbols.flatMap(localSymbolSemanticAssertions);
4663
+ const assertions = symbols.flatMap((symbol) => localSymbolSemanticAssertions(symbol, rules));
4522
4664
  const symbolByKey = new Map(manifest.symbols.map((symbol) => [symbol.localKey, symbol]));
4523
4665
  const criticalityBySymbol = new Map(
4524
4666
  assertions.filter((assertion) => assertion.predicate === "dependency_criticality").map((assertion) => [assertion.subject, String(assertion.value)])
@@ -4555,7 +4697,7 @@ function buildLocalSemanticModel(manifest, options = {}) {
4555
4697
  ];
4556
4698
  let value = "ordinary";
4557
4699
  let confidence = 0.62;
4558
- if (LOCAL_SEMANTIC_TEST_PATH.test(fromFile) || LOCAL_SEMANTIC_TEST_PATH.test(toFile)) {
4700
+ if (localSemanticIsTestPath(fromFile, rules) || localSemanticIsTestPath(toFile, rules)) {
4559
4701
  value = "incidental";
4560
4702
  confidence = 0.92;
4561
4703
  evidence.push(localSemanticEvidence("test_path", "At least one edge endpoint is test-only."));
@@ -4626,6 +4768,40 @@ function buildLocalSemanticModel(manifest, options = {}) {
4626
4768
  version: "snipara.semantic.v1",
4627
4769
  extractorVersion: 1,
4628
4770
  source: "deterministic-graph-inference",
4771
+ modelType: "rule-based-heuristic",
4772
+ scoreContract: {
4773
+ kind: "heuristic_prior",
4774
+ calibrated: false,
4775
+ probability: false,
4776
+ comparableAcrossProjects: false,
4777
+ basis: "hand-tuned-v1",
4778
+ rulesetVersion: 1,
4779
+ interpretation: "Relative rule strength for ranking and explanation; not an observed probability."
4780
+ },
4781
+ ruleConfig: {
4782
+ version: LOCAL_SEMANTIC_RULE_CONFIG_VERSION,
4783
+ source: rules.source,
4784
+ path: ".snipara/semantic-rules.json",
4785
+ replaceDefaults: rules.replaceDefaults,
4786
+ sensitivePathTermCount: rules.sensitivePathTerms.length,
4787
+ contractPathTermCount: rules.contractPathTerms.length,
4788
+ testPathTermCount: rules.testPathTerms.length,
4789
+ architectureRoleTermCount: Object.values(rules.architectureRoleTerms).reduce(
4790
+ (total, terms) => total + terms.length,
4791
+ 0
4792
+ ),
4793
+ configuredRoles: Object.keys(rules.architectureRoleTerms).sort(),
4794
+ configHash: sha2563(
4795
+ JSON.stringify({
4796
+ replaceDefaults: rules.replaceDefaults,
4797
+ sensitivePathTerms: rules.sensitivePathTerms,
4798
+ contractPathTerms: rules.contractPathTerms,
4799
+ testPathTerms: rules.testPathTerms,
4800
+ architectureRoleTerms: rules.architectureRoleTerms
4801
+ })
4802
+ ).slice(0, 16),
4803
+ warnings: rules.warnings
4804
+ },
4629
4805
  scope: options.scope ?? "repository",
4630
4806
  assertions: returnedAssertions,
4631
4807
  publicContracts,
@@ -4663,7 +4839,9 @@ function buildLocalSemanticModel(manifest, options = {}) {
4663
4839
  },
4664
4840
  caveats: [
4665
4841
  "Semantic assertions are deterministic inferences, not declarations of author intent.",
4666
- "Name-only architecture patterns use lower confidence than explicit export or path evidence.",
4842
+ "Confidence is an uncalibrated rule-based prior, not a probability or measured accuracy.",
4843
+ "Priors are not comparable across projects with different semantic rule configuration.",
4844
+ "Name-only architecture patterns use a lower prior than explicit export or path evidence.",
4667
4845
  "Historical regression learning requires hosted outcome evidence and remains shadow-only."
4668
4846
  ]
4669
4847
  };
@@ -4735,7 +4913,8 @@ function buildLocalRisk(manifest, changedFiles, traversal, depth, semantic) {
4735
4913
  const changedSymbolKeys = new Set(
4736
4914
  manifest.symbols.filter((symbol) => changedFiles.includes(symbol.filePath)).map((symbol) => symbol.localKey)
4737
4915
  );
4738
- const changedContracts = manifest.symbols.filter((symbol) => changedSymbolKeys.has(symbol.localKey)).flatMap(localSymbolSemanticAssertions).filter(
4916
+ const semanticRules = loadLocalSemanticRuleConfig(manifest.repoRoot);
4917
+ const changedContracts = manifest.symbols.filter((symbol) => changedSymbolKeys.has(symbol.localKey)).flatMap((symbol) => localSymbolSemanticAssertions(symbol, semanticRules)).filter(
4739
4918
  (assertion) => assertion.predicate === "public_api" || assertion.predicate === "implicit_contract"
4740
4919
  ).length;
4741
4920
  const semanticRiskPoints = Math.min(20, criticalSemanticDependencies * 3) + Math.min(10, changedContracts * 2);
@@ -5800,7 +5979,8 @@ function buildLocalImpactResult(options) {
5800
5979
  const semantic = buildLocalSemanticModel(manifest, {
5801
5980
  scope: "impact",
5802
5981
  symbolKeys: semanticSymbolKeys,
5803
- edges: traversalEdges
5982
+ edges: traversalEdges,
5983
+ semanticRules: loadLocalSemanticRuleConfig(manifest.repoRoot)
5804
5984
  });
5805
5985
  const transitiveFiles = uniqueTraversalFiles(traversal.nodes, selectedFiles);
5806
5986
  const impactedFiles = [.../* @__PURE__ */ new Set([...directImpactedFiles, ...transitiveFiles])].sort();
@@ -13698,6 +13878,9 @@ var why_capture_confidence_v1_default = {
13698
13878
  var WHY_CAPTURE_CONFIDENCE_VERSION = why_capture_confidence_v1_default.version;
13699
13879
  var WHY_CAPTURE_CONFIDENCE_TEST_VECTORS = why_capture_confidence_v1_default.vectors;
13700
13880
 
13881
+ // ../project-intelligence-contracts/src/judgment-integrity.ts
13882
+ var import_node_crypto4 = require("crypto");
13883
+
13701
13884
  // src/commands/decision-requests.ts
13702
13885
  var fs12 = __toESM(require("fs"));
13703
13886
  var path11 = __toESM(require("path"));
@@ -15648,7 +15831,7 @@ var import_chalk10 = __toESM(require("chalk"));
15648
15831
  var fs27 = __toESM(require("fs"));
15649
15832
  var path26 = __toESM(require("path"));
15650
15833
  var import_node_child_process13 = require("child_process");
15651
- var import_node_crypto10 = require("crypto");
15834
+ var import_node_crypto11 = require("crypto");
15652
15835
  var import_chalk9 = __toESM(require("chalk"));
15653
15836
 
15654
15837
  // src/runtime/orchestrator-handoff.ts
@@ -16126,7 +16309,7 @@ function normalizeStringList2(values) {
16126
16309
  // src/commands/activity.ts
16127
16310
  var fs17 = __toESM(require("fs"));
16128
16311
  var path16 = __toESM(require("path"));
16129
- var import_node_crypto4 = require("crypto");
16312
+ var import_node_crypto5 = require("crypto");
16130
16313
  var ACTIVITY_TIMELINE_VERSION = "snipara.activity_timeline.v0";
16131
16314
  var SESSION_SNAPSHOT_VERSION = "snipara.session_snapshot.v0";
16132
16315
  var ACTIVITY_RELATIVE_DIR = path16.join(".snipara", "activity");
@@ -16150,7 +16333,7 @@ function sortJsonValue3(value) {
16150
16333
  return value;
16151
16334
  }
16152
16335
  function hashEvent(input) {
16153
- return (0, import_node_crypto4.createHash)("sha256").update(stableJson(input)).digest("hex").slice(0, 16);
16336
+ return (0, import_node_crypto5.createHash)("sha256").update(stableJson(input)).digest("hex").slice(0, 16);
16154
16337
  }
16155
16338
  function safeReadJson(filePath) {
16156
16339
  try {
@@ -16457,7 +16640,7 @@ async function appendJournalCheckpoint(payload) {
16457
16640
  // src/commands/coding-ledger.ts
16458
16641
  var fs18 = __toESM(require("fs"));
16459
16642
  var path17 = __toESM(require("path"));
16460
- var import_node_crypto5 = require("crypto");
16643
+ var import_node_crypto6 = require("crypto");
16461
16644
  var import_node_child_process5 = require("child_process");
16462
16645
  var CODING_INTELLIGENCE_LEDGER_VERSION = "snipara.coding_intelligence_ledger.v0";
16463
16646
  var MAX_SECTION_ITEMS = 24;
@@ -16913,7 +17096,7 @@ function itemId(section, index, value) {
16913
17096
  return `${section}:${stableHash([section, String(index), value]).slice(0, 16)}`;
16914
17097
  }
16915
17098
  function stableHash(parts) {
16916
- return (0, import_node_crypto5.createHash)("sha256").update(parts.join("\0")).digest("hex");
17099
+ return (0, import_node_crypto6.createHash)("sha256").update(parts.join("\0")).digest("hex");
16917
17100
  }
16918
17101
  function stringValue5(value) {
16919
17102
  if (typeof value === "string") {
@@ -17244,7 +17427,7 @@ function toProjectRelativePath2(absolutePath, cwd) {
17244
17427
 
17245
17428
  // src/commands/collaboration.ts
17246
17429
  var import_node_child_process6 = require("child_process");
17247
- var import_node_crypto6 = require("crypto");
17430
+ var import_node_crypto7 = require("crypto");
17248
17431
  var fs20 = __toESM(require("fs"));
17249
17432
  var os6 = __toESM(require("os"));
17250
17433
  var path19 = __toESM(require("path"));
@@ -18973,7 +19156,7 @@ function printGuardActionCards(cards) {
18973
19156
  }
18974
19157
  }
18975
19158
  function buildLocalSessionId(context, task, files) {
18976
- const hash = (0, import_node_crypto6.createHash)("sha1").update([context.actor.actorId, context.branch ?? "", task ?? "", files.join(",")].join("\n")).digest("hex").slice(0, 12);
19159
+ const hash = (0, import_node_crypto7.createHash)("sha1").update([context.actor.actorId, context.branch ?? "", task ?? "", files.join(",")].join("\n")).digest("hex").slice(0, 12);
18977
19160
  return `local_${hash}`;
18978
19161
  }
18979
19162
  function normalizeFiles(files) {
@@ -19078,7 +19261,7 @@ function fingerprintReviewOnlyGuardEvaluation(evaluation) {
19078
19261
  workSessionId: conflict.workSessionId ?? null,
19079
19262
  reason: conflict.reason
19080
19263
  })).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
19081
- return (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify(findings)).digest("hex");
19264
+ return (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(findings)).digest("hex");
19082
19265
  }
19083
19266
  function persistReviewOnlyAcknowledgement(state, profile, action, findingFingerprint, now = /* @__PURE__ */ new Date()) {
19084
19267
  invalidateReviewOnlyAcknowledgements(state, profile, action);
@@ -19211,7 +19394,7 @@ function sleep2(ms) {
19211
19394
  var fs22 = __toESM(require("fs"));
19212
19395
  var path21 = __toESM(require("path"));
19213
19396
  var import_node_child_process8 = require("child_process");
19214
- var import_node_crypto7 = require("crypto");
19397
+ var import_node_crypto8 = require("crypto");
19215
19398
 
19216
19399
  // src/commands/why-capture.ts
19217
19400
  var import_node_child_process7 = require("child_process");
@@ -21179,7 +21362,7 @@ function formatError2(error) {
21179
21362
  return error instanceof Error ? error.message : String(error);
21180
21363
  }
21181
21364
  function buildRecordId(prefix, createdAt, summary, files) {
21182
- const hash = (0, import_node_crypto7.createHash)("sha1").update([createdAt, summary, files.join(",")].join("\n")).digest("hex").slice(0, 10);
21365
+ const hash = (0, import_node_crypto8.createHash)("sha1").update([createdAt, summary, files.join(",")].join("\n")).digest("hex").slice(0, 10);
21183
21366
  return `${prefix}_${hash}`;
21184
21367
  }
21185
21368
 
@@ -22084,7 +22267,7 @@ function printKeyValue(label, value) {
22084
22267
  }
22085
22268
 
22086
22269
  // src/commands/final-commit-report.ts
22087
- var import_node_crypto8 = require("crypto");
22270
+ var import_node_crypto9 = require("crypto");
22088
22271
  var import_node_child_process10 = require("child_process");
22089
22272
  var fs24 = __toESM(require("fs"));
22090
22273
  var path23 = __toESM(require("path"));
@@ -22365,7 +22548,7 @@ function writeFinalCommitReport(report, cwd = process.cwd()) {
22365
22548
  version: FINAL_COMMIT_REPORT_VERSION,
22366
22549
  path: outputPath,
22367
22550
  relativePath: FINAL_COMMIT_REPORT_RELATIVE_PATH,
22368
- hash: `sha256:${(0, import_node_crypto8.createHash)("sha256").update(content).digest("hex")}`
22551
+ hash: `sha256:${(0, import_node_crypto9.createHash)("sha256").update(content).digest("hex")}`
22369
22552
  };
22370
22553
  } catch (error) {
22371
22554
  return {
@@ -22437,7 +22620,7 @@ function formatFinalCommitReport(report) {
22437
22620
 
22438
22621
  // src/commands/run.ts
22439
22622
  var import_node_child_process12 = require("child_process");
22440
- var import_node_crypto9 = require("crypto");
22623
+ var import_node_crypto10 = require("crypto");
22441
22624
  var fs26 = __toESM(require("fs"));
22442
22625
  var path25 = __toESM(require("path"));
22443
22626
  var import_chalk8 = __toESM(require("chalk"));
@@ -24537,7 +24720,7 @@ function buildProjectIntelligenceRunEnvelope(startedAt = /* @__PURE__ */ new Dat
24537
24720
  const codexSessionId = stringValue10(process.env.CODEX_SESSION_ID)?.slice(0, 200);
24538
24721
  return {
24539
24722
  version: "project-intelligence.judgment-run-envelope.v1",
24540
- runId: sniparaSessionId ?? codexSessionId ?? `judgment-run:${(0, import_node_crypto9.randomUUID)()}`,
24723
+ runId: sniparaSessionId ?? codexSessionId ?? `judgment-run:${(0, import_node_crypto10.randomUUID)()}`,
24541
24724
  identitySource: sniparaSessionId ? "snipara_session" : codexSessionId ? "codex_session" : "generated",
24542
24725
  startedAt: startedAt.toISOString()
24543
24726
  };
@@ -24545,6 +24728,24 @@ function buildProjectIntelligenceRunEnvelope(startedAt = /* @__PURE__ */ new Dat
24545
24728
  function normalizeStringList5(values) {
24546
24729
  return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
24547
24730
  }
24731
+ function localCodeContext(branch) {
24732
+ const context = {};
24733
+ if (branch?.trim()) context.branch = branch.trim();
24734
+ try {
24735
+ const localHeadSha = (0, import_node_child_process12.execFileSync)("git", ["rev-parse", "--verify", "HEAD"], {
24736
+ encoding: "utf8",
24737
+ stdio: ["ignore", "pipe", "ignore"]
24738
+ }).trim();
24739
+ if (localHeadSha) context.localHeadSha = localHeadSha;
24740
+ const status = (0, import_node_child_process12.execFileSync)("git", ["status", "--porcelain"], {
24741
+ encoding: "utf8",
24742
+ stdio: ["ignore", "pipe", "ignore"]
24743
+ }).trim();
24744
+ context.workingTreeDirty = status.length > 0;
24745
+ } catch {
24746
+ }
24747
+ return context;
24748
+ }
24548
24749
  function uniqueStrings20(values) {
24549
24750
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
24550
24751
  }
@@ -24778,6 +24979,9 @@ function advisorReceiptMetadata(args) {
24778
24979
  firstParty: true,
24779
24980
  runVersion: "project-intelligence.production-run.v1",
24780
24981
  runId: args.runEnvelope.runId,
24982
+ servedJudgmentId: args.hostedJudgment?.servedJudgmentId ?? null,
24983
+ judgmentSnapshotId: args.hostedJudgment?.judgmentSnapshotId ?? null,
24984
+ judgmentExposureId: args.hostedJudgment?.judgmentExposureId ?? null,
24781
24985
  runEnvelope: args.runEnvelope,
24782
24986
  generatedAt: args.judgmentCard.generatedAt,
24783
24987
  release: Boolean(args.options.release),
@@ -25039,6 +25243,9 @@ async function recordFirstPartyAdvisorReceipts(args) {
25039
25243
  });
25040
25244
  const result = await client.recordAdvisorInfluenceReceipt({
25041
25245
  servedJudgmentId,
25246
+ ...args.hostedJudgment?.judgmentSnapshotId ? { judgmentSnapshotId: args.hostedJudgment.judgmentSnapshotId } : {},
25247
+ ...args.hostedJudgment?.judgmentExposureId ? { judgmentExposureId: args.hostedJudgment.judgmentExposureId } : {},
25248
+ ...args.hostedJudgment?.status === "linked" ? { runId: args.runEnvelope.runId } : {},
25042
25249
  recommendation: advisorReceiptRecommendation(recommendation),
25043
25250
  agentDecision,
25044
25251
  behaviorChange,
@@ -25057,7 +25264,8 @@ async function recordFirstPartyAdvisorReceipts(args) {
25057
25264
  lifecycle,
25058
25265
  planScope,
25059
25266
  measurementState,
25060
- runEnvelope: args.runEnvelope
25267
+ runEnvelope: args.runEnvelope,
25268
+ hostedJudgment: args.hostedJudgment
25061
25269
  })
25062
25270
  });
25063
25271
  return {
@@ -25207,7 +25415,12 @@ async function buildProjectIntelligenceRun(options) {
25207
25415
  ).createProjectIntelligenceBrief({
25208
25416
  ...options.task ? { task: options.task } : {},
25209
25417
  ...options.diffSummary ? { diffSummary: options.diffSummary } : {},
25210
- ...changedFiles.length > 0 ? { changedFiles: changedFiles.slice(0, 120) } : {}
25418
+ ...changedFiles.length > 0 ? { changedFiles: changedFiles.slice(0, 120) } : {},
25419
+ codeContext: localCodeContext(options.branch),
25420
+ correlation: {
25421
+ surface: "companion",
25422
+ runId: runEnvelope.runId
25423
+ }
25211
25424
  });
25212
25425
  if (hostedProjectIntelligenceBrief.servedJudgmentId) {
25213
25426
  brief.servedJudgmentId = hostedProjectIntelligenceBrief.servedJudgmentId;
@@ -25215,7 +25428,9 @@ async function buildProjectIntelligenceRun(options) {
25215
25428
  version: "project-intelligence.hosted-judgment-link.v1",
25216
25429
  status: "linked",
25217
25430
  timeoutMs: HOSTED_PROJECT_INTELLIGENCE_BRIEF_TIMEOUT_MS,
25218
- servedJudgmentId: hostedProjectIntelligenceBrief.servedJudgmentId
25431
+ servedJudgmentId: hostedProjectIntelligenceBrief.servedJudgmentId,
25432
+ ...hostedProjectIntelligenceBrief.judgmentSnapshotId ? { judgmentSnapshotId: hostedProjectIntelligenceBrief.judgmentSnapshotId } : {},
25433
+ ...hostedProjectIntelligenceBrief.judgmentExposureId ? { judgmentExposureId: hostedProjectIntelligenceBrief.judgmentExposureId } : {}
25219
25434
  };
25220
25435
  } else {
25221
25436
  hostedJudgment = {
@@ -25305,7 +25520,8 @@ async function buildProjectIntelligenceRun(options) {
25305
25520
  judgmentCard,
25306
25521
  verificationEvidence: verificationEvidence2,
25307
25522
  outcomeReceipts,
25308
- runEnvelope
25523
+ runEnvelope,
25524
+ hostedJudgment
25309
25525
  });
25310
25526
  const suggestedCommands3 = [
25311
25527
  ...brief.suggestedCommands,
@@ -25706,7 +25922,7 @@ function contentBufferForHash(content) {
25706
25922
  return Buffer.from(content, "utf8");
25707
25923
  }
25708
25924
  function hashContent(content) {
25709
- return (0, import_node_crypto10.createHash)("sha256").update(contentBufferForHash(content)).digest("hex");
25925
+ return (0, import_node_crypto11.createHash)("sha256").update(contentBufferForHash(content)).digest("hex");
25710
25926
  }
25711
25927
  function toPreview(value, maxLength = 160) {
25712
25928
  if (typeof value === "string") {
@@ -28377,7 +28593,7 @@ function findSandboxRuntimeBinding(state, phaseId) {
28377
28593
  function defaultSandboxSessionId(state, phase) {
28378
28594
  const workflowSlug = sanitizeWorkflowId(state.workflowId, "workflow").slice(0, 24);
28379
28595
  const phaseSlug = sanitizeWorkflowId(phase.id, "phase").slice(0, 24);
28380
- const digest = (0, import_node_crypto10.createHash)("sha1").update(`${state.workflowId}:${phase.id}`).digest("hex").slice(0, 10);
28596
+ const digest = (0, import_node_crypto11.createHash)("sha1").update(`${state.workflowId}:${phase.id}`).digest("hex").slice(0, 10);
28381
28597
  return `sandbox-${workflowSlug}-${phaseSlug}-${digest}`.slice(0, 96);
28382
28598
  }
28383
28599
  function ensureSandboxRuntimeBinding(state, phase, now) {
@@ -35082,7 +35298,7 @@ async function sourceWatchCommand(options = {}) {
35082
35298
  var import_fs5 = __toESM(require("fs"));
35083
35299
  var import_path5 = __toESM(require("path"));
35084
35300
  var import_node_child_process14 = require("child_process");
35085
- var import_node_crypto11 = require("crypto");
35301
+ var import_node_crypto12 = require("crypto");
35086
35302
  var import_chalk11 = __toESM(require("chalk"));
35087
35303
  var CONTROLLED_WORKER_EXECUTIONS_DIR = import_path5.default.join(".snipara", "worker-executions");
35088
35304
  var UNIFIED_RECEIPTS_DIR = import_path5.default.join(".snipara", "unified-receipts");
@@ -35350,7 +35566,7 @@ function pathFingerprint(workspaceRoot2, relativePath2) {
35350
35566
  const stat = import_fs5.default.lstatSync(candidate);
35351
35567
  if (stat.isSymbolicLink()) return `symlink:${import_fs5.default.readlinkSync(candidate)}`;
35352
35568
  if (!stat.isFile()) return `mode:${stat.mode}:size:${stat.size}`;
35353
- return `file:${stat.mode}:${(0, import_node_crypto11.createHash)("sha256").update(import_fs5.default.readFileSync(candidate)).digest("hex")}`;
35569
+ return `file:${stat.mode}:${(0, import_node_crypto12.createHash)("sha256").update(import_fs5.default.readFileSync(candidate)).digest("hex")}`;
35354
35570
  } catch {
35355
35571
  return "missing";
35356
35572
  }
@@ -37649,7 +37865,7 @@ async function leadPlanCommand(options) {
37649
37865
  // src/commands/outcome-capture.ts
37650
37866
  var fs33 = __toESM(require("fs"));
37651
37867
  var path32 = __toESM(require("path"));
37652
- var import_node_crypto12 = require("crypto");
37868
+ var import_node_crypto13 = require("crypto");
37653
37869
  var WHY_OUTCOME_CAPTURE_VERSION = "snipara.why_outcome_capture.v1";
37654
37870
  var DEFAULT_MAX_CANDIDATES = 20;
37655
37871
  var MAX_EVENTS = 50;
@@ -38178,7 +38394,7 @@ function readEventsFromFile(filePath) {
38178
38394
  return [];
38179
38395
  }
38180
38396
  function stableHash2(parts) {
38181
- return (0, import_node_crypto12.createHash)("sha256").update(parts.join("\0")).digest("hex");
38397
+ return (0, import_node_crypto13.createHash)("sha256").update(parts.join("\0")).digest("hex");
38182
38398
  }
38183
38399
  function uniqueStrings22(values) {
38184
38400
  return Array.from(new Set(values.filter(Boolean)));
@@ -14,7 +14,9 @@ contains:
14
14
  - `predicate`: `public_api`, `implicit_contract`, `architecture_role`, or
15
15
  `dependency_criticality`;
16
16
  - `value`: the inferred classification;
17
- - `confidence`: a bounded score from `0` to `1`;
17
+ - `confidence`: a backward-compatible bounded rule-strength prior from `0` to
18
+ `1`, explicitly qualified by `scoreKind: "heuristic_prior"` and
19
+ `calibrated: false`;
18
20
  - `source` and `extractorVersion`;
19
21
  - `evidence`: the export modifier, symbol kind, file/path pattern, or graph edge
20
22
  that produced the assertion.
@@ -22,8 +24,37 @@ contains:
22
24
  The extractor recognizes explicit TypeScript and Go exports, route and MCP
23
25
  surfaces, exported type contracts, and common Repository, Adapter, Facade,
24
26
  Controller, Service, Worker, Factory, schema, route-handler, and test roles.
25
- Name-only patterns have lower confidence than explicit export, route, or graph
26
- evidence.
27
+ `confidence` is not a probability, measured accuracy, or cross-project metric.
28
+ It is a deterministic prior used for relative ranking and explanation inside
29
+ one rule configuration. `semantic.scoreContract.basis` is
30
+ `hand-tuned-v1`; the values must be calibrated against measured outcomes before
31
+ that status can change. Name-only patterns have a lower prior than explicit
32
+ export, route, or graph evidence.
33
+
34
+ ### Project naming rules
35
+
36
+ Projects can extend or replace built-in English naming conventions with
37
+ `.snipara/semantic-rules.json`:
38
+
39
+ ```json
40
+ {
41
+ "replaceDefaults": false,
42
+ "sensitivePathTerms": ["securite", "facturation"],
43
+ "contractPathTerms": ["contrats"],
44
+ "testPathTerms": ["essais"],
45
+ "architectureRoleTerms": {
46
+ "repository": ["depot"],
47
+ "service": ["metier"]
48
+ }
49
+ }
50
+ ```
51
+
52
+ Terms are case-insensitive literal strings, not regular expressions. Each group
53
+ is limited to 64 terms of at most 80 characters and role names are normalized.
54
+ Invalid config is reported in `semantic.ruleConfig.warnings`; it never aborts
55
+ overlay construction. Set `replaceDefaults: true` only when the project wants
56
+ configured naming terms to replace built-in naming conventions. Explicit
57
+ exports, route surfaces, and graph evidence continue to apply.
27
58
 
28
59
  ## Dependency criticality
29
60
 
@@ -51,8 +82,9 @@ does not manufacture cross-source edges or assertions.
51
82
  ## Historical regression paths
52
83
 
53
84
  Hosted impact can associate sanitized warning/error execution traces with
54
- graph paths by their tracked files. Version 1 reports sample count, confidence,
55
- event IDs, and a suggested risk delta. This surface is intentionally
85
+ graph paths by their tracked files. Version 1 reports sample count, an
86
+ uncalibrated shadow association strength, event IDs, and a suggested risk
87
+ delta. This surface is intentionally
56
88
  `mode: shadow` and `riskContributionEnabled: false`: temporal association is
57
89
  not causal proof, and it cannot affect risk until separately calibrated and
58
90
  promoted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.6.2",
3
+ "version": "3.6.4",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {