@suss/resolution 0.30.0 → 0.31.1

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/dist/index.d.cts CHANGED
@@ -312,6 +312,62 @@ interface SpannedNode<N> {
312
312
  */
313
313
  declare function nodeOfKey<N extends SpannedNode<N>>(rootsByFile: ReadonlyMap<string, N>, key: string): N | null;
314
314
 
315
+ /**
316
+ * packWords.ts: the facts a pack states about its own library.
317
+ *
318
+ * Nothing in a source file says that `Account.find(id)` gives back one
319
+ * Account, or that entering `httpx.Client()` gives back the client it
320
+ * built. The library knows, the pack writes it down, and an adapter puts
321
+ * it in the store. Every adapter did that with a loop of its own, so
322
+ * each new word had to be written three times. `addPackWords` is the one
323
+ * loop; an adapter maps its own pack type onto `PackWords` and calls it.
324
+ *
325
+ * The field names match the fact vocabulary at the top of `index.ts`.
326
+ */
327
+
328
+ /** A method on a class reaching `base` gives back one of that class. */
329
+ interface GivesBackOne {
330
+ base: string;
331
+ method: string;
332
+ }
333
+ /** The same, for a library that takes the class at position `argument` instead of as the receiver. */
334
+ interface GivesBackOneOfArgument extends GivesBackOne {
335
+ argument: number;
336
+ }
337
+ /** The same again, for the bare function `module` exports under `name`. */
338
+ interface GivesBackOneOfImport {
339
+ module: string;
340
+ name: string;
341
+ argument: number;
342
+ }
343
+ /** Entering one of `module`'s `name` gives back that same object. */
344
+ interface EntersAsSelf {
345
+ module: string;
346
+ name: string;
347
+ }
348
+ /** Calling `callee` out of `module` gives back its argument at `argument`. */
349
+ interface UnwrapsByName {
350
+ callee: string;
351
+ argument: number;
352
+ module: string;
353
+ }
354
+ /** A field given a call of `module`'s `name` declares an association. */
355
+ interface AssociationConstructor {
356
+ module: string;
357
+ name: string;
358
+ }
359
+ /** What a run's packs say about their own libraries, in one language-neutral shape. */
360
+ interface PackWords {
361
+ givesBackOne?: ReadonlyArray<GivesBackOne>;
362
+ givesBackOneOfArgument?: ReadonlyArray<GivesBackOneOfArgument>;
363
+ givesBackOneOfImport?: ReadonlyArray<GivesBackOneOfImport>;
364
+ entersAsSelf?: ReadonlyArray<EntersAsSelf>;
365
+ unwrapsByName?: ReadonlyArray<UnwrapsByName>;
366
+ associationConstructor?: ReadonlyArray<AssociationConstructor>;
367
+ }
368
+ /** Put a run's pack declarations in the store, so the shared rules can read them. */
369
+ declare function addPackWords(db: Database, words: PackWords): void;
370
+
315
371
  /**
316
372
  * The rules an adapter evaluates, and how a question is put to them.
317
373
  *
@@ -334,11 +390,24 @@ declare const ASKING_RELATIONS: readonly string[];
334
390
  * reaches it. Built once per language and kept, because the rewrite
335
391
  * does not depend on the facts.
336
392
  *
393
+ * The questions asked under an allocation site are left out, which
394
+ * drops every rule behind them, so a caller that never mentions a
395
+ * context pays nothing for the second closure.
396
+ *
337
397
  * `SUSS_RESOLUTION_ON_DEMAND=0` runs the same rules unrestricted. Both
338
398
  * settings give the same answers; they differ in how much never gets
339
399
  * derived at all.
340
400
  */
341
401
  declare function resolutionProgram(languageRules?: readonly Rule[]): OnDemandRules;
402
+ /**
403
+ * The same rules with the three questions under an allocation site
404
+ * added, which is what `askResolutionUnder` evaluates.
405
+ *
406
+ * Running both programs over one database costs the context-free one
407
+ * its resume state, so a caller with many context-free questions and a
408
+ * few under a site is better off asking the context-free ones first.
409
+ */
410
+ declare function resolutionUnderProgram(languageRules?: readonly Rule[]): OnDemandRules;
342
411
  /**
343
412
  * What a caller takes back once it has read an answer: the question and
344
413
  * every relation derived under it. Empty when nothing is demand-driven,
@@ -357,6 +426,59 @@ declare function queryFacts(program: OnDemandRules): readonly string[];
357
426
  * question, which is how each adapter reads a project.
358
427
  */
359
428
  declare function askResolution(db: Database, keys: Iterable<string>, asking?: string, program?: OnDemandRules): void;
429
+ /**
430
+ * How many rows the joins may read for one question under a site.
431
+ *
432
+ * The biggest question a project could answer read 200k rows on the
433
+ * largest public corpus target and 600k on the regression fixtures. A
434
+ * question that cannot be answered walks every node under every context
435
+ * and reads that much per rule, round after round, without settling.
436
+ * The gap is wide enough for thirty times the biggest healthy question,
437
+ * which is a few seconds before the walk is given up on.
438
+ */
439
+ declare const UNDER_QUESTION_ROW_BUDGET = 20000000;
440
+ /**
441
+ * How many rows the joins may read for every under-question a run asks,
442
+ * together.
443
+ *
444
+ * The per-question budget stops one question walking forever. It says
445
+ * nothing about how many questions a project asks, and a value read
446
+ * under every site of a class built in seventy places is seventy
447
+ * questions, each a fixpoint of its own. Twenty of those at the
448
+ * per-question limit is seven minutes, which is a hang however well
449
+ * each behaved on its own.
450
+ *
451
+ * This is two runaway questions' worth, or sixty-six of the biggest
452
+ * one measured. Past it a run keeps the context-free answer.
453
+ */
454
+ declare const UNDER_RUN_ROW_BUDGET = 40000000;
455
+ /** Whether a question was worked through, or given up on a budget. */
456
+ type UnderOutcome = "answered" | "abandoned";
457
+ /** What the under-questions of one run have cost it so far. */
458
+ interface UnderSpend {
459
+ rows: number;
460
+ asked: number;
461
+ abandoned: number;
462
+ skipped: number;
463
+ }
464
+ /**
465
+ * What the under-questions of this database have cost, for a caller
466
+ * reporting on a run.
467
+ */
468
+ declare function underQuestionSpend(db: Database): Readonly<UnderSpend>;
469
+ /**
470
+ * `askResolution` for a value read under one allocation site. Each pair
471
+ * is a question of its own, so asking about a value under two sites is
472
+ * two rows, and asking the same pair twice costs nothing.
473
+ *
474
+ * A question that runs past `questionBudget`, or one asked after the
475
+ * run has spent `runBudget` on questions before it, is abandoned: the
476
+ * rows it added come back out, the answer relations stay as empty as
477
+ * they were, and the caller is told so it can keep the context-free
478
+ * answer instead. The pair stays marked as asked either way, so nobody
479
+ * pays for it twice.
480
+ */
481
+ declare function askResolutionUnder(db: Database, pairs: Iterable<readonly [string, string]>, program?: OnDemandRules, questionBudget?: number, runBudget?: number): UnderOutcome;
360
482
 
361
483
  /**
362
484
  * The result shape a why session hands back, shared by every language
@@ -418,22 +540,58 @@ interface ExplainResolvedKeyOptions {
418
540
  declare function explainResolvedKey(options: ExplainResolvedKeyOptions): WhyExplained | null;
419
541
 
420
542
  /**
421
- * The single-answer policy shared by every question shaped `[key,
422
- * answer]`, such as `wantedIsWrittenAs` or `wantedSubjectWritten`. A
423
- * call is written as itself, so a call asked about directly always
424
- * matches its own key; that row is dropped before the count so one
425
- * other answer settles it instead of counting as two.
543
+ * What counts as an answer to a question shaped `[key, answer]`, such as
544
+ * `wantedIsWrittenAs` or `wantedSubjectWritten`. A call is written as
545
+ * itself, so a call asked about directly always matches its own key; that
546
+ * row is dropped before the count so one other answer settles it instead
547
+ * of counting as two.
426
548
  *
427
549
  * A placeholder write, `client = None` before a guard fills it in, is
428
550
  * set aside the same way when the key has any other answer. A key
429
551
  * written only as a placeholder keeps it.
552
+ *
553
+ * A caller that can only use one answer takes `singleAnswers`; one with
554
+ * something to say about a value written two ways takes `answersByKey`.
430
555
  */
431
556
 
432
- /** The one answer each key settles on, once a key's match against itself is dropped. */
557
+ /** Every answer each key has, in the order the rows arrive, with the two drops above applied. */
558
+ declare function answersByKey(rows: Iterable<Tuple>, placeholders?: ReadonlySet<string>): Map<string, string[]>;
559
+ /** The one answer each key settles on, for a caller that treats several as none. */
433
560
  declare function singleAnswers(rows: Iterable<Tuple>, placeholders?: ReadonlySet<string>): Map<string, string>;
434
561
  /** The keys an adapter marked `placeholderValue`, for passing to `singleAnswers`. */
435
562
  declare function placeholderValues(db: Database): ReadonlySet<string>;
436
563
 
564
+ /**
565
+ * Reading an answer that was asked for under one allocation site.
566
+ *
567
+ * The context-free readers take a key and get everything the rules
568
+ * settled it on. These take a key and a site, and get what the value is
569
+ * when the receiver behind it is the one that site made, so two clients
570
+ * of one class answer apart here and together context-free.
571
+ *
572
+ * Nothing is derived until `askResolutionUnder` has put the pair as a
573
+ * question, the same way the context-free readers need `askResolution`.
574
+ */
575
+
576
+ /** Every expression the value was written as under this site. */
577
+ declare function isWrittenAsUnder(db: Database, key: string, site: string): string[];
578
+ /** Every function or object the value comes down to under this site. */
579
+ declare function comesToUnder(db: Database, key: string, site: string): string[];
580
+ /** Every object the value refers to under this site, sites included. */
581
+ declare function objectOfUnder(db: Database, key: string, site: string): string[];
582
+ /**
583
+ * The single expression the value was written as under this site, under
584
+ * the policy the context-free reader applies: two answers are ambiguity,
585
+ * and ambiguity is nothing.
586
+ */
587
+ declare function writtenValueUnder(db: Database, key: string, site: string): string | null;
588
+ /**
589
+ * Every site a class was made at, once `askResolution` has asked
590
+ * `wantedSites` about it. These are the sites to put a value question
591
+ * under.
592
+ */
593
+ declare function allocationSitesOf(db: Database, cls: string): string[];
594
+
437
595
  /**
438
596
  * What a name written more than once comes down to.
439
597
  *
@@ -483,13 +641,29 @@ interface NameWrite {
483
641
  */
484
642
  declare function valueLeftByWrites(writes: readonly NameWrite[], ordered: boolean): string | null;
485
643
 
644
+ type Ask = (keys: readonly string[]) => void;
486
645
  /**
487
646
  * The single expression a value was written as, once the caller has
488
647
  * asked the rules about `key`. A call to a project function is asked
489
648
  * about as well, through `ask`, so the answer is what that function
490
649
  * returns rather than the call itself.
491
650
  */
492
- declare function writtenValueOf(db: Database, key: string, ask: (keys: readonly string[]) => void): string | null;
651
+ declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string): string | null;
652
+ /**
653
+ * Every expression a value was written as. More than one is a value with
654
+ * more than one possible source, which a caller says something about at
655
+ * the site rather than reading as the same nothing as a chain that went
656
+ * nowhere.
657
+ */
658
+ declare function writtenValuesOf(db: Database, key: string, ask: Ask, relation?: string): string[];
659
+ /**
660
+ * The one expression each key was written as, for a caller with several
661
+ * keys at once. Keys the rules settled on nothing, or on more than one
662
+ * expression, are left out. One round of asking covers them all, which
663
+ * is what a caller gets here that a loop over `writtenValueOf` would
664
+ * not.
665
+ */
666
+ declare function writtenValuesByKey(db: Database, keys: readonly string[], ask: Ask, relation?: string): Map<string, string>;
493
667
 
494
668
  /** A step to the value x is written as. */
495
669
  declare const VALUE_STEP: _suss_datalog.Term;
@@ -501,23 +675,35 @@ declare const VALUE_STEP: _suss_datalog.Term;
501
675
  */
502
676
  declare const NAMESPACE_IMPORT_NAME = "*";
503
677
  declare const NAMESPACE_IMPORT: _suss_datalog.Term;
678
+ /**
679
+ * The label on the `comesFrom` rule for a member read off a whole-module
680
+ * import. `explain` tells that proof from the two import ones by it.
681
+ */
682
+ declare const NAMESPACE_MEMBER_RULE = "namespace member";
683
+ /**
684
+ * The label on the `contains` rule that walks a class's ancestry.
685
+ * `explain` says a name came from a base class when it fired.
686
+ */
687
+ declare const BASE_CLASS_RULE = "base class";
688
+ /** A step from an instance to the class it is one of. */
689
+ declare const INSTANCE_STEP: _suss_datalog.Term;
504
690
  /** A step to what running the call x is handed back. */
505
691
  declare const RESULT_STEP: _suss_datalog.Term;
692
+ /** The context a value read outside every allocation site is read under. */
693
+ declare const NO_CONTEXT_NAME = "none";
694
+ declare const NO_CONTEXT: _suss_datalog.Term;
506
695
  /**
507
- * The rules every language adapter shares. Concatenate a language's own
508
- * rules onto these before evaluating.
509
- *
510
- * Every construct states its hops once, as `stepsTo(x, y, kind)`, which
511
- * says x leads to y. A value step goes to the value x is written as, and a
512
- * result step runs the call x is and goes to what that call handed back.
513
- * `reaches` is the closure of those steps, and a walk counts as a result
514
- * walk once it has run a call anywhere along it.
696
+ * Rules with a `stepsTo` twin for each hop among them.
515
697
  *
516
- * Each question is that one closure with its own stopping condition, so
517
- * adding a construct is one step and every question gets it, and adding
518
- * a question is a stopping condition and no steps at all.
698
+ * `reaches` reads `stepsTo` and `reachesUnder` reads `hop`. A rule
699
+ * passing one through to the other would look tidier and costs far
700
+ * more: the demand for `stepsTo` is one of the largest relations a run
701
+ * derives, and every row of it would be copied into a demand for `hop`
702
+ * whether or not anybody asked about a context. A language that states
703
+ * a hop of its own passes it through here for the same reason.
519
704
  */
520
- declare const RESOLUTION_RULES: _suss_datalog.Rule[];
705
+ declare function alsoSteps(rules: readonly Rule[]): Rule[];
706
+ declare const RESOLUTION_RULES: Rule[];
521
707
  /**
522
708
  * The questions a caller asks, written as rules. Two facts say somebody
523
709
  * is asking: `wanted(x)` for what a value is, and `wantedOrigin(x)` for
@@ -535,8 +721,17 @@ declare const RESOLUTION_RULES: _suss_datalog.Rule[];
535
721
  * the value's function makes, so a caller that only wants to know what a
536
722
  * handler resolves to should not pay for that.
537
723
  */
538
- declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
724
+ declare const RESOLUTION_QUESTIONS: Rule[];
539
725
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
540
726
  declare const ANSWER_RELATIONS: string[];
727
+ /**
728
+ * The three of those a caller asks under one allocation site.
729
+ *
730
+ * They are listed apart because leaving them out of what a program has
731
+ * to answer drops the whole second closure from it. A run that never
732
+ * mentions a context would otherwise carry a thousand rewritten rules
733
+ * it can never fire, and the engine reads every rule once a round.
734
+ */
735
+ declare const UNDER_ANSWER_RELATIONS: string[];
541
736
 
542
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, couldBeSettled, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
737
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type EntersAsSelf, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersByKey, askResolution, askResolutionUnder, calleeOutcomeOf, calleeOutcomes, checkFactContract, comesToUnder, couldBeSettled, explainResolutionProof, explainResolvedKey, isWrittenAsUnder, joinMountedPath, mountPathsOf, nodeOfKey, objectOfUnder, placeholderValues, queryFacts, renderExplanation, resolutionProgram, resolutionUnderProgram, singleAnswers, startsAtName, underQuestionSpend, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };
package/dist/index.d.ts CHANGED
@@ -312,6 +312,62 @@ interface SpannedNode<N> {
312
312
  */
313
313
  declare function nodeOfKey<N extends SpannedNode<N>>(rootsByFile: ReadonlyMap<string, N>, key: string): N | null;
314
314
 
315
+ /**
316
+ * packWords.ts: the facts a pack states about its own library.
317
+ *
318
+ * Nothing in a source file says that `Account.find(id)` gives back one
319
+ * Account, or that entering `httpx.Client()` gives back the client it
320
+ * built. The library knows, the pack writes it down, and an adapter puts
321
+ * it in the store. Every adapter did that with a loop of its own, so
322
+ * each new word had to be written three times. `addPackWords` is the one
323
+ * loop; an adapter maps its own pack type onto `PackWords` and calls it.
324
+ *
325
+ * The field names match the fact vocabulary at the top of `index.ts`.
326
+ */
327
+
328
+ /** A method on a class reaching `base` gives back one of that class. */
329
+ interface GivesBackOne {
330
+ base: string;
331
+ method: string;
332
+ }
333
+ /** The same, for a library that takes the class at position `argument` instead of as the receiver. */
334
+ interface GivesBackOneOfArgument extends GivesBackOne {
335
+ argument: number;
336
+ }
337
+ /** The same again, for the bare function `module` exports under `name`. */
338
+ interface GivesBackOneOfImport {
339
+ module: string;
340
+ name: string;
341
+ argument: number;
342
+ }
343
+ /** Entering one of `module`'s `name` gives back that same object. */
344
+ interface EntersAsSelf {
345
+ module: string;
346
+ name: string;
347
+ }
348
+ /** Calling `callee` out of `module` gives back its argument at `argument`. */
349
+ interface UnwrapsByName {
350
+ callee: string;
351
+ argument: number;
352
+ module: string;
353
+ }
354
+ /** A field given a call of `module`'s `name` declares an association. */
355
+ interface AssociationConstructor {
356
+ module: string;
357
+ name: string;
358
+ }
359
+ /** What a run's packs say about their own libraries, in one language-neutral shape. */
360
+ interface PackWords {
361
+ givesBackOne?: ReadonlyArray<GivesBackOne>;
362
+ givesBackOneOfArgument?: ReadonlyArray<GivesBackOneOfArgument>;
363
+ givesBackOneOfImport?: ReadonlyArray<GivesBackOneOfImport>;
364
+ entersAsSelf?: ReadonlyArray<EntersAsSelf>;
365
+ unwrapsByName?: ReadonlyArray<UnwrapsByName>;
366
+ associationConstructor?: ReadonlyArray<AssociationConstructor>;
367
+ }
368
+ /** Put a run's pack declarations in the store, so the shared rules can read them. */
369
+ declare function addPackWords(db: Database, words: PackWords): void;
370
+
315
371
  /**
316
372
  * The rules an adapter evaluates, and how a question is put to them.
317
373
  *
@@ -334,11 +390,24 @@ declare const ASKING_RELATIONS: readonly string[];
334
390
  * reaches it. Built once per language and kept, because the rewrite
335
391
  * does not depend on the facts.
336
392
  *
393
+ * The questions asked under an allocation site are left out, which
394
+ * drops every rule behind them, so a caller that never mentions a
395
+ * context pays nothing for the second closure.
396
+ *
337
397
  * `SUSS_RESOLUTION_ON_DEMAND=0` runs the same rules unrestricted. Both
338
398
  * settings give the same answers; they differ in how much never gets
339
399
  * derived at all.
340
400
  */
341
401
  declare function resolutionProgram(languageRules?: readonly Rule[]): OnDemandRules;
402
+ /**
403
+ * The same rules with the three questions under an allocation site
404
+ * added, which is what `askResolutionUnder` evaluates.
405
+ *
406
+ * Running both programs over one database costs the context-free one
407
+ * its resume state, so a caller with many context-free questions and a
408
+ * few under a site is better off asking the context-free ones first.
409
+ */
410
+ declare function resolutionUnderProgram(languageRules?: readonly Rule[]): OnDemandRules;
342
411
  /**
343
412
  * What a caller takes back once it has read an answer: the question and
344
413
  * every relation derived under it. Empty when nothing is demand-driven,
@@ -357,6 +426,59 @@ declare function queryFacts(program: OnDemandRules): readonly string[];
357
426
  * question, which is how each adapter reads a project.
358
427
  */
359
428
  declare function askResolution(db: Database, keys: Iterable<string>, asking?: string, program?: OnDemandRules): void;
429
+ /**
430
+ * How many rows the joins may read for one question under a site.
431
+ *
432
+ * The biggest question a project could answer read 200k rows on the
433
+ * largest public corpus target and 600k on the regression fixtures. A
434
+ * question that cannot be answered walks every node under every context
435
+ * and reads that much per rule, round after round, without settling.
436
+ * The gap is wide enough for thirty times the biggest healthy question,
437
+ * which is a few seconds before the walk is given up on.
438
+ */
439
+ declare const UNDER_QUESTION_ROW_BUDGET = 20000000;
440
+ /**
441
+ * How many rows the joins may read for every under-question a run asks,
442
+ * together.
443
+ *
444
+ * The per-question budget stops one question walking forever. It says
445
+ * nothing about how many questions a project asks, and a value read
446
+ * under every site of a class built in seventy places is seventy
447
+ * questions, each a fixpoint of its own. Twenty of those at the
448
+ * per-question limit is seven minutes, which is a hang however well
449
+ * each behaved on its own.
450
+ *
451
+ * This is two runaway questions' worth, or sixty-six of the biggest
452
+ * one measured. Past it a run keeps the context-free answer.
453
+ */
454
+ declare const UNDER_RUN_ROW_BUDGET = 40000000;
455
+ /** Whether a question was worked through, or given up on a budget. */
456
+ type UnderOutcome = "answered" | "abandoned";
457
+ /** What the under-questions of one run have cost it so far. */
458
+ interface UnderSpend {
459
+ rows: number;
460
+ asked: number;
461
+ abandoned: number;
462
+ skipped: number;
463
+ }
464
+ /**
465
+ * What the under-questions of this database have cost, for a caller
466
+ * reporting on a run.
467
+ */
468
+ declare function underQuestionSpend(db: Database): Readonly<UnderSpend>;
469
+ /**
470
+ * `askResolution` for a value read under one allocation site. Each pair
471
+ * is a question of its own, so asking about a value under two sites is
472
+ * two rows, and asking the same pair twice costs nothing.
473
+ *
474
+ * A question that runs past `questionBudget`, or one asked after the
475
+ * run has spent `runBudget` on questions before it, is abandoned: the
476
+ * rows it added come back out, the answer relations stay as empty as
477
+ * they were, and the caller is told so it can keep the context-free
478
+ * answer instead. The pair stays marked as asked either way, so nobody
479
+ * pays for it twice.
480
+ */
481
+ declare function askResolutionUnder(db: Database, pairs: Iterable<readonly [string, string]>, program?: OnDemandRules, questionBudget?: number, runBudget?: number): UnderOutcome;
360
482
 
361
483
  /**
362
484
  * The result shape a why session hands back, shared by every language
@@ -418,22 +540,58 @@ interface ExplainResolvedKeyOptions {
418
540
  declare function explainResolvedKey(options: ExplainResolvedKeyOptions): WhyExplained | null;
419
541
 
420
542
  /**
421
- * The single-answer policy shared by every question shaped `[key,
422
- * answer]`, such as `wantedIsWrittenAs` or `wantedSubjectWritten`. A
423
- * call is written as itself, so a call asked about directly always
424
- * matches its own key; that row is dropped before the count so one
425
- * other answer settles it instead of counting as two.
543
+ * What counts as an answer to a question shaped `[key, answer]`, such as
544
+ * `wantedIsWrittenAs` or `wantedSubjectWritten`. A call is written as
545
+ * itself, so a call asked about directly always matches its own key; that
546
+ * row is dropped before the count so one other answer settles it instead
547
+ * of counting as two.
426
548
  *
427
549
  * A placeholder write, `client = None` before a guard fills it in, is
428
550
  * set aside the same way when the key has any other answer. A key
429
551
  * written only as a placeholder keeps it.
552
+ *
553
+ * A caller that can only use one answer takes `singleAnswers`; one with
554
+ * something to say about a value written two ways takes `answersByKey`.
430
555
  */
431
556
 
432
- /** The one answer each key settles on, once a key's match against itself is dropped. */
557
+ /** Every answer each key has, in the order the rows arrive, with the two drops above applied. */
558
+ declare function answersByKey(rows: Iterable<Tuple>, placeholders?: ReadonlySet<string>): Map<string, string[]>;
559
+ /** The one answer each key settles on, for a caller that treats several as none. */
433
560
  declare function singleAnswers(rows: Iterable<Tuple>, placeholders?: ReadonlySet<string>): Map<string, string>;
434
561
  /** The keys an adapter marked `placeholderValue`, for passing to `singleAnswers`. */
435
562
  declare function placeholderValues(db: Database): ReadonlySet<string>;
436
563
 
564
+ /**
565
+ * Reading an answer that was asked for under one allocation site.
566
+ *
567
+ * The context-free readers take a key and get everything the rules
568
+ * settled it on. These take a key and a site, and get what the value is
569
+ * when the receiver behind it is the one that site made, so two clients
570
+ * of one class answer apart here and together context-free.
571
+ *
572
+ * Nothing is derived until `askResolutionUnder` has put the pair as a
573
+ * question, the same way the context-free readers need `askResolution`.
574
+ */
575
+
576
+ /** Every expression the value was written as under this site. */
577
+ declare function isWrittenAsUnder(db: Database, key: string, site: string): string[];
578
+ /** Every function or object the value comes down to under this site. */
579
+ declare function comesToUnder(db: Database, key: string, site: string): string[];
580
+ /** Every object the value refers to under this site, sites included. */
581
+ declare function objectOfUnder(db: Database, key: string, site: string): string[];
582
+ /**
583
+ * The single expression the value was written as under this site, under
584
+ * the policy the context-free reader applies: two answers are ambiguity,
585
+ * and ambiguity is nothing.
586
+ */
587
+ declare function writtenValueUnder(db: Database, key: string, site: string): string | null;
588
+ /**
589
+ * Every site a class was made at, once `askResolution` has asked
590
+ * `wantedSites` about it. These are the sites to put a value question
591
+ * under.
592
+ */
593
+ declare function allocationSitesOf(db: Database, cls: string): string[];
594
+
437
595
  /**
438
596
  * What a name written more than once comes down to.
439
597
  *
@@ -483,13 +641,29 @@ interface NameWrite {
483
641
  */
484
642
  declare function valueLeftByWrites(writes: readonly NameWrite[], ordered: boolean): string | null;
485
643
 
644
+ type Ask = (keys: readonly string[]) => void;
486
645
  /**
487
646
  * The single expression a value was written as, once the caller has
488
647
  * asked the rules about `key`. A call to a project function is asked
489
648
  * about as well, through `ask`, so the answer is what that function
490
649
  * returns rather than the call itself.
491
650
  */
492
- declare function writtenValueOf(db: Database, key: string, ask: (keys: readonly string[]) => void): string | null;
651
+ declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string): string | null;
652
+ /**
653
+ * Every expression a value was written as. More than one is a value with
654
+ * more than one possible source, which a caller says something about at
655
+ * the site rather than reading as the same nothing as a chain that went
656
+ * nowhere.
657
+ */
658
+ declare function writtenValuesOf(db: Database, key: string, ask: Ask, relation?: string): string[];
659
+ /**
660
+ * The one expression each key was written as, for a caller with several
661
+ * keys at once. Keys the rules settled on nothing, or on more than one
662
+ * expression, are left out. One round of asking covers them all, which
663
+ * is what a caller gets here that a loop over `writtenValueOf` would
664
+ * not.
665
+ */
666
+ declare function writtenValuesByKey(db: Database, keys: readonly string[], ask: Ask, relation?: string): Map<string, string>;
493
667
 
494
668
  /** A step to the value x is written as. */
495
669
  declare const VALUE_STEP: _suss_datalog.Term;
@@ -501,23 +675,35 @@ declare const VALUE_STEP: _suss_datalog.Term;
501
675
  */
502
676
  declare const NAMESPACE_IMPORT_NAME = "*";
503
677
  declare const NAMESPACE_IMPORT: _suss_datalog.Term;
678
+ /**
679
+ * The label on the `comesFrom` rule for a member read off a whole-module
680
+ * import. `explain` tells that proof from the two import ones by it.
681
+ */
682
+ declare const NAMESPACE_MEMBER_RULE = "namespace member";
683
+ /**
684
+ * The label on the `contains` rule that walks a class's ancestry.
685
+ * `explain` says a name came from a base class when it fired.
686
+ */
687
+ declare const BASE_CLASS_RULE = "base class";
688
+ /** A step from an instance to the class it is one of. */
689
+ declare const INSTANCE_STEP: _suss_datalog.Term;
504
690
  /** A step to what running the call x is handed back. */
505
691
  declare const RESULT_STEP: _suss_datalog.Term;
692
+ /** The context a value read outside every allocation site is read under. */
693
+ declare const NO_CONTEXT_NAME = "none";
694
+ declare const NO_CONTEXT: _suss_datalog.Term;
506
695
  /**
507
- * The rules every language adapter shares. Concatenate a language's own
508
- * rules onto these before evaluating.
509
- *
510
- * Every construct states its hops once, as `stepsTo(x, y, kind)`, which
511
- * says x leads to y. A value step goes to the value x is written as, and a
512
- * result step runs the call x is and goes to what that call handed back.
513
- * `reaches` is the closure of those steps, and a walk counts as a result
514
- * walk once it has run a call anywhere along it.
696
+ * Rules with a `stepsTo` twin for each hop among them.
515
697
  *
516
- * Each question is that one closure with its own stopping condition, so
517
- * adding a construct is one step and every question gets it, and adding
518
- * a question is a stopping condition and no steps at all.
698
+ * `reaches` reads `stepsTo` and `reachesUnder` reads `hop`. A rule
699
+ * passing one through to the other would look tidier and costs far
700
+ * more: the demand for `stepsTo` is one of the largest relations a run
701
+ * derives, and every row of it would be copied into a demand for `hop`
702
+ * whether or not anybody asked about a context. A language that states
703
+ * a hop of its own passes it through here for the same reason.
519
704
  */
520
- declare const RESOLUTION_RULES: _suss_datalog.Rule[];
705
+ declare function alsoSteps(rules: readonly Rule[]): Rule[];
706
+ declare const RESOLUTION_RULES: Rule[];
521
707
  /**
522
708
  * The questions a caller asks, written as rules. Two facts say somebody
523
709
  * is asking: `wanted(x)` for what a value is, and `wantedOrigin(x)` for
@@ -535,8 +721,17 @@ declare const RESOLUTION_RULES: _suss_datalog.Rule[];
535
721
  * the value's function makes, so a caller that only wants to know what a
536
722
  * handler resolves to should not pay for that.
537
723
  */
538
- declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
724
+ declare const RESOLUTION_QUESTIONS: Rule[];
539
725
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
540
726
  declare const ANSWER_RELATIONS: string[];
727
+ /**
728
+ * The three of those a caller asks under one allocation site.
729
+ *
730
+ * They are listed apart because leaving them out of what a program has
731
+ * to answer drops the whole second closure from it. A run that never
732
+ * mentions a context would otherwise carry a thousand rewritten rules
733
+ * it can never fire, and the engine reads every rule once a round.
734
+ */
735
+ declare const UNDER_ANSWER_RELATIONS: string[];
541
736
 
542
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, couldBeSettled, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
737
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type EntersAsSelf, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersByKey, askResolution, askResolutionUnder, calleeOutcomeOf, calleeOutcomes, checkFactContract, comesToUnder, couldBeSettled, explainResolutionProof, explainResolvedKey, isWrittenAsUnder, joinMountedPath, mountPathsOf, nodeOfKey, objectOfUnder, placeholderValues, queryFacts, renderExplanation, resolutionProgram, resolutionUnderProgram, singleAnswers, startsAtName, underQuestionSpend, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };