knodin 0.8.2 → 0.8.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.
@@ -48,6 +48,71 @@ const PROTECTED_CONTRACT_KEYS = new Set([
48
48
  function isTruncatableDetail(entry) {
49
49
  return typeof entry.key !== "string" || !PROTECTED_CONTRACT_KEYS.has(entry.key);
50
50
  }
51
+ function synchronizeNestedCapabilityBudget(root, metadata, operation) {
52
+ const cross = root.crossSubstratePath;
53
+ if (!cross)
54
+ return;
55
+ if (metadata.truncated) {
56
+ cross.truncated = true;
57
+ cross.continuation = `Repeat ${operation} with a larger byte/token budget to recover complete source evidence.`;
58
+ const omission = "Flow or Apex source evidence was shortened by the response budget.";
59
+ cross.omissions ??= [];
60
+ if (!cross.omissions.includes(omission))
61
+ cross.omissions.push(omission);
62
+ }
63
+ const measuredBudget = {
64
+ byteLimit: metadata.byteLimit,
65
+ tokenLimit: metadata.tokenLimit,
66
+ itemLimit: metadata.itemLimit,
67
+ serializedBytes: 0,
68
+ estimatedTokens: 0,
69
+ };
70
+ cross.budget = cross.budget ? { ...cross.budget, ...measuredBudget } : measuredBudget;
71
+ for (let iteration = 0; iteration < 4; iteration++) {
72
+ const serializedBytes = bytes(cross);
73
+ const estimatedTokens = Math.ceil(serializedBytes / 4);
74
+ if (cross.budget.serializedBytes === serializedBytes &&
75
+ cross.budget.estimatedTokens === estimatedTokens)
76
+ break;
77
+ cross.budget.serializedBytes = serializedBytes;
78
+ cross.budget.estimatedTokens = estimatedTokens;
79
+ }
80
+ }
81
+ function truncatePayloadToBytes(root, byteLimit) {
82
+ let truncated = false;
83
+ while (bytes(root) > byteLimit) {
84
+ const candidate = collectStrings(root)
85
+ .filter((entry) => !entry.path.startsWith("$/responseBudget") &&
86
+ entry.value.length > 0 &&
87
+ isTruncatableDetail(entry))
88
+ .sort((a, b) => b.value.length - a.value.length)[0];
89
+ if (!candidate)
90
+ break;
91
+ const marker = "\n… [truncated]";
92
+ if (candidate.value.length <= marker.length + 1) {
93
+ if (Array.isArray(candidate.parent))
94
+ candidate.parent.splice(candidate.key, 1);
95
+ else
96
+ delete candidate.parent[candidate.key];
97
+ }
98
+ else {
99
+ const keep = Math.max(0, Math.floor((candidate.value.length - marker.length) / 2));
100
+ candidate.parent[candidate.key] =
101
+ `${candidate.value.slice(0, keep)}${marker}`;
102
+ }
103
+ truncated = true;
104
+ }
105
+ while (bytes(root) > byteLimit) {
106
+ const candidate = collectArrays(root)
107
+ .filter((entry) => !entry.path.startsWith("$/responseBudget") && entry.value.length > 0)
108
+ .sort((a, b) => bytes(b.value) - bytes(a.value))[0];
109
+ if (!candidate)
110
+ break;
111
+ candidate.value.pop();
112
+ truncated = true;
113
+ }
114
+ return truncated;
115
+ }
51
116
  /**
52
117
  * Applies limits to the value that is actually JSON-serialized. Token accounting
53
118
  * intentionally uses the deterministic local estimate of four UTF-8 bytes/token;
@@ -98,38 +163,7 @@ export function applyResponseBudget(value, operation, request, defaults) {
98
163
  root.responseBudget = metadata;
99
164
  // Preserve collection contracts and identifiers where possible: large source,
100
165
  // artifact and diagnostic strings are the first expendable detail.
101
- while (bytes(root) > byteLimit) {
102
- const candidate = collectStrings(root)
103
- .filter((entry) => !entry.path.startsWith("$/responseBudget") &&
104
- entry.value.length > 0 &&
105
- isTruncatableDetail(entry))
106
- .sort((a, b) => b.value.length - a.value.length)[0];
107
- if (!candidate)
108
- break;
109
- const marker = "\n… [truncated]";
110
- if (candidate.value.length <= marker.length + 1) {
111
- if (Array.isArray(candidate.parent))
112
- candidate.parent.splice(candidate.key, 1);
113
- else
114
- delete candidate.parent[candidate.key];
115
- truncated = true;
116
- continue;
117
- }
118
- const keep = Math.max(0, Math.floor((candidate.value.length - marker.length) / 2));
119
- candidate.parent[candidate.key] =
120
- `${candidate.value.slice(0, keep)}${marker}`;
121
- truncated = true;
122
- }
123
- // Only drop tail items after scalar detail has been exhausted.
124
- while (bytes(root) > byteLimit) {
125
- const candidate = collectArrays(root)
126
- .filter((entry) => !entry.path.startsWith("$/responseBudget") && entry.value.length > 0)
127
- .sort((a, b) => bytes(b.value) - bytes(a.value))[0];
128
- if (!candidate)
129
- break;
130
- candidate.value.pop();
131
- truncated = true;
132
- }
166
+ truncated = truncatePayloadToBytes(root, byteLimit) || truncated;
133
167
  metadata.truncated = truncated;
134
168
  if (truncated) {
135
169
  metadata.continuation = {
@@ -138,6 +172,7 @@ export function applyResponseBudget(value, operation, request, defaults) {
138
172
  instruction: `Repeat ${operation} with a narrower selector, larger budget, or query drill-down.`,
139
173
  };
140
174
  }
175
+ synchronizeNestedCapabilityBudget(root, metadata, operation);
141
176
  // Metadata changes the byte count, so converge after setting it. The reserve
142
177
  // above is normally enough; this final pass handles unusually long paths.
143
178
  metadata.serializedBytes = bytes(root);
@@ -147,42 +182,11 @@ export function applyResponseBudget(value, operation, request, defaults) {
147
182
  metadata.serializedBytes = bytes(root);
148
183
  metadata.estimatedTokens = Math.ceil(metadata.serializedBytes / 4);
149
184
  }
150
- while (bytes(root) > byteLimit) {
151
- const candidate = collectStrings(root)
152
- .filter((entry) => !entry.path.startsWith("$/responseBudget") &&
153
- entry.value.length > 0 &&
154
- isTruncatableDetail(entry))
155
- .sort((a, b) => b.value.length - a.value.length)[0];
156
- if (!candidate)
157
- break;
158
- const marker = "\n… [truncated]";
159
- if (candidate.value.length <= marker.length + 1) {
160
- if (Array.isArray(candidate.parent))
161
- candidate.parent.splice(candidate.key, 1);
162
- else
163
- delete candidate.parent[candidate.key];
164
- }
165
- else {
166
- const keep = Math.max(0, Math.floor((candidate.value.length - marker.length) / 2));
167
- candidate.parent[candidate.key] =
168
- `${candidate.value.slice(0, keep)}${marker}`;
169
- }
170
- metadata.truncated = true;
171
- }
185
+ metadata.truncated = truncatePayloadToBytes(root, byteLimit) || metadata.truncated;
172
186
  metadata.serializedBytes = bytes(root);
173
187
  metadata.estimatedTokens = Math.ceil(metadata.serializedBytes / 4);
174
- while (bytes(root) > byteLimit) {
175
- const candidate = collectArrays(root)
176
- .filter((entry) => !entry.path.startsWith("$/responseBudget") && entry.value.length > 0)
177
- .sort((a, b) => bytes(b.value) - bytes(a.value))[0];
178
- if (!candidate)
179
- break;
180
- candidate.value.pop();
181
- metadata.truncated = true;
182
- metadata.serializedBytes = bytes(root);
183
- metadata.estimatedTokens = Math.ceil(metadata.serializedBytes / 4);
184
- }
185
188
  for (let iteration = 0; iteration < 8; iteration++) {
189
+ synchronizeNestedCapabilityBudget(root, metadata, operation);
186
190
  const actual = bytes(root);
187
191
  const tokens = Math.ceil(actual / 4);
188
192
  if (metadata.serializedBytes === actual && metadata.estimatedTokens === tokens)
@@ -116,6 +116,7 @@ const QUERY_PATTERNS = [
116
116
  "batch_outline",
117
117
  "project_overview",
118
118
  "shortest_path",
119
+ "cross_substrate_path",
119
120
  "impact",
120
121
  "dead_code",
121
122
  "large_functions",
@@ -127,6 +128,7 @@ const QUERY_PATTERNS = [
127
128
  "traverse",
128
129
  "feature_path",
129
130
  "flow_analysis",
131
+ "resource_reachability",
130
132
  "knowledge_gaps",
131
133
  "surprising_connections",
132
134
  "suggested_questions",
@@ -227,15 +229,15 @@ function buildDocumentedKnodinTools() {
227
229
  },
228
230
  toIdentity: {
229
231
  type: "string",
230
- description: "shortest_path destination identity selector.",
232
+ description: "shortest_path/cross_substrate_path destination identity selector.",
231
233
  },
232
234
  toFile: {
233
235
  type: "string",
234
- description: "shortest_path destination file selector.",
236
+ description: "shortest_path/cross_substrate_path destination file selector.",
235
237
  },
236
238
  toKind: {
237
239
  type: "string",
238
- description: "shortest_path destination kind selector.",
240
+ description: "shortest_path/cross_substrate_path destination kind selector.",
239
241
  },
240
242
  pattern: {
241
243
  type: "string",
@@ -243,7 +245,7 @@ function buildDocumentedKnodinTools() {
243
245
  description: "feature_path — deterministic downstream DFS over resolved source references. It is bounded by depth and item limits, source-evidenced per hop, cycle-guarded, and never claims runtime execution; dynamic dispatch and unresolved calls are omitted. " +
244
246
  "lsp_diagnostics accepts a repo-relative TypeScript/JavaScript file; lsp_definitions, lsp_declarations, and lsp_implementations accept file:line:column. These use an optional local TypeScript language-service adapter, never start a daemon or write files, and return an explicit unavailable result when no local adapter supports the file type. " +
245
247
  "api_contract_mismatches — bounded, source-evidenced static mismatch findings for literal Express/Fastify routes and literal fetch/Axios clients. Matching requires method and normalized path, and requires exact origin equality when a client uses an absolute origin; dynamic routes remain explicitly unresolved. mcp_tools — list indexed TypeScript MCP SDK tool registrations, or pass symbol to look up one exact tool name; returns description/schema/handler/file and exact versus heuristic confidence. import_cycles — repo-wide canonical directed file-import cycles; no symbol required, type-only imports included, non-import lineage/ORM edges excluded, and limit/truncated bounds output. " +
246
- "query: the graph pattern to run (callers_of, callees_of, imports_of, importers_of, inheritors_of, structural_implementations_of, tests_for, file_summary, shortest_path, impact, dead_code, large_functions, large_files, rename_preview, flows, flow_of, stats, traverse, knowledge_gaps, community, federated_repos). structural_implementations_of returns TypeScript structurally typed object implementations separately from nominal extends/implements results. federated_repos — discover and list all registered/configured repository paths that this engine federates queries across. community — fetch a single community's full detail (name, size, cohesion, files, and symbols) by name or substring filter; empty `symbol` = all communities. large_files — whole-file line counts (min 200), sorted descending — the file-level counterpart to large_functions, for spotting god files rather than god functions; same optional path-substring filter, empty `symbol` = all files. stats — repo-level index size + health in one call (per-repo symbol/reference/dependency/embedding/file counts, per-kind and per-language breakdowns, orphaned-embedding count, schema version, last-indexed HEAD, and cached community/hub/bridge/flow totals); federated, empty `symbol`. traverse — typed BFS from `symbol` within `depth` hops (default 3, 1–6), selectable upstream/downstream/both and explicit edge kinds; every discovered edge includes direction, kind, provenance, confidence, files, and source line, with optional Tree-sitter-grounded argument expressions honestly marked heuristic. Sets `truncated` when the `limit` cap binds; single-repo. knowledge_gaps — repo-health weaknesses in one call: thin communities (<3 symbols), single-file communities, isolated (zero-degree) symbols, and untested hub/bridge hotspots; sourced from the cached map(), federated, empty `symbol`, one row carrying a `knowledgeGaps` block. surprising_connections — resolved edges scored by a composite surprise formula (cross-community, cross-language, peripheral-to-hub, cross-test-boundary, unusual edge kind) to surface unexpected coupling; sorted highest-first, top 15 by default, federated, empty `symbol`, each row carrying a `surprise` block. suggested_questions — prioritized, human-readable review prompts synthesized from knowledge_gaps + surprising_connections (untested hotspots first, then surprising edges, then thin/single-file communities); federated, empty `symbol`, each row a `question` string citing the real symbol/file, top 10 by default. architecture_overview — a federated architecture view with scoped community coupling and independently selectable packages, layers, boundaries, hotspots, entry points, and language facets; `path` is segment-safe and applies consistently. `detailLevel` 'minimal' omits per-edge coupling; 'standard' includes it. Spring/event patterns (Java): triggers_of — the schedule edge between a @Scheduled method and its synthetic scheduler (pass the method to get its scheduler, or the scheduler to list scheduled methods). publishers_of — methods that publish a given event type (ApplicationEventPublisher.publishEvent). listeners_of — @EventListener methods that listen for a given event type. handlers_of — methods that handle a given HTTP endpoint path (Spring @*Mapping + the existing JS/Python endpoints). endpoints_for — the inverse: endpoint path(s) a given method handles. consumers_of — classes/methods consuming a given @Value config property. children_of — symbols contained in a file path (same as file_summary) or, for a class name, its member symbols by line-range containment. All seven require `symbol` (the target); single-repo, not federated.",
248
+ "query: the graph pattern to run (callers_of, callees_of, imports_of, importers_of, inheritors_of, structural_implementations_of, tests_for, file_summary, shortest_path, cross_substrate_path, impact, dead_code, large_functions, large_files, rename_preview, flows, flow_of, flow_analysis, resource_reachability, stats, traverse, knowledge_gaps, community, federated_repos). cross_substrate_path proves only one static Salesforce Flow action to one uniquely resolved Apex @InvocableMethod and returns exact endpoint identities/evidence, freshness, budgets, and explicit unsupported crossings; it does not resolve Terraform, dbt, or arbitrary substrate paths. resource_reachability is a repo-wide, bounded, cached, on-demand TS/JS static heuristic for literal process.env/fs.readFileSync sources reaching console.log/fetch/db.query sinks; it reports evidence, coverage, omissions, freshness, truncation, and continuation and is never runtime reachability or exploitability proof. structural_implementations_of returns TypeScript structurally typed object implementations separately from nominal extends/implements results. federated_repos — discover and list all registered/configured repository paths that this engine federates queries across. community — fetch a single community's full detail (name, size, cohesion, files, and symbols) by name or substring filter; empty `symbol` = all communities. large_files — whole-file line counts (min 200), sorted descending — the file-level counterpart to large_functions, for spotting god files rather than god functions; same optional path-substring filter, empty `symbol` = all files. stats — repo-level index size + health in one call (per-repo symbol/reference/dependency/embedding/file counts, per-kind and per-language breakdowns, orphaned-embedding count, schema version, last-indexed HEAD, and cached community/hub/bridge/flow totals); federated, empty `symbol`. traverse — typed BFS from `symbol` within `depth` hops (default 3, 1–6), selectable upstream/downstream/both and explicit edge kinds; every discovered edge includes direction, kind, provenance, confidence, files, and source line, with optional Tree-sitter-grounded argument expressions honestly marked heuristic. Sets `truncated` when the `limit` cap binds; single-repo. knowledge_gaps — repo-health weaknesses in one call: thin communities (<3 symbols), single-file communities, isolated (zero-degree) symbols, and untested hub/bridge hotspots; sourced from the cached map(), federated, empty `symbol`, one row carrying a `knowledgeGaps` block. surprising_connections — resolved edges scored by a composite surprise formula (cross-community, cross-language, peripheral-to-hub, cross-test-boundary, unusual edge kind) to surface unexpected coupling; sorted highest-first, top 15 by default, federated, empty `symbol`, each row carrying a `surprise` block. suggested_questions — prioritized, human-readable review prompts synthesized from knowledge_gaps + surprising_connections (untested hotspots first, then surprising edges, then thin/single-file communities); federated, empty `symbol`, each row a `question` string citing the real symbol/file, top 10 by default. architecture_overview — a federated architecture view with scoped community coupling and independently selectable packages, layers, boundaries, hotspots, entry points, and language facets; `path` is segment-safe and applies consistently. Spring/event patterns (Java): triggers_of — the schedule edge between a @Scheduled method and its synthetic scheduler (pass the method to get its scheduler, or the scheduler to list scheduled methods). publishers_of — methods that publish a given event type (ApplicationEventPublisher.publishEvent). listeners_of — @EventListener methods that listen for a given event type. handlers_of — methods that handle a given HTTP endpoint path (Spring @*Mapping + the existing JS/Python endpoints). endpoints_for — the inverse: endpoint path(s) a given method handles. consumers_of — classes/methods consuming a given @Value config property. children_of — symbols contained in a file path (same as file_summary) or, for a class name, its member symbols by line-range containment. All seven require `symbol` (the target); single-repo, not federated.",
247
249
  },
248
250
  depth: {
249
251
  type: "number",
@@ -292,7 +294,7 @@ function buildDocumentedKnodinTools() {
292
294
  },
293
295
  to: {
294
296
  type: "string",
295
- description: "query shortest_path: destination symbol (start symbol goes in `symbol`). query rename_preview: the new name (old name goes in `symbol`). Read-only by default (returns edit sites + a unified diff); pass `apply: true` to write the rename to disk.",
297
+ description: "query shortest_path/cross_substrate_path: destination symbol (start symbol goes in `symbol`). query rename_preview: the new name (old name goes in `symbol`). Read-only by default (returns edit sites + a unified diff); pass `apply: true` to write the rename to disk.",
296
298
  },
297
299
  apply: {
298
300
  type: "boolean",
@@ -1569,8 +1571,8 @@ async function dispatchKnodinTool(args) {
1569
1571
  const repoWide = REPO_WIDE_QUERY_PATTERNS.includes(pattern);
1570
1572
  if (!symbol && !repoWide)
1571
1573
  throw new Error(`knodin query ${pattern} requires \`symbol\``);
1572
- if (pattern === "shortest_path" && !to)
1573
- throw new Error("knodin query shortest_path requires `to`");
1574
+ if ((pattern === "shortest_path" || pattern === "cross_substrate_path") && !to)
1575
+ throw new Error(`knodin query ${pattern} requires \`to\``);
1574
1576
  if (pattern === "rename_preview" && !to)
1575
1577
  throw new Error("knodin query rename_preview requires `symbol` and `to`");
1576
1578
  const queryHealth = GRAPH_INDEPENDENT_QUERY_PATTERNS.has(pattern)
@@ -1602,6 +1604,9 @@ async function dispatchKnodinTool(args) {
1602
1604
  direction: pattern === "traverse" ? direction : undefined,
1603
1605
  includeDataFlow: pattern === "traverse" ? includeDataFlow : undefined,
1604
1606
  flowVariable: pattern === "flow_analysis" ? flowVariable : undefined,
1607
+ resourceOffset: pattern === "resource_reachability" ? offset : undefined,
1608
+ resourceMaxBytes: pattern === "resource_reachability" ? byteBudget : undefined,
1609
+ resourceMaxTokens: pattern === "resource_reachability" ? tokenBudget : undefined,
1605
1610
  architectureFacets,
1606
1611
  topN,
1607
1612
  sort,
@@ -16,10 +16,11 @@ action-specific response semantics fail closed when a required signal is absent.
16
16
  - **Value:** less manual context assembly and fewer missed dependencies. The
17
17
  paired task method and its bounded results are checked in under
18
18
  [`benchmarks/evaluations/c93-engineering-outcomes/`](../benchmarks/evaluations/c93-engineering-outcomes/).
19
- - **ROI:** fewer corrective agent round trips without hosted-service, account,
20
- authentication, or source-egress overhead. C93 reports measured local task
21
- outcomes and explicitly leaves billed cost and token counts unavailable when
22
- the harness cannot observe them.
19
+ - **ROI:** the intended mechanism is fewer corrective agent round trips without
20
+ hosted-service, account, authentication, or source-egress overhead. C93's
21
+ accepted two-task replay measured no round-trip benefit; both arms needed one
22
+ correction. It therefore authorizes no productivity claim and leaves billed
23
+ cost and unobserved token counts unavailable.
23
24
  - **Tomorrow:** run `knodin init`, inspect `knodin status`, then use the existing
24
25
  checkout through the CLI or the single `knodin.knodin` MCP gateway. The
25
26
  installation and client readiness evidence remains bounded by C91 below.
@@ -35,6 +36,13 @@ preserving its stable identity and evidence provenance. Stale, unavailable, or
35
36
  ambiguous graph evidence must remain explicit and cannot be promoted to a
36
37
  confident failure diagnosis, impact claim, or runtime-causality proof.
37
38
 
39
+ `ExplainResult.callers[].kind` and `ExplainResult.omissions[]` are governed by
40
+ the existing explain mapping: relationship kinds require source provenance and
41
+ stable resolved endpoints, while omissions require an exact source location and
42
+ must never be promoted into a graph edge. C99 exercises these added fields
43
+ without adding a gateway operation or discriminator, so the C95 applicability
44
+ matrix is unchanged.
45
+
38
46
  The C95 fixtures run in a linked non-default worktree. A child harness installs
39
47
  guards before importing the production dispatcher and observes global `fetch`,
40
48
  HTTP/HTTPS request and get, TCP/TLS connection, callback and promise DNS
@@ -46,6 +54,39 @@ credential forwarding, source bytes at network/subprocess boundaries, and
46
54
  native-addon loads must remain zero. Local subprocess execution is counted and
47
55
  permitted only when those observed payload checks remain clean.
48
56
 
57
+ ## C101 composition closure
58
+
59
+ C101 composes four independently bounded decisions without adding an MCP tool
60
+ or operation:
61
+
62
+ - C97 retained lazy semantic residency in one local process. It does **not**
63
+ authorize an idle-RSS-reclamation claim.
64
+ - C98 reports literal TS/JS source-to-sink paths as static heuristics with
65
+ coverage, omissions, freshness, budgets, and continuation. Truncation cannot
66
+ become a complete-result claim.
67
+ - C99 persists only uniquely source-resolved Inversify/tsyringe relationships;
68
+ ambiguous, dynamic, factory, alias, conditional, and reflective wiring stays
69
+ omission evidence rather than an edge.
70
+ - C100 proves one Salesforce Flow action to one uniquely resolved Apex
71
+ `@InvocableMethod`. It does not prove Terraform, dbt, arbitrary Salesforce
72
+ metadata, or general cross-substrate traversal.
73
+
74
+ `npm run verify:c101` source-binds those decisions, checks the CLI and one-tool
75
+ MCP query declarations, and rejects eight adversarial mutations of actual
76
+ production responses. Stale resource/path evidence and heuristic-to-exact
77
+ promotion fail; a truncated result cannot be presented as complete; and a DI
78
+ ambiguity, missing source witness, or path omission cannot become a supported
79
+ edge.
80
+
81
+ ## Repository stewardship
82
+
83
+ An authoritative pull request starts the full test matrix and Sonar analysis,
84
+ so it is a long, resource-intensive operation. Assistants must not automatically
85
+ open one PR per item. Dependency-compatible, coherently scoped commits should
86
+ normally be stacked into one reasonably sized shared PR; unrelated work must
87
+ not be bundled merely to make the PR larger. The user or approved batch decides
88
+ when that review unit is ready.
89
+
49
90
  ## Known bounds
50
91
 
51
92
  This replay proves the checked operation/action fixtures, not all repositories,
@@ -53,7 +94,8 @@ languages, frameworks, dynamic runtime behavior, clients, or deployment
53
94
  environments. Static relationships and failure correlations are candidates,
54
95
  not runtime-causality proof. Budgets can truncate results; indexes can be stale
55
96
  or unavailable; names can be ambiguous; generated code, dynamic dispatch,
56
- framework wiring, and language coverage can leave relationships unresolved.
97
+ framework wiring outside C99's declared Inversify/tsyringe patterns, and
98
+ language coverage can leave relationships unresolved.
57
99
 
58
100
  C91 remains parked pending native client, runtime-manager, Homebrew, and
59
101
  Artifactory certification evidence. C94 remains blocked on C91 and therefore
package/docs/CLI.md CHANGED
@@ -32,6 +32,16 @@ example repository existence, mutually exclusive configuration modes, and
32
32
  graph-query target rules), but syntax cannot reach a handler unless the shared
33
33
  declarative model accepts it first.
34
34
 
35
+ `knodin query resource_reachability --limit 100` runs cached, on-demand static
36
+ TS/JS source-to-sink analysis. It covers literal `process.env` and
37
+ `fs.readFileSync` reads reaching `console.log`, `fetch`, or `db.query` through
38
+ bounded assignment, argument, and return handoff. Results include ordered source
39
+ evidence, stable path identities, registry coverage, omissions, freshness, and
40
+ hard truncation/continuation metadata. Depth is fixed at 6 and paths at 100;
41
+ dynamic names, reflection, computed aliases, and unsupported languages are
42
+ reported rather than guessed. This is a static heuristic, not runtime reachability
43
+ or an exploitability verdict.
44
+
35
45
  For support evidence, `knodin diagnostics enable|status|preview|archive|inspect|clear|disable`
36
46
  manages an explicit local failure journal and redacted gzip JSON bundles.
37
47
  Collection does not upload data. See `docs/DIAGNOSTICS.md` for retention,
@@ -51,6 +51,16 @@ the evaluation's competitor limitations. The active
51
51
  [competitive roadmap](../roadmap/competitive-roadmap.md) links each claim to
52
52
  its acceptance criteria and replay state.
53
53
 
54
+ The current positioning lead is the checked behavioral contract, not the raw
55
+ capability count. C98 adds a bounded TS/JS resource-reachability heuristic; C99
56
+ adds only uniquely source-resolved Inversify/tsyringe relationships; C100 proves
57
+ one Salesforce Flow action to one Apex `@InvocableMethod`; and C97 retains the
58
+ one-process semantic design without claiming idle RSS reclamation. C101 binds
59
+ those decisions and rejects stale, truncated, ambiguous, or omission-only
60
+ results when promoted into complete or exact claims. None of these fixtures
61
+ supports general runtime causality, arbitrary cross-substrate paths, broad DI
62
+ framework coverage, or universal competitor superiority.
63
+
54
64
  ## The quartet: knodin vs its three direct progenitors
55
65
 
56
66
  knodin exists to fuse three tools this project already used side by side
package/docs/DEMO.md ADDED
@@ -0,0 +1,49 @@
1
+ # Bounded product walkthrough
2
+
3
+ This is the maintained positioning walkthrough, not C94's pending certified
4
+ five-minute client demonstration. C94 remains parked behind native installation
5
+ and client evidence; this document must not be cited as completing it.
6
+
7
+ ## Value
8
+
9
+ On an existing checkout, initialize and orient before assembling context by
10
+ hand:
11
+
12
+ ```bash
13
+ knodin init
14
+ knodin status --deep
15
+ knodin context "change the review submission path"
16
+ knodin query impact submit --direction upstream
17
+ ```
18
+
19
+ The intended value is less manual context assembly and fewer missed
20
+ dependencies. C100 demonstrates one narrow improvement: a Salesforce Flow
21
+ action can resolve to the exact local Apex `@InvocableMethod`, rather than only
22
+ its class file. It does not demonstrate arbitrary cross-substrate analysis.
23
+
24
+ ## ROI
25
+
26
+ Use the same local CLI or one MCP gateway against the checkout already on disk;
27
+ no hosted index, account, authentication, or source-code egress is required.
28
+ Fewer corrective round trips are an intended mechanism, not a measured product
29
+ claim: C93's accepted two-task replay found no round-trip difference and both
30
+ arms required one correction.
31
+
32
+ ## Tomorrow
33
+
34
+ After `knodin init`, use `context`, `explain`, structured `query`, and `review`
35
+ through the CLI or the single operation-routed MCP tool. If status reports stale
36
+ or damaged state, repair it before relying on graph evidence.
37
+
38
+ ## Secret sauce and bounds
39
+
40
+ Stable identity, exact source evidence, explicit freshness, and truthful
41
+ budgets compose behind the compact local surface. C101 mutates actual production
42
+ responses for resource reachability, TypeScript DI, and one Flow-to-Apex path;
43
+ stale, truncated, ambiguous, or evidence-free variants must fail the contract
44
+ oracle. C97 separately retains one-process semantic residency and authorizes no
45
+ idle-RSS-reclamation claim.
46
+
47
+ These checked fixtures do not prove runtime causality, broad DI framework
48
+ coverage, arbitrary Salesforce/Terraform/dbt paths, every client or platform,
49
+ or superiority over competitors.
package/docs/MCP.md CHANGED
@@ -4,6 +4,11 @@ knodin exposes exactly one MCP tool named `knodin`. Capabilities such as
4
4
  context, explain, review, search, docs, doctor, repositories, and systems are
5
5
  operations of that gateway, not separate top-level tools.
6
6
 
7
+ Use `operation: "query", pattern: "resource_reachability"` for the same bounded,
8
+ cached, repo-wide TS/JS analysis as the CLI. It requires no symbol and retains
9
+ the gateway's freshness refusal and response budgets; returned paths remain
10
+ source-evidenced static heuristics with explicit coverage and omissions.
11
+
7
12
  Use `operation: "evidence"` for deterministic `locate`, `outline`, `evidence`,
8
13
  and `expand` source delivery with a verified complete-file hash handshake and
9
14
  recoverable hard-budget continuations. See `docs/PROGRESSIVE-EVIDENCE.md`.
@@ -0,0 +1,47 @@
1
+ # knodin 0.8.3
2
+
3
+ This release strengthens Knodin's source-evidenced graph across repository
4
+ lifecycle, cross-substrate analysis, and bounded organization-scale workflows.
5
+
6
+ - Adds bounded repository-wide resource reachability for source-proven
7
+ environment, file, fetch, console, and database paths. Results retain stable
8
+ identities, exact evidence, explicit unsupported cases, freshness, response
9
+ budgets, and deterministic continuation.
10
+ - Resolves source-proven TypeScript dependency-injection wiring for supported
11
+ Inversify and tsyringe forms. Framework and registry scoping, ambiguity,
12
+ conditional wiring, incremental refresh, and removal are represented without
13
+ guessing across containers or frameworks.
14
+ - Proves exact Salesforce Flow-to-Apex paths from Flow action metadata to unique
15
+ `@InvocableMethod` targets, including targeted Apex refresh, source evidence,
16
+ omissions, freshness, and bounded responses.
17
+ - Makes repair converge truthfully across schema migration, empty repositories,
18
+ final verification failure, and future-schema refusal. Scoped repairs avoid
19
+ unrelated TypeScript DI reads while still rebuilding affected DI evidence.
20
+ - Strengthens the public behavioral contract and positioning with replayed
21
+ production CLI, MCP, and engine responses. C97-C101 evidence is independently
22
+ replayable and source-bound; failed performance experiments remain recorded
23
+ as limitations rather than superiority claims.
24
+ - Prevents stale package and barrel-resolution caches, avoids caching transient
25
+ filesystem failures, and keeps independent engine instances from closing
26
+ resources still owned by another instance.
27
+ - Reuses a bounded, generation-scoped resource corpus so warm reachability
28
+ queries do not reread every indexed source file while still failing closed on
29
+ disk drift.
30
+ - Bounds cross-substrate evidence consistently with the outer response budget,
31
+ fixes member-call reachability false positives, and makes evidence verifiers
32
+ recompute their claim-bearing gates instead of trusting stored booleans.
33
+ - Removes duplicated progress-worker and response-budget machinery, updates the
34
+ audited development dependency set, and fixes lifecycle evaluation owners so
35
+ the complete test process terminates.
36
+ - Records that pull requests are intentionally expensive because Sonar and the
37
+ complete repository gates run on each review stream; compatible work should
38
+ be stacked into one coherent PR rather than opened automatically as many small
39
+ PRs.
40
+
41
+ The one-process semantic-residency evaluation remains a retained no-go: neither
42
+ in-process ONNX disposal, worker threads, quantized weights, nor vector/cache
43
+ eviction met the repeatable memory gate without violating the local one-process
44
+ product boundary. Knodin retains lazy loading and bounded caches rather than
45
+ claiming unsupported reclamation.
46
+
47
+ This remains an ordinary 0.x release, not a dogfood-only build and not GA.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knodin",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "knodin — source-evidenced local code intelligence with known bounds. Stable identity, fresh evidence, truthful budgets, and recoverable bounded views.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,6 +25,7 @@
25
25
  "docs/DOCTOR-AND-UPDATES.md",
26
26
  "docs/DIAGNOSTICS.md",
27
27
  "docs/BEHAVIORAL-CONTRACT.md",
28
+ "docs/DEMO.md",
28
29
  "docs/COMPARISON.md",
29
30
  "docs/COMPETITIVE-LANDSCAPE-2026-08.md",
30
31
  "docs/INDEXING-POLICY-AND-PROVENANCE.md",
@@ -57,6 +58,7 @@
57
58
  "docs/releases/0.7.5.md",
58
59
  "docs/releases/0.8.0.md",
59
60
  "docs/releases/0.8.2.md",
61
+ "docs/releases/0.8.3.md",
60
62
  "docs/assets/knodin-favicon.svg",
61
63
  "docs/SYSTEMS-AND-RELATIONSHIPS.md",
62
64
  "docs/TELEMETRY.md",
@@ -114,6 +116,16 @@
114
116
  "bench:c93": "tsx benchmarks/evaluations/c93-engineering-outcomes/runner.ts",
115
117
  "verify:c93": "tsx scripts/verify-c93.ts",
116
118
  "verify:c95": "tsx scripts/verify-c95.ts && vitest run src/__tests__/unit/behavior-contract.spec.ts src/__tests__/unit/failure-diagnosis.spec.ts src/__tests__/unit/impact.spec.ts src/__tests__/unit/freshness.spec.ts",
119
+ "bench:c97": "tsx benchmarks/evaluations/c97-one-process-semantic-residency/runner.ts",
120
+ "verify:c97": "tsx scripts/verify-c97.ts",
121
+ "bench:c98": "tsx benchmarks/evaluations/c98-resource-reachability/runner.ts",
122
+ "verify:c98": "tsx scripts/verify-c98.ts && vitest run src/__tests__/unit/resource-reachability.spec.ts",
123
+ "bench:c99": "tsx benchmarks/evaluations/c99-typescript-di/runner.ts",
124
+ "verify:c99": "tsx scripts/verify-c99.ts && vitest run src/__tests__/unit/typescript-di.spec.ts",
125
+ "bench:c100": "tsx benchmarks/evaluations/c100-flow-apex-path/runner.ts",
126
+ "verify:c100": "tsx scripts/verify-c100.ts && vitest run src/__tests__/unit/salesforce-metadata.spec.ts",
127
+ "bench:c101": "tsx benchmarks/evaluations/c101-contract-positioning/runner.ts",
128
+ "verify:c101": "tsx scripts/verify-c101.ts",
117
129
  "bench:ann": "bun scripts/ann-bench.ts",
118
130
  "bench:perf": "tsx scripts/perf-bench.ts",
119
131
  "bench:competitive": "tsx scripts/competitive-bakeoff.ts",
@@ -132,6 +132,65 @@ new items or strengthen existing ones; they do not reopen the archived roadmap.
132
132
  | C94 | P1 | parked-external-evidence — 2026-08-06 (previous: blocked) | Make first use self-explanatory and demonstrable | C89, C90, C91 |
133
133
  | C95 | P1 | implemented | Codify and replay the defensible behavioral contract | C3, C24, C83 |
134
134
  | C96 | P2 | evaluated — deferred | Gate every new capability on a narrow outcome replay | C24, C93, C95 |
135
+ | C97 | P1 | evaluated — retained existing design | Reclaim idle semantic memory without changing one-process architecture | C46, C51, C80, C87 |
136
+ | C98 | P1 | implemented | Add bounded source-to-sink resource reachability | C2, C3, C8, C24, C78, C95 |
137
+ | C99 | P1 | implemented | Resolve bounded TypeScript dependency-injection wiring | C2, C3, C24, C95 |
138
+ | C100 | P1 | implemented | Prove bounded Salesforce Flow-to-Apex paths | C2, C3, C20, C23, C95, C96 |
139
+ | C101 | P1 | implemented | Strengthen contract-led positioning | C95, C97, C98, C99, C100 |
140
+
141
+ ### C98 — Bounded source-to-sink resource reachability
142
+
143
+ - Status: implemented
144
+ - Evidence: `benchmarks/evaluations/c98-resource-reachability/` freezes the strengthened eight-path C78 oracle and records 100% precision, 100% recall, deterministic ordering, and historical cold/warm/RSS measurements across three local repositories. The verifier independently replays correctness and its current sub-1-GB memory gate; portfolio timings remain descriptive rather than acceptance gates.
145
+ - Implementation: `query resource_reachability` performs cached, on-demand TS/JS analysis without persisted resource-flow tables. It recognizes literal `process.env` and `fs.readFileSync` sources, `console.log`, `fetch`, and `db.query` sinks, plus assignment, argument, return, and bounded recursive handoff.
146
+ - Bounds: maximum depth 6 and 100 paths. Results are static heuristics, never runtime reachability or exploitability proof; dynamic names, reflection, computed aliases, unsupported languages, cycles, truncation, continuation, coverage, and freshness remain explicit.
147
+ - Locality: no hosted service, authentication, credentials, source egress, network access, or production model is required.
148
+
149
+ ### C99 — Bounded TypeScript dependency-injection wiring
150
+
151
+ - Status: implemented
152
+ - Evidence: `benchmarks/evaluations/c99-typescript-di/` records 100% precision and recall on its declared Inversify/tsyringe oracle, including an explicit ambiguous-token omission; `npm run verify:c99` rejects stale evidence or any missed/extra edge.
153
+ - Implementation: exact source-only `di_binding` and `di_resolution` references cover `bind().to`, `toSelf`, `register(useClass)`, `registerSingleton`, `@inject`, `get`, and `resolve`. Existing explain, impact, traversal, shortest-path, review, and architecture consumers reuse the persisted relationships.
154
+ - Known bounds: only source-proven framework imports, receivers, token declarations, and uniquely resolved implementations qualify. Multiple bindings, computed tokens, aliases, factories, runtime container modules, conditionals, and reflection remain explicit omissions rather than inferred edges.
155
+
156
+ ### C100 — Bounded Salesforce Flow-to-Apex paths
157
+
158
+ - Status: implemented
159
+ - Evidence: `benchmarks/evaluations/c100-flow-apex-path/` records 100% precision and recall for one preregistered Salesforce DX Flow `actionCalls` to uniquely declared Apex `@InvocableMethod` boundary, including unannotated, missing, and overloaded negative cases. The paired missed-dependency replay improves from an imprecise file answer to a stable method identity with exact XML and Apex evidence.
160
+ - Implementation: Flow indexing persists an exact `flow_apex_action` method reference in addition to its compatible file topology. `query cross_substrate_path <from> <to>` requires both endpoints and returns only that supported substrate transition with stable identities, per-step substrates, extracted provenance, confidence, freshness, exact source evidence, truthful truncation/continuation, and explicit omissions.
161
+ - Known bounds: this is not a general path engine and does not support Terraform, dbt, arbitrary Salesforce metadata, runtime dispatch, namespaced packages, factories, aliases, or ambiguous/multiple Apex methods. One passing boundary and paired task do not establish broad cross-substrate or competitor superiority.
162
+ - Locality: the replay requires no Salesforce org, hosted service, authentication, credentials, network access, or source egress.
163
+
164
+ ### C101 — Contract-led positioning closure
165
+
166
+ - Status: implemented
167
+ - Evidence: `benchmarks/evaluations/c101-contract-positioning/` and
168
+ `scripts/verify-c101.ts` source-bind the C97–C100 decisions, the behavioral
169
+ contract, public CLI/MCP declarations, positioning documents, and repository
170
+ stewardship instructions.
171
+ - Contract: eight adversarial mutations of actual production responses reject
172
+ resource/path stale evidence, continuation-free truncation, a truncated
173
+ response presented as complete, heuristic-to-exact promotion, DI source
174
+ removal, DI ambiguity, and a path omission retained beside a supported step.
175
+ Fixture-backed CLI and one-tool MCP calls return the same bounded resource and
176
+ Flow-to-Apex envelopes; DI reuses
177
+ existing explain/query/review/map surfaces rather than adding a discriminator.
178
+ - Positioning: value is less manual context assembly and fewer missed
179
+ dependencies; ROI's intended mechanism is fewer corrective round trips
180
+ without hosted-service, authentication, or source-egress overhead, while
181
+ C93's null two-task result authorizes no measured productivity claim;
182
+ tomorrow is `knodin init` plus the local CLI or single gateway; the secret
183
+ sauce is stable identity, source evidence, freshness, and truthful budgets
184
+ composed under one contract.
185
+ - Known bounds: C98 is a static TS/JS heuristic, C99 covers only its declared
186
+ Inversify/tsyringe patterns, C100 proves one Flow-to-Apex boundary, and C97
187
+ does not reclaim idle semantic RSS. The closure proves checked fixtures, not
188
+ runtime causality, arbitrary cross-substrate paths, broad framework coverage,
189
+ client/platform certification, or competitor superiority.
190
+ - Stewardship: pull requests invoke the full test matrix and Sonar analysis and
191
+ are therefore long and resource intensive. Related dependency-compatible
192
+ commits normally form one reasonably sized shared PR; assistants do not
193
+ auto-open per-item PRs, and unrelated changes are not bundled for size.
135
194
 
136
195
  ## Roadmap completion contract
137
196
 
@@ -3709,6 +3768,42 @@ Evidence: `benchmarks/evaluations/c93-engineering-outcomes/verification.json`,
3709
3768
  `contracts/behavior-contract-v1.json`, and
3710
3769
  `benchmarks/evaluations/c95-behavior-contract/runner.ts`.
3711
3770
 
3771
+ ### C97 — One-process semantic residency reclamation
3772
+
3773
+ - Status: evaluated — retained existing design
3774
+ - Priority: P1
3775
+ - Disposition: Evaluate
3776
+ - DependsOn: C46, C51, C80, C87
3777
+ - Touches: evaluation and decision evidence only; no runtime, status, API, or
3778
+ storage change.
3779
+ - What: test whether idle model/vector/HNSW eviction can make a repeatable OS-RSS
3780
+ guarantee while preserving Knodin's one-local-process architecture.
3781
+ - Evidence: `benchmarks/evaluations/c97-one-process-semantic-residency/` and
3782
+ `scripts/verify-c97.ts` bind 10-trial fresh-process measurements to the runner,
3783
+ engine, package, and embedding-test sources.
3784
+
3785
+ Decision: retain the current lazy local model and bounded generation-scoped
3786
+ caches. No in-process candidate repeatedly reclaimed both 32 MiB and 15% of its
3787
+ attributable semantic increment across two corpus tiers. ONNX disposal and
3788
+ allocator behavior were nondeterministic; `worker_threads` retained the shared
3789
+ native address space; q8 lacks the required C28 retrieval-equivalence replay;
3790
+ fp16 failed to initialize. Child-process isolation is explicitly rejected
3791
+ because it would violate the one-local-process product contract. Do not claim
3792
+ idle RSS reclamation from model disposal or cache clearing on this evidence.
3793
+
3794
+ Known limitations: resident semantic search retains a measurable one-process
3795
+ RSS cost after first use. Structural-first routing still avoids loading the model
3796
+ when evidence is sufficient, and decoded-vector/HNSW caches remain bounded and
3797
+ generation-invalidated. Reopen only with a portable allocator/runtime mechanism
3798
+ that passes the unchanged absolute, percentage, p95, retrieval, and 20-cycle
3799
+ gates.
3800
+
3801
+ Final-stack refresh: C101 reran the full C97 measurement after C98–C100 changed
3802
+ the shared engine. Some individual cache/ONNX trials crossed the memory
3803
+ threshold, but no production candidate passed all 10 trials; the retained
3804
+ one-process decision therefore remains unchanged. The refreshed raw artifact
3805
+ replaces the mutable C97 measurement file and `verify:c97` recomputes this gate.
3806
+
3712
3807
  ## Explicit non-goals from the GitNexus comparison
3713
3808
 
3714
3809
  - Do not split knodin's gateway into one MCP schema per capability; the one-tool