carrick 0.3.123 → 0.3.124

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carrick",
3
- "version": "0.3.123",
3
+ "version": "0.3.124",
4
4
  "carrickCache": {
5
5
  "cache_version": 22,
6
6
  "intent_cache_version": 2
@@ -113,11 +113,11 @@
113
113
  "zod": "^3.23.0"
114
114
  },
115
115
  "optionalDependencies": {
116
- "@carrick-tools/cli-darwin-arm64": "0.3.123",
117
- "@carrick-tools/cli-darwin-x64": "0.3.123",
118
- "@carrick-tools/cli-linux-arm64": "0.3.123",
119
- "@carrick-tools/cli-linux-x64": "0.3.123",
120
- "@carrick-tools/cli-win32-x64": "0.3.123"
116
+ "@carrick-tools/cli-darwin-arm64": "0.3.124",
117
+ "@carrick-tools/cli-darwin-x64": "0.3.124",
118
+ "@carrick-tools/cli-linux-arm64": "0.3.124",
119
+ "@carrick-tools/cli-linux-x64": "0.3.124",
120
+ "@carrick-tools/cli-win32-x64": "0.3.124"
121
121
  },
122
122
  "devDependencies": {
123
123
  "@types/node": "^24.13.3"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "carrick",
3
3
  "description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
4
- "version": "0.3.123",
4
+ "version": "0.3.124",
5
5
  "author": {
6
6
  "name": "Carrick",
7
7
  "email": "hello@carrick.tools"
@@ -39,6 +39,12 @@ export const MACHINERY_MEMBER_INDICATORS = new Set([
39
39
  'clone',
40
40
  'json',
41
41
  'statusText',
42
+ // FROZEN (old path, 2026-10-21): the reply-object half below only (the
43
+ // fetch half above and the threshold stay). Deletions and safety fixes
44
+ // only. Fixes for wrong or missing types go in labelled reply objects
45
+ // (carrick#2472 C4) and the language library rows (carrick#2445). Delete
46
+ // when a delete-and-replay on the four repos moves no row wrongly, after
47
+ // carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
42
48
  // Node http ServerResponse / reply-object surface
43
49
  'statusCode',
44
50
  'statusMessage',
@@ -442,7 +442,7 @@ function handleVerifyLibraryClaims(request) {
442
442
  writeProgress(request.request_id, 'verify_library_claims', 'program ready');
443
443
  const fromDir = path.resolve(projectLoader.getRepoRoot(), request.from_dir);
444
444
  log(`Verifying ${request.checks.length} library claim(s) from ${fromDir}`);
445
- const { semantics, modules } = claimsVerifier.run(fromDir, request.checks, request.budget_ms ?? SEMANTICS_BUDGET_MS);
445
+ const { semantics, modules } = claimsVerifier.run(fromDir, request.checks, request.budget_ms ?? SEMANTICS_BUDGET_MS, request.scan_root);
446
446
  return {
447
447
  request_id: request.request_id,
448
448
  status: 'success',
@@ -63,7 +63,7 @@ export declare class LibraryClaimsVerifier {
63
63
  * A check on an instance or a scope reads the maker or scope claims of the
64
64
  * same request first, whatever their place in it.
65
65
  */
66
- run(fromDir: string, checks: LibraryCheck[], budgetMs: number): VerifyResult;
66
+ run(fromDir: string, checks: LibraryCheck[], budgetMs: number, scanRoot?: string): VerifyResult;
67
67
  /**
68
68
  * Each specifier's declared surface, read with the verifier's predicates
69
69
  * (carrick#1660): every value export (or only those `only` names for the
@@ -39,6 +39,7 @@ import * as crypto from 'node:crypto';
39
39
  import * as fs from 'node:fs';
40
40
  import * as path from 'node:path';
41
41
  import { ts } from 'ts-morph';
42
+ import { PathNames, declaringFileName } from './position-rows.js';
42
43
  /** The methods a `verb` claim may name, and a method key may accept. */
43
44
  const HTTP_METHODS = new Set([
44
45
  'GET',
@@ -130,8 +131,9 @@ const HTTP_V2_RULES = { http: false, message: true, ops: new Set(['request']), m
130
131
  const SERVER_RULES = { http: false, message: true, ops: new Set(['receive']), methods: ROUTE_METHODS };
131
132
  /**
132
133
  * The role table: the role alone picks the checks. A role this build does
133
- * not read yet (GraphQL executors, server mounts) answers `role_unsupported`,
134
- * and `none` has nothing on the wire to check.
134
+ * not read yet (GraphQL executors) answers `role_unsupported`, and `none`
135
+ * has nothing on the wire to check. A server's mounts are scope claims
136
+ * (`mounts`, #2474 R0).
135
137
  */
136
138
  const ROLE_TABLE = {
137
139
  http_client: HTTP_RULES,
@@ -238,6 +240,13 @@ export function httpCheck(check) {
238
240
  }
239
241
  }
240
242
  }
243
+ /** Declarations in a fixed order, each once. */
244
+ function sortedDeclarations(declarations) {
245
+ const seen = new Map();
246
+ for (const each of declarations)
247
+ seen.set(`${each.decl_file}:${each.decl_line}`, each);
248
+ return [...seen.values()].sort((a, b) => (a.decl_file < b.decl_file ? -1 : a.decl_file > b.decl_file ? 1 : a.decl_line - b.decl_line));
249
+ }
241
250
  let probeSequence = 0;
242
251
  export class LibraryClaimsVerifier {
243
252
  project;
@@ -250,10 +259,15 @@ export class LibraryClaimsVerifier {
250
259
  * A check on an instance or a scope reads the maker or scope claims of the
251
260
  * same request first, whatever their place in it.
252
261
  */
253
- run(fromDir, checks, budgetMs) {
262
+ run(fromDir, checks, budgetMs, scanRoot) {
254
263
  const deadline = performance.now() + budgetMs;
255
264
  const stamp = (check, outcome) => outcome.verdict === 'verified'
256
- ? { claim_id: check.claim_id, receiver: check.receiver, verdict: 'verified' }
265
+ ? {
266
+ claim_id: check.claim_id,
267
+ receiver: check.receiver,
268
+ verdict: 'verified',
269
+ ...(outcome.declarations?.length ? { declarations: outcome.declarations } : {}),
270
+ }
257
271
  : {
258
272
  claim_id: check.claim_id,
259
273
  receiver: check.receiver,
@@ -330,7 +344,7 @@ export class LibraryClaimsVerifier {
330
344
  const file = program.getSourceFile(probe.getFilePath());
331
345
  if (!file)
332
346
  throw new Error(`probe file ${probePath} is not in the program`);
333
- const reader = new DeclarationReader(program, file, this.project.getModuleResolutionHost(), fromDir);
347
+ const reader = new DeclarationReader(program, file, this.project.getModuleResolutionHost(), fromDir, scanRoot);
334
348
  const localRanges = readLocalRanges(fromDir);
335
349
  const moduleReads = new Map();
336
350
  const exportReads = new Map();
@@ -501,13 +515,15 @@ export class LibraryClaimsVerifier {
501
515
  const built = builtReceiver(check);
502
516
  const builtAtDefaults = built === undefined ? undefined : atDefaults(check, built);
503
517
  const holding = [];
518
+ const declarations = [];
504
519
  for (const receiver of receivers.value) {
505
520
  const result = reader.judgeMessage(receiver, check.claim, rules, builtAtDefaults);
506
521
  if (result.outcome.verdict !== 'verified')
507
522
  return result;
508
523
  holding.push(...(result.holding ?? []));
524
+ declarations.push(...(result.outcome.declarations ?? []));
509
525
  }
510
- return { outcome: VERIFIED, holding };
526
+ return { outcome: declarations.length > 0 ? { verdict: 'verified', declarations: sortedDeclarations(declarations) } : VERIFIED, holding };
511
527
  };
512
528
  const semantics = [];
513
529
  for (let index = 0; index < checks.length; index++) {
@@ -695,6 +711,19 @@ function placementInvalid(claim) {
695
711
  const of = parseReceiver(claim.of);
696
712
  return of === undefined || (of.maker === undefined && of.scope === undefined);
697
713
  }
714
+ /**
715
+ * Where a handler's request holds its path and method (#2474 R0) is read on
716
+ * a receive op with a handler, and names `path` or `url`; every member path
717
+ * it gives is non-empty.
718
+ */
719
+ function requestAtValid(claim) {
720
+ const request = claim.request;
721
+ if (claim.op !== 'receive' || claim.handler === undefined)
722
+ return false;
723
+ if (request.path === undefined && request.url === undefined)
724
+ return false;
725
+ return [request.path, request.url, request.method].every(members => members === undefined || members.length > 0);
726
+ }
698
727
  /**
699
728
  * A message check names a receiver its claim can be read on (contract
700
729
  * amendment 2, B3). A maker is read on the export itself. An element with
@@ -841,6 +870,8 @@ class DeclarationReader {
841
870
  probe;
842
871
  host;
843
872
  checker;
873
+ /** Names declaring files as position rows do: from the scan root, else the service root. */
874
+ pathNames;
844
875
  /** The service root, as a realpath. */
845
876
  root;
846
877
  packageDirectories = new Map();
@@ -853,11 +884,12 @@ class DeclarationReader {
853
884
  declared = new Map();
854
885
  /** The service root as given and as a realpath, longest first: what a listing prints as `<root>`. */
855
886
  printedRoots;
856
- constructor(program, probe, host, serviceRoot) {
887
+ constructor(program, probe, host, serviceRoot, scanRoot) {
857
888
  this.program = program;
858
889
  this.probe = probe;
859
890
  this.host = host;
860
891
  this.checker = program.getTypeChecker();
892
+ this.pathNames = new PathNames(scanRoot ?? serviceRoot);
861
893
  this.root = this.realpath(serviceRoot);
862
894
  this.printedRoots = [...new Set([this.root, serviceRoot])]
863
895
  .filter(root => root.length > 1)
@@ -1472,9 +1504,26 @@ class DeclarationReader {
1472
1504
  * `member` (after `path`) is a home-declared callable member whose name
1473
1505
  * slot accepts a string, under the name-slot rules, and returns a type that
1474
1506
  * says something, read at its type-parameter defaults (`returnOf`).
1507
+ *
1508
+ * Route fields (#2474 R0, contract amendment of 2026-10-22 on carrick#2434):
1509
+ * - `runs` with `yields`: the member runs the function at `runs` (a
1510
+ * function with a declared signature) and hands it, at `yields.param`,
1511
+ * an instance of the package's export `yields.export` (`yieldsOutcome`);
1512
+ * - `mounts`: the receiver the claim is read on fits the parameter there
1513
+ * (`mountsOutcome`).
1514
+ * Either one binds its instance through a parameter or an argument, not
1515
+ * the member's return, so the return is not read and the claim holds no
1516
+ * overloads: a `>scope:` receiver read through it is not resolved.
1517
+ * `runs` without `yields` (or the reverse), or `mounts` beside either, is
1518
+ * `claim_invalid`.
1475
1519
  */
1476
1520
  judgeScope(receiver, claim, built) {
1477
- const layout = this.layout({ name: claim.name });
1521
+ if ((claim.runs === undefined) !== (claim.yields === undefined))
1522
+ return { outcome: unchecked('claim_invalid') };
1523
+ if (claim.mounts !== undefined && claim.runs !== undefined)
1524
+ return { outcome: unchecked('claim_invalid') };
1525
+ const binds = claim.runs !== undefined || claim.mounts !== undefined;
1526
+ const layout = this.layout({ name: claim.name, runs: claim.runs, mounts: claim.mounts });
1478
1527
  if ('failure' in layout)
1479
1528
  return { outcome: layout.failure };
1480
1529
  const callee = this.ownCallee(receiver, claim.path ?? [], claim.member, 'call');
@@ -1485,7 +1534,17 @@ class DeclarationReader {
1485
1534
  let best;
1486
1535
  for (const signature of callee.signatures) {
1487
1536
  let result = this.messageSignatureOutcome(signature, layout.value, labels);
1488
- if (result.rank === Infinity && this.returnSaysNothing(this.returnOf(signature, built))) {
1537
+ if (result.rank === Infinity && claim.yields !== undefined) {
1538
+ const given = this.yieldsOutcome(signature, claim.runs, claim.yields, receiver);
1539
+ if (given)
1540
+ result = { rank: 5, outcome: given };
1541
+ }
1542
+ if (result.rank === Infinity && claim.mounts !== undefined) {
1543
+ const mounted = this.mountsOutcome(signature, claim.mounts, receiver);
1544
+ if (mounted)
1545
+ result = { rank: 5, outcome: mounted };
1546
+ }
1547
+ if (result.rank === Infinity && !binds && this.returnSaysNothing(this.returnOf(signature, built))) {
1489
1548
  result = { rank: 6, outcome: unchecked('scope_unresolved') };
1490
1549
  }
1491
1550
  if (result.rank === Infinity)
@@ -1494,9 +1553,38 @@ class DeclarationReader {
1494
1553
  best = furthest(best, result.rank, result.outcome);
1495
1554
  }
1496
1555
  if (holding.length > 0)
1497
- return { outcome: VERIFIED, holding };
1556
+ return binds ? { outcome: VERIFIED } : { outcome: VERIFIED, holding };
1498
1557
  return { outcome: best.outcome };
1499
1558
  }
1559
+ /**
1560
+ * `mounts: { arg, key? }` on a scope (#2474 R0): the receiver the claim is
1561
+ * read on is assignable to the parameter there, so an instance like it can
1562
+ * be served under the scope's name. A parameter typed `any`, `unknown`,
1563
+ * `{}` or by an unconstrained type parameter says nothing
1564
+ * (`mounts_untyped`). There is no separate "not a function" rule: a router
1565
+ * type that is itself a handler is a common shape, and a parameter only a
1566
+ * function fits fails here because the instance does not fit it.
1567
+ * Undefined when it holds.
1568
+ */
1569
+ mountsOutcome(signature, mounts, receiver) {
1570
+ let declared = this.keyParameterAt(signature, mounts.arg);
1571
+ if (declared !== undefined && mounts.key !== undefined) {
1572
+ const property = this.declaredProperty(declared, mounts.key);
1573
+ declared = property ? this.checker.getTypeOfSymbol(property) : undefined;
1574
+ }
1575
+ if (declared === undefined)
1576
+ return failed('param_missing');
1577
+ const slot = this.throughConstraint(declared);
1578
+ if (slot === VARIADIC)
1579
+ return unchecked('mounts_untyped');
1580
+ const parts = this.parts(slot);
1581
+ if (parts.length === 0)
1582
+ return failed('mounts_not_instance');
1583
+ if (parts.some(part => this.isOpenTop(part) || this.isUnconstrained(part) || this.isEmptyObject(part))) {
1584
+ return unchecked('mounts_untyped');
1585
+ }
1586
+ return this.checker.isTypeAssignableTo(receiver.type, slot) ? undefined : failed('mounts_not_instance');
1587
+ }
1500
1588
  /**
1501
1589
  * A send, receive or request op: the home-declared callable member (after
1502
1590
  * `path`; or the receiver itself, when null) with some overload that takes
@@ -1513,6 +1601,9 @@ class DeclarationReader {
1513
1601
  if (claim.yields !== undefined && (claim.op !== 'receive' || claim.handler === undefined)) {
1514
1602
  return unchecked('claim_invalid');
1515
1603
  }
1604
+ // So is where the handler's request holds its path and method (#2474 R0).
1605
+ if (claim.request !== undefined && !requestAtValid(claim))
1606
+ return unchecked('claim_invalid');
1516
1607
  const name = claim.name;
1517
1608
  // A send carries a payload: a name alone on `send(data)` is the data. An
1518
1609
  // op that runs a function or client it is handed takes its name from
@@ -1562,6 +1653,11 @@ class DeclarationReader {
1562
1653
  if (given)
1563
1654
  result = { rank: 5, outcome: given };
1564
1655
  }
1656
+ if (result.rank === Infinity && claim.request !== undefined) {
1657
+ const read = this.requestOutcome(signature, claim.handler, claim.request);
1658
+ if (read)
1659
+ result = { rank: 5, outcome: read };
1660
+ }
1565
1661
  if (result.rank === Infinity) {
1566
1662
  held = true;
1567
1663
  break;
@@ -1572,7 +1668,28 @@ class DeclarationReader {
1572
1668
  return best.outcome;
1573
1669
  if (boundRuns)
1574
1670
  return this.boundRunsOutcome(receiver.makerHolding, boundRuns);
1575
- return VERIFIED;
1671
+ const declarations = this.declarationsOf(callee.signatures);
1672
+ return declarations.length > 0 ? { verdict: 'verified', declarations } : VERIFIED;
1673
+ }
1674
+ /**
1675
+ * Where the signatures a verified op resolved to are declared, as position
1676
+ * rows spell it (`decl_file`, `decl_line`): the member on this receiver, so
1677
+ * two members of one name on two types give two answers. A declaration no
1678
+ * row could name is left out.
1679
+ */
1680
+ declarationsOf(signatures) {
1681
+ const found = [];
1682
+ for (const signature of signatures) {
1683
+ const declaration = signature.getDeclaration();
1684
+ if (!declaration)
1685
+ continue;
1686
+ const file = declaration.getSourceFile();
1687
+ const decl_file = declaringFileName(this.program, this.pathNames, file);
1688
+ if (decl_file === null)
1689
+ continue;
1690
+ found.push({ decl_file, decl_line: file.getLineAndCharacterOfPosition(declaration.getStart(file)).line + 1 });
1691
+ }
1692
+ return sortedDeclarations(found);
1576
1693
  }
1577
1694
  /**
1578
1695
  * The method fields of an op (label structure v2, carrick#2434), under the
@@ -1680,6 +1797,48 @@ class DeclarationReader {
1680
1797
  }
1681
1798
  return failed('yields_not_export');
1682
1799
  }
1800
+ /**
1801
+ * `request: { param, path?, url?, method? }` on a receive op (#2474 R0):
1802
+ * some signature of the handler this overload takes declares a parameter
1803
+ * at `param` on which every member path the claim gives resolves, step by
1804
+ * step, through properties the library declares (`declaredProperty`).
1805
+ * Undefined when it holds.
1806
+ */
1807
+ requestOutcome(signature, handler, request) {
1808
+ let declared = this.keyParameterAt(signature, handler.arg);
1809
+ if (declared !== undefined && handler.key !== undefined) {
1810
+ const property = this.declaredProperty(declared, handler.key);
1811
+ declared = property ? this.checker.getTypeOfSymbol(property) : undefined;
1812
+ }
1813
+ if (declared === undefined)
1814
+ return failed('param_missing');
1815
+ const slot = this.throughConditional(this.throughConstraint(declared));
1816
+ if (slot === VARIADIC)
1817
+ return unchecked('handler_untyped');
1818
+ const paths = [request.path, request.url, request.method].filter((p) => p !== undefined);
1819
+ let best = failed('param_missing');
1820
+ for (const part of this.parts(slot)) {
1821
+ for (const handed of part.getCallSignatures()) {
1822
+ const given = this.parameterAt(handed, request.param);
1823
+ if (given === undefined || given === VARIADIC)
1824
+ continue;
1825
+ const resolves = paths.every(members => {
1826
+ let at = given;
1827
+ for (const member of members) {
1828
+ const property = this.declaredProperty(at, member);
1829
+ if (!property)
1830
+ return false;
1831
+ at = this.checker.getTypeOfSymbol(property);
1832
+ }
1833
+ return true;
1834
+ });
1835
+ if (resolves)
1836
+ return undefined;
1837
+ best = failed('request_member_missing');
1838
+ }
1839
+ }
1840
+ return best;
1841
+ }
1683
1842
  /**
1684
1843
  * The declarations spell `name` in one of `member`'s parameters: an
1685
1844
  * overload whose parameter there is that literal, or a key of the map a
@@ -107,5 +107,49 @@ export interface PositionRowsOptions {
107
107
  * a directory inside the scanned trees anywhere but beneath `.carrick`.
108
108
  */
109
109
  export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
110
+ /** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
111
+ export declare function declaringFileName(program: ts.Program, paths: PathNames, file: ts.SourceFile): string | null;
112
+ /**
113
+ * Names for the files rows point at, never an absolute path: relative to the
114
+ * scanned repo's root when inside it (its real path too, so a root reached
115
+ * through a link still contains its files), `lib:<name>` for the compiler's
116
+ * default library, `pkg:<name>/<path>` for an installed package outside the
117
+ * root, and nothing otherwise.
118
+ */
119
+ export declare class PathNames {
120
+ private readonly roots;
121
+ private readonly packageNames;
122
+ /**
123
+ * Answers by declaring file: a service's rows name a few thousand files
124
+ * hundreds of thousands of times, and the module graph's answer is a scan
125
+ * of its packages (on a Deno service it was 40% of the walk).
126
+ */
127
+ private readonly declaringFiles;
128
+ private readonly declaringPackages;
129
+ constructor(scanRoot: string);
130
+ /** The root `file` lies under, and its path from there; `undefined` outside every root. */
131
+ private inside;
132
+ /** A request file's name: relative to the root, which every file the scanner sends is inside. */
133
+ relative(file: string): string;
134
+ /** What a declaring file is written as, or `null` when nothing names it. */
135
+ declaringFile(fileName: string): string | null;
136
+ private nameDeclaringFile;
137
+ /**
138
+ * The package a declaring file belongs to: the module graph's or the path's
139
+ * answer (`installedPackageOf`), else, for a file inside the root, the
140
+ * `name` of the nearest package.json at or above it, up to the root.
141
+ */
142
+ declaringPackage(fileName: string, packageOf: (filePath: string) => string | undefined): string | null;
143
+ private nameDeclaringPackage;
144
+ private packageNameIn;
145
+ /**
146
+ * `import("/abs/path")` in a printed type, written with the declaring
147
+ * file's name instead. The print has no enclosing node, so it names every
148
+ * module by the file that declares it and one type reads the same from
149
+ * every file; it also skips the accessibility search an enclosing node
150
+ * costs (40% of the walk on a 600-file service).
151
+ */
152
+ scrubType(text: string): string;
153
+ }
110
154
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
111
155
  export declare function kindOf(node: ts.Node): NodeKind | undefined;
@@ -242,6 +242,10 @@ export function writePositionRows(options) {
242
242
  counts.load_ms = Math.round(counts.load_ms);
243
243
  return counts;
244
244
  }
245
+ /** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
246
+ export function declaringFileName(program, paths, file) {
247
+ return program.isSourceFileDefaultLibrary(file) ? `lib:${path.basename(file.fileName)}` : paths.declaringFile(file.fileName);
248
+ }
245
249
  /** A file's line, with its keys in the pinned order. */
246
250
  function fileLine(line) {
247
251
  return JSON.stringify(line);
@@ -253,7 +257,7 @@ function fileLine(line) {
253
257
  * default library, `pkg:<name>/<path>` for an installed package outside the
254
258
  * root, and nothing otherwise.
255
259
  */
256
- class PathNames {
260
+ export class PathNames {
257
261
  roots;
258
262
  packageNames = new Map();
259
263
  /**
@@ -466,9 +470,7 @@ class PositionWalker {
466
470
  }
467
471
  /** How rows name a position in a program file: its file, 1-based line and package. */
468
472
  located(declaringFile, position) {
469
- const declFile = this.program.isSourceFileDefaultLibrary(declaringFile)
470
- ? `lib:${path.basename(declaringFile.fileName)}`
471
- : this.paths.declaringFile(declaringFile.fileName);
473
+ const declFile = declaringFileName(this.program, this.paths, declaringFile);
472
474
  const declLine = declaringFile.getLineAndCharacterOfPosition(position).line + 1;
473
475
  const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
474
476
  return { declFile, declLine, declPackage };
@@ -115,6 +115,12 @@ export const MACHINERY_MEMBER_INDICATORS = new Set([
115
115
  'clone',
116
116
  'json',
117
117
  'statusText',
118
+ // FROZEN (old path, 2026-10-21): the reply-object half below only (the
119
+ // fetch half above and the threshold stay). Deletions and safety fixes
120
+ // only. Fixes for wrong or missing types go in labelled reply objects
121
+ // (carrick#2472 C4) and the language library rows (carrick#2445). Delete
122
+ // when a delete-and-replay on the four repos moves no row wrongly, after
123
+ // carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
118
124
  // Node http ServerResponse / reply-object surface
119
125
  'statusCode',
120
126
  'statusMessage',
@@ -1628,6 +1634,8 @@ export class TypeInferrer {
1628
1634
  branches = [
1629
1635
  ...returnedBranches,
1630
1636
  ...statementSends
1637
+ // FROZEN (old path, 2026-10-21): the `statementEndsFunctionPath` test
1638
+ // here; see that method.
1631
1639
  .filter((s) => s.send === locatedBranch || this.statementEndsFunctionPath(s.statement, func))
1632
1640
  .map((s) => s.send),
1633
1641
  ];
@@ -1702,6 +1710,10 @@ export class TypeInferrer {
1702
1710
  }
1703
1711
  return sends;
1704
1712
  }
1713
+ // FROZEN (old path, 2026-10-21): this method and its one use. Deletions and
1714
+ // safety fixes only. Fixes for wrong or missing types go in send versus
1715
+ // setter, read from the framework label (carrick#2472 C4). Delete when C4 is
1716
+ // released (carrick#2472, C6).
1705
1717
  /**
1706
1718
  * True when nothing in `func` runs after `statement`: a bare `return;`
1707
1719
  * follows it, or it is the last statement of its block and the statement
@@ -1748,6 +1760,11 @@ export class TypeInferrer {
1748
1760
  return false;
1749
1761
  }
1750
1762
  }
1763
+ // FROZEN (old path, 2026-10-21): this method's structural test (the whole
1764
+ // method is the upper bound). Deletions and safety fixes only. Fixes for
1765
+ // wrong or missing types go in labelled sends (carrick#2472 C4). Delete when
1766
+ // a delete-and-replay on the four repos moves no row wrongly, after
1767
+ // carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
1751
1768
  /**
1752
1769
  * True when `candidate` is a response send: a call or `new` whose result is
1753
1770
  * transport machinery (by the extraction config's verified rule or the
@@ -5288,6 +5305,10 @@ export class TypeInferrer {
5288
5305
  }
5289
5306
  return flattened;
5290
5307
  }
5308
+ // FROZEN (old path, 2026-10-21): this method only. Deletions and safety fixes
5309
+ // only. Fixes for wrong or missing types go in labelled sends (carrick#2472
5310
+ // C4). Delete when a delete-and-replay on the four repos moves no row
5311
+ // wrongly, after carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
5291
5312
  /**
5292
5313
  * The callees these returned expressions PROVE are response serialisers.
5293
5314
  *
@@ -411,6 +411,18 @@ export interface Yields {
411
411
  export: string;
412
412
  side: string;
413
413
  }
414
+ /**
415
+ * Where a route handler's request holds its path and method (#2474 R0): the
416
+ * handler's parameter `param` is the request, and each of the others is a
417
+ * member path read off it, relative to that parameter (`['path']`,
418
+ * `['raw', 'url']`). At least one of `path` or `url`.
419
+ */
420
+ export interface RequestAt {
421
+ param: number;
422
+ path?: string[];
423
+ url?: string[];
424
+ method?: string[];
425
+ }
414
426
  /** The closed role list; the role alone picks the checks a claim needs. */
415
427
  export type LibraryRole = 'http_client' | 'graphql_client' | 'broker' | 'in_process_bus' | 'socket' | 'server_framework' | 'none';
416
428
  /** What an `op` claim does on the wire. */
@@ -467,6 +479,15 @@ export type LibraryClaim = {
467
479
  key_labels?: Record<string, KeyLabel>;
468
480
  name_scope?: NameScope;
469
481
  picker?: string;
482
+ /**
483
+ * Route fields (#2474 R0): the member runs the function here and hands
484
+ * it, as `yields` says, an instance bound under `name`. Comes with
485
+ * `yields`.
486
+ */
487
+ runs?: ClaimSlot;
488
+ yields?: Yields;
489
+ /** Route fields (#2474 R0): the instance handed here is served under `name`. */
490
+ mounts?: ClaimSlot;
470
491
  } | {
471
492
  kind: 'op';
472
493
  op: LibraryOp;
@@ -502,6 +523,8 @@ export type LibraryClaim = {
502
523
  runs?: RunsSlot;
503
524
  /** Label v2, `receive` only: what the handler is handed. */
504
525
  yields?: Yields;
526
+ /** Route fields (#2474 R0), `receive` only: where the handler's request holds its path and method. */
527
+ request?: RequestAt;
505
528
  } | {
506
529
  /** A name the library emits itself, spelled in one of `member`'s parameters. */
507
530
  kind: 'reserved';
@@ -538,6 +561,8 @@ export interface LibraryCheck {
538
561
  export interface VerifyLibraryClaimsRequest extends BaseRequest {
539
562
  action: 'verify_library_claims';
540
563
  from_dir: string;
564
+ /** The scanned repo's root, as on `init`: a verdict's `declarations` are named from it, as position rows are. */
565
+ scan_root?: string;
541
566
  checks: LibraryCheck[];
542
567
  /** Checks not reached in time come back `unchecked` with reason `budget`; null or absent is the default. */
543
568
  budget_ms?: number | null;
@@ -825,6 +850,11 @@ export interface RetypeCheckResponse extends BaseResponse {
825
850
  outcomes?: RetypeOutcome[];
826
851
  errors?: string[];
827
852
  }
853
+ /** Where a verified op's member is declared on its receiver, as a position row's `decl_file` and `decl_line`. */
854
+ export interface SemanticsDeclaration {
855
+ decl_file: string;
856
+ decl_line: number;
857
+ }
828
858
  /**
829
859
  * The verdict on one `(claim_id, receiver)` pair.
830
860
  *
@@ -838,6 +868,8 @@ export interface SemanticsResult {
838
868
  verdict: 'verified' | 'failed' | 'unchecked';
839
869
  /** Absent exactly when verified. */
840
870
  reason?: string;
871
+ /** Verified op verdicts only: the declarations its member resolves to on the receiver. */
872
+ declarations?: SemanticsDeclaration[];
841
873
  }
842
874
  /** How one package of the request resolved, for logs. */
843
875
  export interface SemanticsModule {