snipara-companion 3.6.2 → 3.6.3

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,17 @@
2
2
 
3
3
  Release notes for `snipara-companion`, newest first.
4
4
 
5
+ ## New In 3.6.3
6
+
7
+ - Labels every semantic `confidence` value as an uncalibrated
8
+ `heuristic_prior`, preserving the numeric compatibility field without
9
+ presenting it as a measured probability.
10
+ - Loads bounded, literal project naming terms from
11
+ `.snipara/semantic-rules.json`; teams can extend or replace English defaults
12
+ without enabling arbitrary regular expressions.
13
+ - Keeps historical regression associations strictly shadow-only and marks
14
+ their association strength as uncalibrated.
15
+
5
16
  ## New In 3.6.2
6
17
 
7
18
  - Adds `snipara.semantic.v1` to local code overlays and impact results with
package/dist/index.d.ts CHANGED
@@ -1825,6 +1825,8 @@ interface LocalSemanticAssertion {
1825
1825
  predicate: LocalSemanticPredicate;
1826
1826
  value: string | boolean;
1827
1827
  confidence: number;
1828
+ scoreKind: "heuristic_prior";
1829
+ calibrated: false;
1828
1830
  source: "typescript-compiler-api" | "language-parser" | "architecture-pattern-rules" | "criticality-rules" | "graph-criticality-rules";
1829
1831
  extractorVersion: 1;
1830
1832
  evidence: LocalSemanticEvidence[];
@@ -1833,6 +1835,29 @@ interface LocalSemanticModel {
1833
1835
  version: "snipara.semantic.v1";
1834
1836
  extractorVersion: 1;
1835
1837
  source: "deterministic-graph-inference";
1838
+ modelType: "rule-based-heuristic";
1839
+ scoreContract: {
1840
+ kind: "heuristic_prior";
1841
+ calibrated: false;
1842
+ probability: false;
1843
+ comparableAcrossProjects: false;
1844
+ basis: "hand-tuned-v1";
1845
+ rulesetVersion: 1;
1846
+ interpretation: string;
1847
+ };
1848
+ ruleConfig: {
1849
+ version: "snipara.semantic.rules.v1";
1850
+ source: "defaults" | "project-file";
1851
+ path: ".snipara/semantic-rules.json";
1852
+ replaceDefaults: boolean;
1853
+ sensitivePathTermCount: number;
1854
+ contractPathTermCount: number;
1855
+ testPathTermCount: number;
1856
+ architectureRoleTermCount: number;
1857
+ configuredRoles: string[];
1858
+ configHash: string;
1859
+ warnings: string[];
1860
+ };
1836
1861
  scope: "repository" | "impact";
1837
1862
  assertions: LocalSemanticAssertion[];
1838
1863
  publicContracts: LocalSemanticAssertion[];
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();
@@ -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.3",
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": {