@wairon/cli 5.1.1-dev.41 → 5.1.1-dev.43
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/cli/index.js +714 -134
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +709 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -436,7 +436,7 @@ function activeTargetTypes(config) {
|
|
|
436
436
|
const targets = config.targets;
|
|
437
437
|
return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
|
|
438
438
|
}
|
|
439
|
-
var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
439
|
+
var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, CarriedDebtKindSchema, CarriedFindingSchema, CarriedDebtSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
440
440
|
var init_project = __esm({
|
|
441
441
|
"src/models/project.ts"() {
|
|
442
442
|
"use strict";
|
|
@@ -533,6 +533,57 @@ var init_project = __esm({
|
|
|
533
533
|
*/
|
|
534
534
|
maxCognitiveLevel: import_zod3.z.string().optional()
|
|
535
535
|
});
|
|
536
|
+
CarriedDebtKindSchema = import_zod3.z.enum(["drift", "undecided", "unreadable"]);
|
|
537
|
+
CarriedFindingSchema = import_zod3.z.object({
|
|
538
|
+
/** The issue code, which must be one a rule declares CARRYABLE — anything else is UNCARRYABLE_FINDING, an error. */
|
|
539
|
+
code: import_zod3.z.string(),
|
|
540
|
+
/** The spec the finding is anchored to. */
|
|
541
|
+
spec: import_zod3.z.string(),
|
|
542
|
+
/** The site inside that spec the finding names — for the code-vs-spec call checks, the contract method. */
|
|
543
|
+
at: import_zod3.z.string(),
|
|
544
|
+
/**
|
|
545
|
+
* The units an aggregating finding covers: the crossings of one
|
|
546
|
+
* UNDECLARED_COLOCATED_CALL, the steps of one CALL_STEP_UNREALIZED. Keyed by
|
|
547
|
+
* code + spec + site ALONE, a 24th crossing added to a finding that already
|
|
548
|
+
* lists 23 would be carried by an entry nobody wrote for it — so a live
|
|
549
|
+
* finding is carried only when every unit it reports is listed here, and a
|
|
550
|
+
* unit that appears is reported as new.
|
|
551
|
+
*/
|
|
552
|
+
covers: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
553
|
+
});
|
|
554
|
+
CarriedDebtSchema = import_zod3.z.object({
|
|
555
|
+
/** Which of the three kinds this reason is (see CarriedDebtKindSchema). */
|
|
556
|
+
kind: CarriedDebtKindSchema,
|
|
557
|
+
/** The reason itself, in the author's own words — what is true here, and what paying it would take. */
|
|
558
|
+
why: import_zod3.z.string().min(1),
|
|
559
|
+
/**
|
|
560
|
+
* This classification is PROVISIONAL, and this is what would settle it.
|
|
561
|
+
*
|
|
562
|
+
* A confident-sounding `why` that is wrong is worse than a missing one: it
|
|
563
|
+
* reads as settled, so nobody looks again, and the register keeps counting
|
|
564
|
+
* the finding under a kind that was never true. Nothing can check a reason
|
|
565
|
+
* for truth — `kind` and `why` are prose, and STALE_CARRIED_FINDING only
|
|
566
|
+
* ever catches "this stopped applying", never "this still applies for a
|
|
567
|
+
* reason that has become false". What a register CAN do is let an author say
|
|
568
|
+
* out loud that they are not sure yet, and then keep saying it: every run
|
|
569
|
+
* counts the groups marked here beside the kind totals, so the uncertainty
|
|
570
|
+
* is as loud as the debt.
|
|
571
|
+
*
|
|
572
|
+
* It is deliberately a SENTENCE and not a flag, and deliberately on the
|
|
573
|
+
* reason GROUP rather than the finding: what is provisional is the reason,
|
|
574
|
+
* and a reader deciding whether to pick this up needs to know what to
|
|
575
|
+
* measure — "is the adapter really realized by the file that consumes it, or
|
|
576
|
+
* is that a modelling error?" — not merely that somebody once hesitated. A
|
|
577
|
+
* finding whose own classification is uncertain while its neighbours' is not
|
|
578
|
+
* is a different reason, and belongs in its own group.
|
|
579
|
+
*
|
|
580
|
+
* There is no counterpart on `lint.allow`, and that is a decision, not an
|
|
581
|
+
* omission: see the note on ConformanceRuleConfig.carried.
|
|
582
|
+
*/
|
|
583
|
+
revisit: import_zod3.z.string().min(1).optional(),
|
|
584
|
+
/** The findings this reason explains. */
|
|
585
|
+
findings: import_zod3.z.array(CarriedFindingSchema)
|
|
586
|
+
});
|
|
536
587
|
ConformanceRuleConfigSchema = import_zod3.z.object({
|
|
537
588
|
/**
|
|
538
589
|
* Project-relative paths holding this project's own source code; a
|
|
@@ -555,7 +606,39 @@ var init_project = __esm({
|
|
|
555
606
|
* that is now claimed, proven a barrel, or no longer found is
|
|
556
607
|
* STALE_UNCLAIMED_ENTRY.
|
|
557
608
|
*/
|
|
558
|
-
unclaimed: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
609
|
+
unclaimed: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
610
|
+
/**
|
|
611
|
+
* The conformance findings this tree carries as declared debt, grouped by
|
|
612
|
+
* the reason that explains them — `unclaimed`'s shape, for findings about
|
|
613
|
+
* code a spec DOES claim.
|
|
614
|
+
*
|
|
615
|
+
* It is not a second lint.allow, and the difference is the claim each makes.
|
|
616
|
+
* An allow says "this finding is wrong here, by design", and is meant to
|
|
617
|
+
* live forever; an entry here says "this finding is RIGHT, and it is not
|
|
618
|
+
* paid yet". A mechanism that cannot tell those apart can never be asked how
|
|
619
|
+
* much the tree owes. Four properties keep it a register:
|
|
620
|
+
* • only a code a rule declares CARRYABLE may appear (UNCARRYABLE_FINDING,
|
|
621
|
+
* an error, which no allow and no --ci waiver can reach);
|
|
622
|
+
* • an entry carries a finding only when it lists EVERY unit that finding
|
|
623
|
+
* reports, so an aggregating finding cannot grow behind it;
|
|
624
|
+
* • an entry that stops applying — gone, or listing a unit no longer
|
|
625
|
+
* reported — is STALE_CARRIED_FINDING, so the register only shrinks;
|
|
626
|
+
* • every entry states its kind and its reason, and `wairon validate`
|
|
627
|
+
* prints the running total, so the debt is loud where a suppression is
|
|
628
|
+
* silent.
|
|
629
|
+
*
|
|
630
|
+
* A reason group may also declare itself PROVISIONAL (`revisit`), and the
|
|
631
|
+
* total says how many did. There is no such marker on a `lint.allow`, and
|
|
632
|
+
* the asymmetry is the point: an entry here does not silence anything — the
|
|
633
|
+
* finding is counted out loud on every run — so marking one uncertain adds a
|
|
634
|
+
* second dial to something already visible. An allow DOES silence, and a
|
|
635
|
+
* "provisional allow" would buy the silence and defer the decision, which is
|
|
636
|
+
* the one combination that cannot be reviewed: the finding is gone and the
|
|
637
|
+
* doubt is in a field nobody opens. The mechanism for a decision nobody has
|
|
638
|
+
* taken is to not take it — leave the warning firing — or, where the code is
|
|
639
|
+
* carryable, an entry of kind `undecided`, which is exactly that sentence.
|
|
640
|
+
*/
|
|
641
|
+
carried: import_zod3.z.array(CarriedDebtSchema).optional()
|
|
559
642
|
});
|
|
560
643
|
DesignDepthSchema = import_zod3.z.enum(["components", "interfaces", "implementations", "narratives"]);
|
|
561
644
|
RulesConfigSchema = import_zod3.z.object({
|
|
@@ -1139,7 +1222,7 @@ var init_defaults = __esm({
|
|
|
1139
1222
|
copilot: ".github/prompts",
|
|
1140
1223
|
codex: ".codex/agents"
|
|
1141
1224
|
};
|
|
1142
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
1225
|
+
WAIRON_VERSION = "5.1.1-dev.43";
|
|
1143
1226
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1144
1227
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1145
1228
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -1651,8 +1734,28 @@ var init_specs = __esm({
|
|
|
1651
1734
|
LintAllowSchema = import_zod8.z.object({
|
|
1652
1735
|
/** The issue code being allowed (see `wairon rules list`). */
|
|
1653
1736
|
code: import_zod8.z.string(),
|
|
1737
|
+
/**
|
|
1738
|
+
* The SITE inside this spec the allow covers, named exactly as the finding
|
|
1739
|
+
* names it (a contract method, an import edge "from -> to", a declared edge
|
|
1740
|
+
* "component -> target"). A finding that names a site is covered ONLY by an
|
|
1741
|
+
* allow naming that same site; a finding that names none is covered only by
|
|
1742
|
+
* an allow that names none either.
|
|
1743
|
+
*/
|
|
1744
|
+
at: import_zod8.z.string().min(1).optional(),
|
|
1745
|
+
/**
|
|
1746
|
+
* The units of an AGGREGATING finding this allow covers — the steps of one
|
|
1747
|
+
* CALL_STEP_UNREALIZED, the crossings of one UNDECLARED_COLOCATED_CALL —
|
|
1748
|
+
* each named the way the finding's own message names it. The allow silences
|
|
1749
|
+
* the finding only when it lists EVERY unit reported, so a unit nobody
|
|
1750
|
+
* decided on surfaces on the day it appears instead of inheriting a decision
|
|
1751
|
+
* taken about its neighbours. Meaningless without `at`, and refused there.
|
|
1752
|
+
*/
|
|
1753
|
+
covers: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
1654
1754
|
/** Why this finding is acceptable here (e.g. "dispatcher — fan-out is the point"). */
|
|
1655
1755
|
reason: import_zod8.z.string().min(1)
|
|
1756
|
+
}).refine((a) => !a.covers?.length || !!a.at, {
|
|
1757
|
+
message: "`covers` names the units of ONE finding, so it needs the `at` that says which finding \u2014 add the site, or drop covers.",
|
|
1758
|
+
path: ["covers"]
|
|
1656
1759
|
});
|
|
1657
1760
|
LintConfigSchema = import_zod8.z.object({
|
|
1658
1761
|
allow: import_zod8.z.array(LintAllowSchema).default([])
|
|
@@ -2812,6 +2915,24 @@ function factsFor(model, sourcePath) {
|
|
|
2812
2915
|
}
|
|
2813
2916
|
return void 0;
|
|
2814
2917
|
}
|
|
2918
|
+
function ownEntry(record, key) {
|
|
2919
|
+
return record && Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
|
|
2920
|
+
}
|
|
2921
|
+
function importBindingOf(facts, name) {
|
|
2922
|
+
return ownEntry(facts.importBindings, name);
|
|
2923
|
+
}
|
|
2924
|
+
function fieldTypesOf(facts, field) {
|
|
2925
|
+
return ownEntry(facts.fieldTypes, field) ?? NO_FIELD_TYPES;
|
|
2926
|
+
}
|
|
2927
|
+
function typeBindingOf(facts, name) {
|
|
2928
|
+
return ownEntry(facts.typeOnlyBindings, name) ?? ownEntry(facts.importBindings, name)?.from;
|
|
2929
|
+
}
|
|
2930
|
+
function callSitesOf(facts, fn) {
|
|
2931
|
+
return ownEntry(facts.functionCallSites, fn);
|
|
2932
|
+
}
|
|
2933
|
+
function hasFunctionBody(facts, symbol) {
|
|
2934
|
+
return callSitesOf(facts, symbol) !== void 0;
|
|
2935
|
+
}
|
|
2815
2936
|
function resolveImport(fromFile, specifier, knownPaths) {
|
|
2816
2937
|
if (!specifier.startsWith(".")) return void 0;
|
|
2817
2938
|
const joined = pathKey(path5.posix.normalize(path5.posix.join(path5.posix.dirname(fromFile), specifier)));
|
|
@@ -2830,11 +2951,12 @@ function resolveImport(fromFile, specifier, knownPaths) {
|
|
|
2830
2951
|
}
|
|
2831
2952
|
return void 0;
|
|
2832
2953
|
}
|
|
2833
|
-
var path5;
|
|
2954
|
+
var path5, NO_FIELD_TYPES;
|
|
2834
2955
|
var init_code_model = __esm({
|
|
2835
2956
|
"src/models/code-model.ts"() {
|
|
2836
2957
|
"use strict";
|
|
2837
2958
|
path5 = __toESM(require("path"));
|
|
2959
|
+
NO_FIELD_TYPES = [];
|
|
2838
2960
|
}
|
|
2839
2961
|
});
|
|
2840
2962
|
|
|
@@ -3198,6 +3320,26 @@ var init_lockfile = __esm({
|
|
|
3198
3320
|
}
|
|
3199
3321
|
});
|
|
3200
3322
|
|
|
3323
|
+
// src/core/spec-files.ts
|
|
3324
|
+
function readSpecFile(filePath) {
|
|
3325
|
+
return readYamlFile(filePath);
|
|
3326
|
+
}
|
|
3327
|
+
function writeSpecFile(filePath, document) {
|
|
3328
|
+
writeYamlFile(filePath, document);
|
|
3329
|
+
}
|
|
3330
|
+
function listSpecFiles(specsDir) {
|
|
3331
|
+
return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
|
|
3332
|
+
}
|
|
3333
|
+
var SPEC_FILE_EXTENSION;
|
|
3334
|
+
var init_spec_files = __esm({
|
|
3335
|
+
"src/core/spec-files.ts"() {
|
|
3336
|
+
"use strict";
|
|
3337
|
+
init_fs();
|
|
3338
|
+
init_yaml();
|
|
3339
|
+
SPEC_FILE_EXTENSION = ".yaml";
|
|
3340
|
+
}
|
|
3341
|
+
});
|
|
3342
|
+
|
|
3201
3343
|
// src/core/narrative-labels.ts
|
|
3202
3344
|
function resolveNarrativeLabels(methodName, steps) {
|
|
3203
3345
|
const errors = [];
|
|
@@ -8863,13 +9005,84 @@ function buildCodeIndex(model) {
|
|
|
8863
9005
|
cache.set(path27, built);
|
|
8864
9006
|
return built;
|
|
8865
9007
|
};
|
|
9008
|
+
const republished = /* @__PURE__ */ new Map();
|
|
9009
|
+
const republishedFrom = (path27) => {
|
|
9010
|
+
const cached = republished.get(path27);
|
|
9011
|
+
if (cached) return cached;
|
|
9012
|
+
const out = /* @__PURE__ */ new Set([path27]);
|
|
9013
|
+
const queue = [path27];
|
|
9014
|
+
while (queue.length) {
|
|
9015
|
+
const from = queue.pop();
|
|
9016
|
+
const f = facts.get(from);
|
|
9017
|
+
if (f?.status !== "analyzed") continue;
|
|
9018
|
+
for (const specifier of f.reexports) {
|
|
9019
|
+
const to = resolveImport(from, specifier, paths);
|
|
9020
|
+
if (to && !out.has(to)) {
|
|
9021
|
+
out.add(to);
|
|
9022
|
+
queue.push(to);
|
|
9023
|
+
}
|
|
9024
|
+
}
|
|
9025
|
+
}
|
|
9026
|
+
republished.set(path27, out);
|
|
9027
|
+
return out;
|
|
9028
|
+
};
|
|
9029
|
+
const NO_ORIGIN = /* @__PURE__ */ new Set();
|
|
9030
|
+
const landingFrom = (scope, specifier) => {
|
|
9031
|
+
const to = resolveImport(scope, specifier, paths);
|
|
9032
|
+
return to ? republishedFrom(to) : NO_ORIGIN;
|
|
9033
|
+
};
|
|
9034
|
+
const scopeOf = (site, from) => {
|
|
9035
|
+
const path27 = pathKey(site.from ?? from);
|
|
9036
|
+
const f = facts.get(path27);
|
|
9037
|
+
return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path27, facts: f } : void 0;
|
|
9038
|
+
};
|
|
9039
|
+
const originOf = (site, from) => {
|
|
9040
|
+
const resolved = scopeOf(site, from);
|
|
9041
|
+
if (!resolved) return NO_ORIGIN;
|
|
9042
|
+
const { path: scope, facts: f } = resolved;
|
|
9043
|
+
const landing = (specifier) => landingFrom(scope, specifier);
|
|
9044
|
+
if (!site.member) {
|
|
9045
|
+
const binding = importBindingOf(f, site.name);
|
|
9046
|
+
if (binding) return landing(binding.from);
|
|
9047
|
+
return declarationsAt(scope).has(site.name) ? republishedFrom(scope) : NO_ORIGIN;
|
|
9048
|
+
}
|
|
9049
|
+
if (!site.via) return NO_ORIGIN;
|
|
9050
|
+
const receiver = importBindingOf(f, site.via);
|
|
9051
|
+
if (!receiver) return NO_ORIGIN;
|
|
9052
|
+
if (receiver.namespace) return landing(receiver.from);
|
|
9053
|
+
const through = landing(receiver.from);
|
|
9054
|
+
for (const candidate of through) {
|
|
9055
|
+
if (declarationsAt(candidate).has(site.name)) return through;
|
|
9056
|
+
}
|
|
9057
|
+
return NO_ORIGIN;
|
|
9058
|
+
};
|
|
9059
|
+
const possibleOriginsOf = (site, from) => {
|
|
9060
|
+
const proven = originOf(site, from);
|
|
9061
|
+
if (!site.field && !site.constructed) return proven;
|
|
9062
|
+
const resolved = scopeOf(site, from);
|
|
9063
|
+
if (!resolved) return proven;
|
|
9064
|
+
const { path: scope, facts: f } = resolved;
|
|
9065
|
+
const out = new Set(proven);
|
|
9066
|
+
const widenThrough = (name, specifier) => {
|
|
9067
|
+
const landings = specifier !== void 0 ? landingFrom(scope, specifier) : declarationsAt(scope).has(name) ? republishedFrom(scope) : NO_ORIGIN;
|
|
9068
|
+
for (const candidate of landings) out.add(candidate);
|
|
9069
|
+
};
|
|
9070
|
+
if (site.field) {
|
|
9071
|
+
for (const typeName of fieldTypesOf(f, site.field)) widenThrough(typeName, typeBindingOf(f, typeName));
|
|
9072
|
+
}
|
|
9073
|
+
if (site.constructed) widenThrough(site.constructed, importBindingOf(f, site.constructed)?.from);
|
|
9074
|
+
return out;
|
|
9075
|
+
};
|
|
9076
|
+
const declarationsAt = (path27) => namesAt(declarations, pathKey(path27), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
|
|
8866
9077
|
return {
|
|
8867
9078
|
paths,
|
|
8868
9079
|
exactPaths,
|
|
8869
9080
|
factsAt: (path27) => facts.get(pathKey(path27)),
|
|
8870
|
-
declarationsAt
|
|
9081
|
+
declarationsAt,
|
|
8871
9082
|
anchorsAt: (path27) => namesAt(anchors, pathKey(path27), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
|
|
8872
|
-
findingAnchorsAt: (path27) => namesAt(findingAnchors, pathKey(path27), (f) => new Set(f.anchoredNames))
|
|
9083
|
+
findingAnchorsAt: (path27) => namesAt(findingAnchors, pathKey(path27), (f) => new Set(f.anchoredNames)),
|
|
9084
|
+
originOf,
|
|
9085
|
+
possibleOriginsOf
|
|
8873
9086
|
};
|
|
8874
9087
|
}
|
|
8875
9088
|
function buildRealizationIndex(ctx) {
|
|
@@ -9483,10 +9696,10 @@ var init_lint_allows = __esm({
|
|
|
9483
9696
|
"use strict";
|
|
9484
9697
|
lintAllowsRule = {
|
|
9485
9698
|
name: "lint-allows",
|
|
9486
|
-
description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. Allows silence warnings only; errors always surface.",
|
|
9699
|
+
description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings only; errors always surface.",
|
|
9487
9700
|
codes: [
|
|
9488
9701
|
{ code: "UNKNOWN_LINT_ALLOW_CODE", defaultSeverity: "warning", summary: "lint.allow names an issue code no registered rule emits" },
|
|
9489
|
-
{ code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry matched no finding this run \u2014
|
|
9702
|
+
{ code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry matched no finding this run \u2014 the code never fired, or it fired at sites this allow does not name" }
|
|
9490
9703
|
],
|
|
9491
9704
|
check(ctx) {
|
|
9492
9705
|
for (const a of ctx.lintAllows) {
|
|
@@ -9499,14 +9712,24 @@ var init_lint_allows = __esm({
|
|
|
9499
9712
|
);
|
|
9500
9713
|
continue;
|
|
9501
9714
|
}
|
|
9502
|
-
if (
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9715
|
+
if (a.used) continue;
|
|
9716
|
+
const reported = ctx.sitesReported(a.specId, a.code);
|
|
9717
|
+
const at = a.at ? ` at "${a.at}"` : "";
|
|
9718
|
+
let why;
|
|
9719
|
+
if (a.at && reported.unsited && reported.sites.length === 0) {
|
|
9720
|
+
why = `findings of "${a.code}" on this spec name no site at all, so this allow must not name one \u2014 drop the \`at\``;
|
|
9721
|
+
} else if (reported.sites.length > 0) {
|
|
9722
|
+
const sites = reported.sites.map((s) => `"${s}"`).join("; ");
|
|
9723
|
+
why = a.at ? `that code fired at ${sites} instead \u2014 retarget the allow, or remove it` : `that code fired at ${sites}, and a sited finding is covered only by an allow naming its site \u2014 give this allow an \`at\` (one per site, each with its own reason), or remove it`;
|
|
9724
|
+
} else {
|
|
9725
|
+
why = "no such finding fired this run \u2014 remove the stale allow";
|
|
9509
9726
|
}
|
|
9727
|
+
ctx.addIssue(
|
|
9728
|
+
"warning",
|
|
9729
|
+
"UNUSED_LINT_ALLOW",
|
|
9730
|
+
`Spec "${a.specId}" allows "${a.code}"${at} (reason: ${a.reason}), but ${why}.`,
|
|
9731
|
+
a.specId
|
|
9732
|
+
);
|
|
9510
9733
|
}
|
|
9511
9734
|
}
|
|
9512
9735
|
};
|
|
@@ -13098,6 +13321,21 @@ var init_source_file_linkage = __esm({
|
|
|
13098
13321
|
});
|
|
13099
13322
|
|
|
13100
13323
|
// src/core/rules/conformance/method-realization.ts
|
|
13324
|
+
function bodyReachable(code, file, symbol) {
|
|
13325
|
+
const facts = code.factsAt(file);
|
|
13326
|
+
if (!facts) return true;
|
|
13327
|
+
const names = /* @__PURE__ */ new Set([symbol]);
|
|
13328
|
+
const binding = importBindingOf(facts, symbol);
|
|
13329
|
+
if (binding?.imported) names.add(binding.imported);
|
|
13330
|
+
const origins = code.originOf({ name: symbol, member: false }, file);
|
|
13331
|
+
if (origins.size === 0) return true;
|
|
13332
|
+
for (const origin of origins) {
|
|
13333
|
+
const at = code.factsAt(origin);
|
|
13334
|
+
if (!at || at.status !== "analyzed" || at.analysisGrade !== "exact") return true;
|
|
13335
|
+
for (const name of names) if (hasFunctionBody(at, name)) return true;
|
|
13336
|
+
}
|
|
13337
|
+
return false;
|
|
13338
|
+
}
|
|
13101
13339
|
var methodRealizationRule;
|
|
13102
13340
|
var init_method_realization = __esm({
|
|
13103
13341
|
"src/core/rules/conformance/method-realization.ts"() {
|
|
@@ -13105,9 +13343,10 @@ var init_method_realization = __esm({
|
|
|
13105
13343
|
init_models();
|
|
13106
13344
|
methodRealizationRule = {
|
|
13107
13345
|
name: "method-realization",
|
|
13108
|
-
description: "Code\u2194spec Level 1: every L3 contract method must be realized in its own source file (the method's sourcePath, else the implementation's) at its conformance tier \u2014 declared | anchored | off; Portals default to anchored, everything else to declared, and a per-method `symbol` maps an intent-language name onto the code name. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible. Methods whose file escapes the root, is missing or could not be analyzed are left to source-file-linkage, and implementations under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
13346
|
+
description: "Code\u2194spec Level 1: every L3 contract method must be realized in its own source file (the method's sourcePath, else the implementation's) at its conformance tier \u2014 declared | anchored | off; Portals default to anchored, everything else to declared, and a per-method `symbol` maps an intent-language name onto the code name. A method realized by a DECLARATION owes a function BODY as well: at exact grade one must be reachable under the symbol, here or through the imports and republications this file forwards it by, so a signature, an ambient or interface declaration or a plain value binding stops reading as an implementation (METHOD_BODY_NOT_FOUND). Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible. Methods whose file escapes the root, is missing or could not be analyzed are left to source-file-linkage, and implementations under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
13109
13347
|
codes: [
|
|
13110
|
-
{ code: "UNREALIZED_METHOD", defaultSeverity: "warning", summary: "An L3 contract method has no anchor in its own source file (the method's sourcePath, else the implementation's) at the required conformance tier" }
|
|
13348
|
+
{ code: "UNREALIZED_METHOD", defaultSeverity: "warning", summary: "An L3 contract method has no anchor in its own source file (the method's sourcePath, else the implementation's) at the required conformance tier" },
|
|
13349
|
+
{ code: "METHOD_BODY_NOT_FOUND", defaultSeverity: "warning", summary: "The method symbol IS declared in its own source file, but the file holds no function-like body under it \u2014 a signature, an ambient or interface declaration, a value binding, an imported or re-exported name", carryable: true }
|
|
13111
13350
|
],
|
|
13112
13351
|
check(ctx) {
|
|
13113
13352
|
const code = ctx.codeIndex();
|
|
@@ -13131,7 +13370,22 @@ var init_method_realization = __esm({
|
|
|
13131
13370
|
const inDeclared = code.declarationsAt(file).has(symbol);
|
|
13132
13371
|
const inAnchored = inDeclared || code.anchorsAt(file).has(symbol);
|
|
13133
13372
|
const realized = tier === "declared" ? inDeclared : inAnchored;
|
|
13134
|
-
if (realized)
|
|
13373
|
+
if (realized) {
|
|
13374
|
+
if (!inDeclared || facts.analysisGrade !== "exact" || bodyReachable(code, file, symbol)) continue;
|
|
13375
|
+
const bodyLabel = methodImpl?.symbol ? `"${method.name}" (symbol "${symbol}")` : `"${method.name}"`;
|
|
13376
|
+
ctx.addIssue(
|
|
13377
|
+
"warning",
|
|
13378
|
+
"METHOD_BODY_NOT_FOUND",
|
|
13379
|
+
`Method ${bodyLabel} of contract "${impl.contract}" is declared in "${file}" but has no function body there \u2014 a signature, an ambient or interface declaration, a value binding, or a name this file only imports or re-exports. Every deeper check reads a body (the realized calls of Level 3, the measured complexity of the detail dial), so this method is judged on its name alone. Point the sourcePath at the file that implements it, map the code name via a per-method SYMBOL, or dial this method to "off".`,
|
|
13380
|
+
impl.id,
|
|
13381
|
+
draft,
|
|
13382
|
+
void 0,
|
|
13383
|
+
// One method, one indivisible fact: nothing here aggregates, so
|
|
13384
|
+
// the site alone is the whole identity.
|
|
13385
|
+
{ at: method.name }
|
|
13386
|
+
);
|
|
13387
|
+
continue;
|
|
13388
|
+
}
|
|
13135
13389
|
const label = methodImpl?.symbol ? `"${method.name}" (symbol "${symbol}")` : `"${method.name}"`;
|
|
13136
13390
|
const weakHint = tier === "declared" && inAnchored ? " Only a weak string/word anchor exists \u2014 declare the symbol, map it via a per-method `symbol`, or dial this method to `anchored`." : "";
|
|
13137
13391
|
ctx.addIssue(
|
|
@@ -13198,24 +13452,54 @@ var init_finding_realization = __esm({
|
|
|
13198
13452
|
});
|
|
13199
13453
|
|
|
13200
13454
|
// src/core/rules/conformance/call-conformance.ts
|
|
13201
|
-
function
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
function closedCallees(facts, fn) {
|
|
13205
|
-
const direct = ownEntry(facts.functionCalls, fn);
|
|
13455
|
+
function closedCallSites(code, file, fn, stopAt) {
|
|
13456
|
+
const facts = code.factsAt(file);
|
|
13457
|
+
const direct = facts && callSitesOf(facts, fn);
|
|
13206
13458
|
if (!direct) return void 0;
|
|
13207
|
-
const
|
|
13208
|
-
const
|
|
13459
|
+
const here = pathKey(file);
|
|
13460
|
+
const out = [];
|
|
13461
|
+
const descended = /* @__PURE__ */ new Set([`${here}|${fn}`]);
|
|
13462
|
+
const queue = direct.map((s) => ({ ...s, from: s.from ?? here }));
|
|
13209
13463
|
while (queue.length) {
|
|
13210
|
-
const
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13464
|
+
const site = queue.pop();
|
|
13465
|
+
out.push(site);
|
|
13466
|
+
if (stopAt?.has(site.name)) continue;
|
|
13467
|
+
const key = `${site.from}|${site.name}`;
|
|
13468
|
+
if (descended.has(key)) continue;
|
|
13469
|
+
descended.add(key);
|
|
13470
|
+
const scope = code.factsAt(site.from);
|
|
13471
|
+
const next = scope && callSitesOf(scope, site.name);
|
|
13472
|
+
if (!next) continue;
|
|
13473
|
+
queue.push(...next.map((s) => ({ ...s, from: s.from ?? site.from })));
|
|
13217
13474
|
}
|
|
13218
|
-
return
|
|
13475
|
+
return out;
|
|
13476
|
+
}
|
|
13477
|
+
function resolveCallTarget(ctx, componentId, methodName) {
|
|
13478
|
+
const accepted = /* @__PURE__ */ new Set([methodName]);
|
|
13479
|
+
const files = /* @__PURE__ */ new Set();
|
|
13480
|
+
for (const intf of ctx.interfacesByComponent.get(componentId) ?? []) {
|
|
13481
|
+
for (const impl of ctx.implementationsByContract.get(intf.id) ?? []) {
|
|
13482
|
+
const method = impl.methods.find((m) => m.name === methodName);
|
|
13483
|
+
if (!method && !intf.methods.some((m) => m.name === methodName)) continue;
|
|
13484
|
+
if (method?.symbol) accepted.add(method.symbol);
|
|
13485
|
+
const file = methodSourceFile(method ?? {}, impl.sourcePath);
|
|
13486
|
+
if (file) files.add(pathKey(file));
|
|
13487
|
+
}
|
|
13488
|
+
}
|
|
13489
|
+
return { accepted, files };
|
|
13490
|
+
}
|
|
13491
|
+
function describeSite(site) {
|
|
13492
|
+
if (!site.member) return `${site.name}(\u2026)`;
|
|
13493
|
+
if (site.via) return `${site.via}.${site.name}(\u2026)`;
|
|
13494
|
+
if (site.field) return `this.${site.field}.${site.name}(\u2026)`;
|
|
13495
|
+
if (site.constructed) return `new ${site.constructed}(\u2026).${site.name}(\u2026)`;
|
|
13496
|
+
return `<receiver>.${site.name}(\u2026)`;
|
|
13497
|
+
}
|
|
13498
|
+
function describeMiss(m) {
|
|
13499
|
+
const head = `step ${m.step} \u2192 ${m.target} (looked for ${m.accepted.map((a) => `"${a}"`).join(" / ")}`;
|
|
13500
|
+
if (m.miss.kind === "elsewhere") return `${head}, called but resolved to ${m.miss.landed.map((p) => `"${p}"`).join(", ")})`;
|
|
13501
|
+
if (m.miss.kind === "unresolved") return `${head}, written as ${m.miss.shapes.map((s) => `\`${s}\``).join(" / ")})`;
|
|
13502
|
+
return `${head})`;
|
|
13219
13503
|
}
|
|
13220
13504
|
var callConformanceRule;
|
|
13221
13505
|
var init_call_conformance = __esm({
|
|
@@ -13224,13 +13508,24 @@ var init_call_conformance = __esm({
|
|
|
13224
13508
|
init_models();
|
|
13225
13509
|
callConformanceRule = {
|
|
13226
13510
|
name: "call-conformance",
|
|
13227
|
-
description: "Code\u2194spec Level 3
|
|
13511
|
+
description: "Code\u2194spec Level 3: the narrative `call` step \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. Forward: every `call` step must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from. A matching call whose origin a pure model cannot resolve is reported apart as CALL_ORIGIN_UNRESOLVED, which NAMES the shape it could not follow and asks for nothing \u2014 a coverage hole in the reader, reported for the reason CONFORMANCE_DEGRADED is: a silently degraded gate is worse than a degraded one. A finding names a landing only from the PROVEN tier so that widening what a call reached can accept a step but never accuse one, and a target that names no file of its own falls back to name membership. Converse: a call that resolves to a modelled method of ANOTHER component in the SAME file crosses a component boundary while looking local, so the narrative must declare it (UNDECLARED_COLOCATED_CALL) \u2014 judged on the proven tier alone, and a same-file private helper is no modelled method and is never reported. Order, arguments and conditions stay unverified, dispatch steps (runtime-table routed) are skipped, the conformance dial (off) skips, and weaker analysis grades never guess.",
|
|
13228
13512
|
codes: [
|
|
13229
|
-
{ code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step
|
|
13513
|
+
{ code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step realized by no call that resolves to the target method's own source file \u2014 the call is absent, or it lands in another module", carryable: true },
|
|
13514
|
+
{ code: "CALL_ORIGIN_UNRESOLVED", defaultSeverity: "warning", summary: "Narrative call step whose target name IS called, but only from call sites written in a shape this analysis cannot resolve to a file \u2014 the step was not checked, and is neither proven realized nor accused", carryable: true },
|
|
13515
|
+
{ code: "UNDECLARED_COLOCATED_CALL", defaultSeverity: "warning", summary: "The realized function calls a modelled method of another component living in the same source file, and no narrative step declares that call", carryable: true }
|
|
13230
13516
|
],
|
|
13231
13517
|
check(ctx) {
|
|
13232
13518
|
const code = ctx.codeIndex();
|
|
13233
|
-
|
|
13519
|
+
const methods = ctx.implementationMethods();
|
|
13520
|
+
const modelledAt = /* @__PURE__ */ new Map();
|
|
13521
|
+
for (const entry of methods) {
|
|
13522
|
+
if (!entry.sourceFile) continue;
|
|
13523
|
+
const file = pathKey(entry.sourceFile);
|
|
13524
|
+
const byName = modelledAt.get(file) ?? /* @__PURE__ */ new Map();
|
|
13525
|
+
byName.set(entry.method.symbol ?? entry.method.name, { component: entry.component.id, method: entry.method.name });
|
|
13526
|
+
modelledAt.set(file, byName);
|
|
13527
|
+
}
|
|
13528
|
+
for (const entry of methods) {
|
|
13234
13529
|
const { implementation: impl, method: implMethod, component, sourceFile: file } = entry;
|
|
13235
13530
|
const tier = implMethod.conformance ?? impl.conformance ?? defaultConformanceTier(component);
|
|
13236
13531
|
if (tier === "off") continue;
|
|
@@ -13238,37 +13533,91 @@ var init_call_conformance = __esm({
|
|
|
13238
13533
|
if (!file) continue;
|
|
13239
13534
|
const facts = code.factsAt(file);
|
|
13240
13535
|
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
|
|
13536
|
+
const here = pathKey(file);
|
|
13241
13537
|
const fnSymbol = implMethod.symbol ?? implMethod.name;
|
|
13242
|
-
const
|
|
13243
|
-
|
|
13244
|
-
|
|
13538
|
+
const colocated = modelledAt.get(here) ?? /* @__PURE__ */ new Map();
|
|
13539
|
+
const sites = closedCallSites(code, file, fnSymbol);
|
|
13540
|
+
if (!sites) continue;
|
|
13541
|
+
const missed = [];
|
|
13245
13542
|
for (const step of implMethod.narrative) {
|
|
13246
13543
|
if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
|
|
13247
13544
|
if (!ctx.componentMap.has(step.targetComponent)) continue;
|
|
13248
|
-
const
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13545
|
+
const target = resolveCallTarget(ctx, step.targetComponent, step.targetMethod);
|
|
13546
|
+
if (target.accepted.has(fnSymbol)) continue;
|
|
13547
|
+
const matching = sites.filter((s) => target.accepted.has(s.name));
|
|
13548
|
+
const ref = `${step.targetComponent}.${step.targetMethod}`;
|
|
13549
|
+
if (target.files.size === 0) {
|
|
13550
|
+
if (matching.length === 0) {
|
|
13551
|
+
missed.push({ step: step.stepNumber, target: ref, accepted: [...target.accepted], miss: { kind: "absent" } });
|
|
13253
13552
|
}
|
|
13553
|
+
continue;
|
|
13254
13554
|
}
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
13261
|
-
|
|
13555
|
+
const landed = /* @__PURE__ */ new Set();
|
|
13556
|
+
let realized = false;
|
|
13557
|
+
for (const site of matching) {
|
|
13558
|
+
for (const origin of code.originOf(site, file)) landed.add(origin);
|
|
13559
|
+
for (const origin of code.possibleOriginsOf(site, file)) {
|
|
13560
|
+
if (target.files.has(origin)) realized = true;
|
|
13561
|
+
}
|
|
13562
|
+
}
|
|
13563
|
+
if (realized) continue;
|
|
13564
|
+
const miss = matching.length === 0 ? { kind: "absent" } : landed.size === 0 ? { kind: "unresolved", shapes: [...new Set(matching.map(describeSite))].sort() } : { kind: "elsewhere", landed: [...landed].sort() };
|
|
13565
|
+
missed.push({ step: step.stepNumber, target: ref, accepted: [...target.accepted], miss });
|
|
13566
|
+
}
|
|
13567
|
+
const unrealized = missed.filter((m) => m.miss.kind !== "unresolved");
|
|
13568
|
+
const unresolved = missed.filter((m) => m.miss.kind === "unresolved");
|
|
13569
|
+
if (unrealized.length) {
|
|
13570
|
+
ctx.addIssue(
|
|
13571
|
+
"warning",
|
|
13572
|
+
"CALL_STEP_UNREALIZED",
|
|
13573
|
+
`Method "${implMethod.name}" in implementation "${impl.id}": ${unrealized.length} narrative call step(s) are realized by no call of the function "${fnSymbol}" in "${file}" that resolves to the target's own source file \u2014 ${unrealized.map(describeMiss).join("; ")}. Callees are closed over the named helpers the function calls, and each call site is resolved through this file's import bindings (order, arguments and conditions are not checked). Realize the calls, fix the narrative, or map code names via per-method symbols on the targets.`,
|
|
13574
|
+
impl.id,
|
|
13575
|
+
entry.draftContext,
|
|
13576
|
+
void 0,
|
|
13577
|
+
// A step, named by its number and its target: what the register
|
|
13578
|
+
// carries is ONE unresolved step, never "this method's call steps".
|
|
13579
|
+
{ at: implMethod.name, covers: unrealized.map((m) => `${m.step}:${m.target}`) }
|
|
13580
|
+
);
|
|
13581
|
+
}
|
|
13582
|
+
if (unresolved.length) {
|
|
13583
|
+
ctx.addIssue(
|
|
13584
|
+
"warning",
|
|
13585
|
+
"CALL_ORIGIN_UNRESOLVED",
|
|
13586
|
+
`Method "${implMethod.name}" in implementation "${impl.id}": ${unresolved.length} narrative call step(s) ARE called by name inside the function "${fnSymbol}" in "${file}", but every site calling them is written in a shape this analysis cannot resolve to a file \u2014 ${unresolved.map(describeMiss).join("; ")}. What resolves is a name bound to a module of THIS project: a bare or namespaced call through such an import binding, a \`this.<field>\` receiver whose declared type names one, a \`new Class(\u2026)\` receiver whose class does. What does not: an import from a PACKAGE specifier, a receiver holding a value the module assembled, a receiver this model records no name for. This reports what was not checked, not what is wrong \u2014 the step is neither proven realized nor accused of being missing, and no working call is asked to be rewritten to suit the reader.`,
|
|
13587
|
+
impl.id,
|
|
13588
|
+
entry.draftContext,
|
|
13589
|
+
void 0,
|
|
13590
|
+
{ at: implMethod.name, covers: unresolved.map((m) => `${m.step}:${m.target}`) }
|
|
13591
|
+
);
|
|
13592
|
+
}
|
|
13593
|
+
const declared = /* @__PURE__ */ new Set();
|
|
13594
|
+
for (const step of implMethod.narrative) {
|
|
13595
|
+
if (step.targetComponent && step.targetMethod) declared.add(`${step.targetComponent}.${step.targetMethod}`);
|
|
13596
|
+
}
|
|
13597
|
+
const crossings = /* @__PURE__ */ new Map();
|
|
13598
|
+
for (const site of closedCallSites(code, file, fnSymbol, new Set(colocated.keys())) ?? []) {
|
|
13599
|
+
const owner = colocated.get(site.name);
|
|
13600
|
+
if (!owner || owner.component === component.id) continue;
|
|
13601
|
+
const ref = `${owner.component}.${owner.method}`;
|
|
13602
|
+
if (declared.has(ref) || crossings.has(ref)) continue;
|
|
13603
|
+
if (!code.originOf(site, file).has(here)) continue;
|
|
13604
|
+
crossings.set(ref, site.name);
|
|
13605
|
+
}
|
|
13606
|
+
if (crossings.size) {
|
|
13607
|
+
const detail = [...crossings].map(([ref, name]) => `"${name}" (${ref})`).join("; ");
|
|
13608
|
+
ctx.addIssue(
|
|
13609
|
+
"warning",
|
|
13610
|
+
"UNDECLARED_COLOCATED_CALL",
|
|
13611
|
+
`Method "${implMethod.name}" in implementation "${impl.id}": the function "${fnSymbol}" in "${file}" calls ${crossings.size} modelled method(s) of OTHER components living in that same file, and no narrative step declares the call \u2014 ${detail}. Sharing a file does not make the hop internal: it crosses a component boundary nothing imports, so no file-level check can see it. Narrate the call, or move the code so the boundary is real.`,
|
|
13612
|
+
impl.id,
|
|
13613
|
+
entry.draftContext,
|
|
13614
|
+
void 0,
|
|
13615
|
+
// The crossings themselves. Keyed by method alone, a 24th crossing
|
|
13616
|
+
// would ride into a register entry written for 23 - so each one is
|
|
13617
|
+
// named, and one nobody carried fires on the day it appears.
|
|
13618
|
+
{ at: implMethod.name, covers: [...crossings.keys()] }
|
|
13619
|
+
);
|
|
13262
13620
|
}
|
|
13263
|
-
if (missing.length === 0) continue;
|
|
13264
|
-
const detail = missing.map((m) => `step ${m.step} \u2192 ${m.target} (looked for ${m.accepted.map((a) => `"${a}"`).join(" / ")})`).join("; ");
|
|
13265
|
-
ctx.addIssue(
|
|
13266
|
-
"warning",
|
|
13267
|
-
"CALL_STEP_UNREALIZED",
|
|
13268
|
-
`Method "${implMethod.name}" in implementation "${impl.id}": ${missing.length} narrative call step(s) are not realized as calls of the function "${fnSymbol}" in "${file}" \u2014 ${detail}. Callees are matched by name, closed over same-file helpers (exact grade, set membership \u2014 order and arguments are not checked). Realize the calls, fix the narrative, or map code names via per-method symbols on the targets.`,
|
|
13269
|
-
impl.id,
|
|
13270
|
-
entry.draftContext
|
|
13271
|
-
);
|
|
13272
13621
|
}
|
|
13273
13622
|
}
|
|
13274
13623
|
};
|
|
@@ -13399,7 +13748,13 @@ var init_dependency_conformance = __esm({
|
|
|
13399
13748
|
"UNDECLARED_DEPENDENCY",
|
|
13400
13749
|
`"${fromPath}" (realizing ${fromComponents.map((c) => c.id).join(", ")}) imports "${toPath}" (realizing ${toComponents.map((c) => c.id).join(", ")}) but no declared dependsOn/owns edge justifies it \u2014 declare the collaboration on the component that actually uses it, or route the cross-subsystem hop through the target's published surface.`,
|
|
13401
13750
|
realization.implementationsAt(fromPath)[0]?.id,
|
|
13402
|
-
draftAt(fromPath) || draftAt(toPath)
|
|
13751
|
+
draftAt(fromPath) || draftAt(toPath),
|
|
13752
|
+
void 0,
|
|
13753
|
+
// One import edge, one indivisible fact — but one implementation
|
|
13754
|
+
// maps many files and many edges, so the EDGE is the site, not the
|
|
13755
|
+
// spec. Without it a single allow on the spec covers every crossing
|
|
13756
|
+
// that file ever grows.
|
|
13757
|
+
{ at: `${fromPath} -> ${toPath}` }
|
|
13403
13758
|
);
|
|
13404
13759
|
}
|
|
13405
13760
|
}
|
|
@@ -13426,7 +13781,12 @@ var init_dependency_conformance = __esm({
|
|
|
13426
13781
|
"UNREALIZED_DEPENDENCY",
|
|
13427
13782
|
`Component "${component.id}" declares ${relation} "${targetId}", but no runtime import connects their source files (${fromFiles.join(", ")} \u219B ${crossSubsystem ? `subsystem ${target.subsystem}` : filesOf(targetId).join(", ")}) \u2014 either the collaboration is wired indirectly (DI) or the declared edge is stale.`,
|
|
13428
13783
|
impls[0]?.id ?? component.id,
|
|
13429
|
-
draft
|
|
13784
|
+
draft,
|
|
13785
|
+
void 0,
|
|
13786
|
+
// One declared edge, one indivisible fact. A component declares many,
|
|
13787
|
+
// and they all anchor on its one implementation spec, so the EDGE is
|
|
13788
|
+
// the site.
|
|
13789
|
+
{ at: `${component.id} -> ${targetId}` }
|
|
13430
13790
|
);
|
|
13431
13791
|
}
|
|
13432
13792
|
}
|
|
@@ -13778,6 +14138,69 @@ var init_unclaimed_source = __esm({
|
|
|
13778
14138
|
}
|
|
13779
14139
|
});
|
|
13780
14140
|
|
|
14141
|
+
// src/core/rules/conformance/carried-debt.ts
|
|
14142
|
+
var carriedDebtRule;
|
|
14143
|
+
var init_carried_debt = __esm({
|
|
14144
|
+
"src/core/rules/conformance/carried-debt.ts"() {
|
|
14145
|
+
"use strict";
|
|
14146
|
+
carriedDebtRule = {
|
|
14147
|
+
name: "carried-debt",
|
|
14148
|
+
description: "The conformance debt register (`rules.conformance.carried`) holds the code\u2194spec findings this tree carries as declared, classified debt \u2014 `unclaimed`'s one-way shape, for findings about code a spec does name. It is not a second lint.allow: an allow says a finding is wrong here by design, an entry here says the finding is right and unpaid, and each entry states which of the three kinds it is (drift: spec and code disagree; undecided: the fix waits on a modelling decision nobody has taken; unreadable: the analysis cannot follow the shape the code is written in) and why. Only a code a rule declares CARRYABLE may appear, and naming any other is an error, so the register can never widen into general-purpose suppression. An entry carries a finding only when it lists EVERY unit that finding reports, so an aggregating finding cannot grow behind it; an entry that matched nothing, that duplicates another, that names a spec the tree does not hold, or that lists a unit no longer reported is STALE_CARRIED_FINDING, so the register only shrinks. The audit is silent in a run that could not have fired the findings at all: one that built no code model, the specs outside a scoped run, and a code the project switched off. Nothing can check a reason for TRUTH \u2014 a stale entry is one that stopped applying, never one that still applies for a reason that has become false \u2014 so a reason group may declare its own classification provisional (`revisit`: what would settle it), and every run counts those findings beside the kind totals.",
|
|
14149
|
+
codes: [
|
|
14150
|
+
{ code: "UNCARRYABLE_FINDING", defaultSeverity: "error", summary: "The conformance debt register names an issue code no rule emits, or one no rule declares carryable \u2014 the register holds measured code\u2194spec debt, never a design rule somebody wants quiet" },
|
|
14151
|
+
{ code: "STALE_CARRIED_FINDING", defaultSeverity: "warning", summary: "An entry of the conformance debt register carries a finding that no longer fires, or lists a unit that finding no longer reports \u2014 delete it, the register only shrinks" }
|
|
14152
|
+
],
|
|
14153
|
+
check(ctx) {
|
|
14154
|
+
const carried = ctx.carriedFindings;
|
|
14155
|
+
if (carried.length === 0) return;
|
|
14156
|
+
for (const entry of carried) {
|
|
14157
|
+
if (ctx.carryableIssueCodes.has(entry.code)) continue;
|
|
14158
|
+
const why = ctx.knownIssueCodes.has(entry.code) ? "that code is not CARRYABLE \u2014 the register holds measured code\u2194spec debt, and a doctrine, soundness or configuration finding says the design is illegal rather than unfinished" : "no registered rule emits that code";
|
|
14159
|
+
ctx.addIssue(
|
|
14160
|
+
"error",
|
|
14161
|
+
"UNCARRYABLE_FINDING",
|
|
14162
|
+
`The conformance debt register carries "${entry.code}" on "${entry.spec}" (at "${entry.at}"), but ${why}. Remove the entry; if the code belongs in the register, declare it carryable on the rule that emits it.`
|
|
14163
|
+
);
|
|
14164
|
+
}
|
|
14165
|
+
if (ctx.codeModel.files.length === 0) return;
|
|
14166
|
+
const known = new Set(ctx.specIds().map((s) => s.id));
|
|
14167
|
+
const addressed = /* @__PURE__ */ new Set();
|
|
14168
|
+
for (const entry of carried) {
|
|
14169
|
+
if (!ctx.carryableIssueCodes.has(entry.code)) continue;
|
|
14170
|
+
if (ctx.rules?.sddRuleSeverity?.[entry.code] === "off") continue;
|
|
14171
|
+
if (!ctx.isSpecInScope(entry.spec)) continue;
|
|
14172
|
+
const key = `${entry.spec}|${entry.code}|${entry.at}`;
|
|
14173
|
+
if (addressed.has(key)) {
|
|
14174
|
+
ctx.addIssue(
|
|
14175
|
+
"warning",
|
|
14176
|
+
"STALE_CARRIED_FINDING",
|
|
14177
|
+
`The conformance debt register carries "${entry.code}" on "${entry.spec}" (at "${entry.at}") more than once. Only the first entry can ever match, so the rest carry nothing \u2014 delete them and merge their units into the one entry.`
|
|
14178
|
+
);
|
|
14179
|
+
continue;
|
|
14180
|
+
}
|
|
14181
|
+
addressed.add(key);
|
|
14182
|
+
if (!entry.fired) {
|
|
14183
|
+
const why = known.has(entry.spec) ? "no such finding fired this run \u2014 the debt is paid, or the spec no longer reaches that site" : `the tree holds no spec "${entry.spec}"`;
|
|
14184
|
+
ctx.addIssue(
|
|
14185
|
+
"warning",
|
|
14186
|
+
"STALE_CARRIED_FINDING",
|
|
14187
|
+
`The conformance debt register carries "${entry.code}" on "${entry.spec}" (at "${entry.at}" \u2014 ${entry.kind}: ${entry.why}), but ${why}. Delete the entry; the register only shrinks.`
|
|
14188
|
+
);
|
|
14189
|
+
continue;
|
|
14190
|
+
}
|
|
14191
|
+
const paid = entry.covers.filter((unit) => !entry.seen.has(unit));
|
|
14192
|
+
if (paid.length === 0) continue;
|
|
14193
|
+
ctx.addIssue(
|
|
14194
|
+
"warning",
|
|
14195
|
+
"STALE_CARRIED_FINDING",
|
|
14196
|
+
`The conformance debt register carries "${entry.code}" on "${entry.spec}" (at "${entry.at}") together with ${paid.length} unit(s) the finding no longer reports \u2014 ${paid.map((u) => `"${u}"`).join("; ")}. That much of the debt is paid: delete those lines from the entry's \`covers\`; the register only shrinks.`
|
|
14197
|
+
);
|
|
14198
|
+
}
|
|
14199
|
+
}
|
|
14200
|
+
};
|
|
14201
|
+
}
|
|
14202
|
+
});
|
|
14203
|
+
|
|
13781
14204
|
// src/core/rules/heuristic/coupling-health.ts
|
|
13782
14205
|
var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
|
|
13783
14206
|
var init_coupling_health = __esm({
|
|
@@ -14932,8 +15355,8 @@ function registerPackRules(packRules) {
|
|
|
14932
15355
|
for (const rule of packRules) addRule(rule);
|
|
14933
15356
|
}
|
|
14934
15357
|
function ruleSequence() {
|
|
14935
|
-
const
|
|
14936
|
-
return ruleSet.
|
|
15358
|
+
const audits = [carriedDebtRule, lintAllowsRule].filter((r) => ruleSet.includes(r));
|
|
15359
|
+
return [...ruleSet.filter((r) => !audits.includes(r)), ...audits];
|
|
14937
15360
|
}
|
|
14938
15361
|
function specScopedRules() {
|
|
14939
15362
|
return ruleSequence().filter((r) => r.scope === "spec");
|
|
@@ -15023,6 +15446,7 @@ var init_repository = __esm({
|
|
|
15023
15446
|
init_integration_sim_coverage();
|
|
15024
15447
|
init_type_realization();
|
|
15025
15448
|
init_unclaimed_source();
|
|
15449
|
+
init_carried_debt();
|
|
15026
15450
|
init_coupling_health();
|
|
15027
15451
|
init_signature_language_builtins();
|
|
15028
15452
|
init_narrative_language_constructs();
|
|
@@ -15214,8 +15638,11 @@ var init_repository = __esm({
|
|
|
15214
15638
|
// elsewhere?) rather than spec content.
|
|
15215
15639
|
packResolutionRule,
|
|
15216
15640
|
reproducibilityRule,
|
|
15217
|
-
// MUST run last:
|
|
15218
|
-
//
|
|
15641
|
+
// MUST run last, in this order: each audits what the earlier rules did
|
|
15642
|
+
// with a declared exception. The debt register first (which carried
|
|
15643
|
+
// findings the conformance family actually matched), then the allows
|
|
15644
|
+
// (which suppressions any rule actually consumed).
|
|
15645
|
+
carriedDebtRule,
|
|
15219
15646
|
lintAllowsRule
|
|
15220
15647
|
];
|
|
15221
15648
|
ruleSet = [];
|
|
@@ -15338,9 +15765,15 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15338
15765
|
const imports = /* @__PURE__ */ new Set();
|
|
15339
15766
|
const reexports = /* @__PURE__ */ new Set();
|
|
15340
15767
|
const starExports = [];
|
|
15768
|
+
const namedReexports = [];
|
|
15341
15769
|
const complexity = /* @__PURE__ */ new Map();
|
|
15342
15770
|
const calls = /* @__PURE__ */ new Map();
|
|
15771
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
15772
|
+
const typeOnlyBindings = /* @__PURE__ */ new Map();
|
|
15773
|
+
const fieldTypes = /* @__PURE__ */ new Map();
|
|
15343
15774
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
15775
|
+
const receiverKey = (site) => site.via ?? (site.field ? `this.${site.field}` : site.constructed ? `new ${site.constructed}` : "");
|
|
15776
|
+
const siteKey = (site) => `${site.member ? "m" : "b"}:${receiverKey(site)}:${site.name}`;
|
|
15344
15777
|
const addBindingNames = (name) => {
|
|
15345
15778
|
if (ts.isIdentifier(name)) declared.add(name.text);
|
|
15346
15779
|
else if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
@@ -15354,6 +15787,17 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15354
15787
|
if (ts.isPrivateIdentifier(name)) return name.text;
|
|
15355
15788
|
return void 0;
|
|
15356
15789
|
};
|
|
15790
|
+
const typeReferenceName = (type) => type && ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName) ? type.typeName.text : void 0;
|
|
15791
|
+
const isParameterProperty = (node) => !!node.modifiers?.some((m) => m.kind === ts.SyntaxKind.PublicKeyword || m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword || m.kind === ts.SyntaxKind.ReadonlyKeyword);
|
|
15792
|
+
const recordFieldType = (name, type) => {
|
|
15793
|
+
const typeName = typeReferenceName(type);
|
|
15794
|
+
if (!typeName) return;
|
|
15795
|
+
const field = propertyNameText(name);
|
|
15796
|
+
if (!field) return;
|
|
15797
|
+
const named = fieldTypes.get(field) ?? /* @__PURE__ */ new Set();
|
|
15798
|
+
named.add(typeName);
|
|
15799
|
+
fieldTypes.set(field, named);
|
|
15800
|
+
};
|
|
15357
15801
|
const hasExportModifier = (node) => {
|
|
15358
15802
|
const mods = node.modifiers;
|
|
15359
15803
|
return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
@@ -15376,7 +15820,10 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15376
15820
|
};
|
|
15377
15821
|
const collectFunctionFacts = (fn) => {
|
|
15378
15822
|
let score = 1;
|
|
15379
|
-
const callees = /* @__PURE__ */ new
|
|
15823
|
+
const callees = /* @__PURE__ */ new Map();
|
|
15824
|
+
const addSite = (site) => {
|
|
15825
|
+
callees.set(siteKey(site), site);
|
|
15826
|
+
};
|
|
15380
15827
|
const count = (node) => {
|
|
15381
15828
|
if (namedFunctionName(node) !== void 0) return;
|
|
15382
15829
|
if (ts.isIfStatement(node) || ts.isConditionalExpression(node) || ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node) || ts.isCaseClause(node) || ts.isCatchClause(node)) {
|
|
@@ -15388,8 +15835,17 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15388
15835
|
}
|
|
15389
15836
|
} else if (ts.isCallExpression(node)) {
|
|
15390
15837
|
const callee = node.expression;
|
|
15391
|
-
if (ts.isIdentifier(callee))
|
|
15392
|
-
else if (ts.isPropertyAccessExpression(callee))
|
|
15838
|
+
if (ts.isIdentifier(callee)) addSite({ name: callee.text, member: false });
|
|
15839
|
+
else if (ts.isPropertyAccessExpression(callee)) {
|
|
15840
|
+
const receiver = callee.expression;
|
|
15841
|
+
const name = callee.name.text;
|
|
15842
|
+
if (ts.isIdentifier(receiver)) addSite({ name, member: true, via: receiver.text });
|
|
15843
|
+
else if (ts.isPropertyAccessExpression(receiver) && receiver.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
15844
|
+
addSite({ name, member: true, field: receiver.name.text });
|
|
15845
|
+
} else if (ts.isNewExpression(receiver) && ts.isIdentifier(receiver.expression)) {
|
|
15846
|
+
addSite({ name, member: true, constructed: receiver.expression.text });
|
|
15847
|
+
} else addSite({ name, member: true });
|
|
15848
|
+
}
|
|
15393
15849
|
}
|
|
15394
15850
|
ts.forEachChild(node, count);
|
|
15395
15851
|
};
|
|
@@ -15401,9 +15857,13 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15401
15857
|
if (fnName && node.body) {
|
|
15402
15858
|
const facts = collectFunctionFacts(node);
|
|
15403
15859
|
complexity.set(fnName, Math.max(complexity.get(fnName) ?? 0, facts.score));
|
|
15404
|
-
const
|
|
15405
|
-
for (const
|
|
15406
|
-
calls.set(fnName,
|
|
15860
|
+
const sites = calls.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
15861
|
+
for (const [key, site] of facts.callees) sites.set(key, site);
|
|
15862
|
+
calls.set(fnName, sites);
|
|
15863
|
+
}
|
|
15864
|
+
if (ts.isPropertyDeclaration(node)) recordFieldType(node.name, node.type);
|
|
15865
|
+
else if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent) && isParameterProperty(node)) {
|
|
15866
|
+
recordFieldType(node.name, node.type);
|
|
15407
15867
|
}
|
|
15408
15868
|
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
|
|
15409
15869
|
const name = node.name && ts.isIdentifier(node.name) ? node.name.text : void 0;
|
|
@@ -15430,11 +15890,24 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15430
15890
|
} else if (ts.isImportDeclaration(node)) {
|
|
15431
15891
|
const clause = node.importClause;
|
|
15432
15892
|
const typeOnly = clause?.isTypeOnly ?? false;
|
|
15433
|
-
|
|
15434
|
-
if (
|
|
15893
|
+
const spec = ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
|
|
15894
|
+
if (!typeOnly && spec) imports.add(spec);
|
|
15895
|
+
const bind = (name, namespace, imported, elementTypeOnly = false) => {
|
|
15896
|
+
declared.add(name);
|
|
15897
|
+
if (!spec) return;
|
|
15898
|
+
if (typeOnly || elementTypeOnly) {
|
|
15899
|
+
typeOnlyBindings.set(name, spec);
|
|
15900
|
+
return;
|
|
15901
|
+
}
|
|
15902
|
+
const fact = { from: spec };
|
|
15903
|
+
if (namespace) fact.namespace = true;
|
|
15904
|
+
if (imported && imported !== name) fact.imported = imported;
|
|
15905
|
+
importBindings.set(name, fact);
|
|
15906
|
+
};
|
|
15907
|
+
if (clause?.name) bind(clause.name.text, false);
|
|
15435
15908
|
if (clause?.namedBindings) {
|
|
15436
|
-
if (ts.isNamespaceImport(clause.namedBindings))
|
|
15437
|
-
else for (const el of clause.namedBindings.elements)
|
|
15909
|
+
if (ts.isNamespaceImport(clause.namedBindings)) bind(clause.namedBindings.name.text, true);
|
|
15910
|
+
else for (const el of clause.namedBindings.elements) bind(el.name.text, false, el.propertyName?.text, el.isTypeOnly);
|
|
15438
15911
|
}
|
|
15439
15912
|
} else if (ts.isExportDeclaration(node)) {
|
|
15440
15913
|
const spec = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
|
|
@@ -15443,6 +15916,9 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15443
15916
|
for (const el of node.exportClause.elements) {
|
|
15444
15917
|
declared.add(el.name.text);
|
|
15445
15918
|
exported.add(el.name.text);
|
|
15919
|
+
if (spec && !node.isTypeOnly && !el.isTypeOnly) {
|
|
15920
|
+
namedReexports.push({ exported: el.name.text, local: (el.propertyName ?? el.name).text, from: spec });
|
|
15921
|
+
}
|
|
15446
15922
|
}
|
|
15447
15923
|
} else if (!node.exportClause && spec) {
|
|
15448
15924
|
starExports.push(spec);
|
|
@@ -15465,7 +15941,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15465
15941
|
};
|
|
15466
15942
|
visit(sf);
|
|
15467
15943
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
15468
|
-
return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings, reexportOnly };
|
|
15944
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, mutableBindings, reexportOnly };
|
|
15469
15945
|
}
|
|
15470
15946
|
function resolveRelativeModule(fromFile, specifier) {
|
|
15471
15947
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -15488,34 +15964,47 @@ function resolveRelativeModule(fromFile, specifier) {
|
|
|
15488
15964
|
}
|
|
15489
15965
|
return null;
|
|
15490
15966
|
}
|
|
15491
|
-
function
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
targetFacts = walkExact(ts, fs8.readFileSync(target, "utf8"), target);
|
|
15501
|
-
} catch {
|
|
15502
|
-
targetFacts = null;
|
|
15503
|
-
}
|
|
15504
|
-
exactCache.set(target, targetFacts);
|
|
15967
|
+
function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCache) {
|
|
15968
|
+
const target = resolveRelativeModule(filePath, specifier);
|
|
15969
|
+
if (!target || path11.relative(projectRoot, target).startsWith("..")) return null;
|
|
15970
|
+
let targetFacts = exactCache.get(target);
|
|
15971
|
+
if (targetFacts === void 0) {
|
|
15972
|
+
try {
|
|
15973
|
+
targetFacts = walkExact(ts, fs8.readFileSync(target, "utf8"), target);
|
|
15974
|
+
} catch {
|
|
15975
|
+
targetFacts = null;
|
|
15505
15976
|
}
|
|
15506
|
-
|
|
15507
|
-
|
|
15508
|
-
|
|
15977
|
+
exactCache.set(target, targetFacts);
|
|
15978
|
+
}
|
|
15979
|
+
if (!targetFacts) return null;
|
|
15980
|
+
if (!visited.has(target)) {
|
|
15981
|
+
visited.add(target);
|
|
15982
|
+
chaseReexports(ts, targetFacts, target, projectRoot, visited, exactCache);
|
|
15983
|
+
}
|
|
15984
|
+
return { path: target, facts: targetFacts };
|
|
15985
|
+
}
|
|
15986
|
+
function carryBody(facts, target, targetPath, local, exported, projectRoot) {
|
|
15987
|
+
const score = target.complexity.get(local);
|
|
15988
|
+
if (score !== void 0) facts.complexity.set(exported, Math.max(facts.complexity.get(exported) ?? 0, score));
|
|
15989
|
+
const targetCalls = target.calls.get(local);
|
|
15990
|
+
if (!targetCalls) return;
|
|
15991
|
+
const carriedFrom = pathKey(path11.relative(projectRoot, targetPath));
|
|
15992
|
+
const sites = facts.calls.get(exported) ?? /* @__PURE__ */ new Map();
|
|
15993
|
+
for (const [key, site] of targetCalls) sites.set(carriedFrom + "|" + key, { ...site, from: site.from ?? carriedFrom });
|
|
15994
|
+
facts.calls.set(exported, sites);
|
|
15995
|
+
}
|
|
15996
|
+
function chaseReexports(ts, facts, filePath, projectRoot, visited, exactCache) {
|
|
15997
|
+
for (const re of facts.namedReexports) {
|
|
15998
|
+
const target = reexportTarget(ts, filePath, re.from, projectRoot, visited, exactCache);
|
|
15999
|
+
if (target) carryBody(facts, target.facts, target.path, re.local, re.exported, projectRoot);
|
|
16000
|
+
}
|
|
16001
|
+
for (const spec of facts.starExports) {
|
|
16002
|
+
const target = reexportTarget(ts, filePath, spec, projectRoot, visited, exactCache);
|
|
16003
|
+
if (!target) continue;
|
|
16004
|
+
for (const name of target.facts.exported) {
|
|
15509
16005
|
facts.exported.add(name);
|
|
15510
16006
|
facts.declared.add(name);
|
|
15511
|
-
|
|
15512
|
-
if (c !== void 0) facts.complexity.set(name, Math.max(facts.complexity.get(name) ?? 0, c));
|
|
15513
|
-
const targetCalls = targetFacts.calls.get(name);
|
|
15514
|
-
if (targetCalls) {
|
|
15515
|
-
const set = facts.calls.get(name) ?? /* @__PURE__ */ new Set();
|
|
15516
|
-
for (const callee of targetCalls) set.add(callee);
|
|
15517
|
-
facts.calls.set(name, set);
|
|
15518
|
-
}
|
|
16007
|
+
carryBody(facts, target.facts, target.path, name, name, projectRoot);
|
|
15519
16008
|
}
|
|
15520
16009
|
}
|
|
15521
16010
|
}
|
|
@@ -15620,7 +16109,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
15620
16109
|
if (ts) {
|
|
15621
16110
|
try {
|
|
15622
16111
|
const facts = walkExact(ts, text, absolute);
|
|
15623
|
-
|
|
16112
|
+
chaseReexports(ts, facts, absolute, projectRoot, /* @__PURE__ */ new Set([absolute]), exactCache);
|
|
15624
16113
|
analyzed = {
|
|
15625
16114
|
analysisGrade: "exact",
|
|
15626
16115
|
declaredNames: [...facts.declared],
|
|
@@ -15629,7 +16118,10 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
15629
16118
|
imports: [...facts.imports],
|
|
15630
16119
|
reexports: [...facts.reexports],
|
|
15631
16120
|
functionComplexity: Object.fromEntries(facts.complexity),
|
|
15632
|
-
|
|
16121
|
+
functionCallSites: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v.values()]])),
|
|
16122
|
+
importBindings: Object.fromEntries(facts.importBindings),
|
|
16123
|
+
typeOnlyBindings: Object.fromEntries(facts.typeOnlyBindings),
|
|
16124
|
+
fieldTypes: Object.fromEntries([...facts.fieldTypes].map(([k, v]) => [k, [...v]])),
|
|
15633
16125
|
topLevelMutableBindings: [...facts.mutableBindings],
|
|
15634
16126
|
reexportOnly: facts.reexportOnly
|
|
15635
16127
|
};
|
|
@@ -16031,18 +16523,46 @@ function buildRuleContext(opts) {
|
|
|
16031
16523
|
const allowLookup = /* @__PURE__ */ new Map();
|
|
16032
16524
|
const collectAllows = (specId, lint) => {
|
|
16033
16525
|
for (const a of lint?.allow ?? []) {
|
|
16034
|
-
const entry = { specId, code: a.code, reason: a.reason, used: false };
|
|
16526
|
+
const entry = { specId, code: a.code, at: a.at, covers: a.covers, reason: a.reason, used: false };
|
|
16035
16527
|
lintAllows.push(entry);
|
|
16036
16528
|
if (!allowLookup.has(specId)) allowLookup.set(specId, /* @__PURE__ */ new Map());
|
|
16037
|
-
allowLookup.get(specId)
|
|
16529
|
+
const forSpec = allowLookup.get(specId);
|
|
16530
|
+
const forCode = forSpec.get(a.code) ?? [];
|
|
16531
|
+
forCode.push(entry);
|
|
16532
|
+
forSpec.set(a.code, forCode);
|
|
16038
16533
|
}
|
|
16039
16534
|
};
|
|
16535
|
+
const sitesSeen = /* @__PURE__ */ new Map();
|
|
16536
|
+
const sitesReported = (specId, code) => {
|
|
16537
|
+
const seen = sitesSeen.get(`${specId}\0${code}`);
|
|
16538
|
+
return { sites: [...seen?.sites ?? []], unsited: seen?.unsited ?? false };
|
|
16539
|
+
};
|
|
16040
16540
|
for (const s of subsystems) collectAllows(s.id, s.lint);
|
|
16041
16541
|
for (const c of components) collectAllows(c.id, c.lint);
|
|
16042
16542
|
for (const i of interfaces) collectAllows(i.id, i.lint);
|
|
16043
16543
|
for (const im of implementations) collectAllows(im.id, im.lint);
|
|
16044
16544
|
for (const t of types) collectAllows(t.id, t.lint);
|
|
16045
|
-
const
|
|
16545
|
+
const carriedFindings = [];
|
|
16546
|
+
const carriedLookup = /* @__PURE__ */ new Map();
|
|
16547
|
+
const carriedKey = (spec, code, at) => `${spec}\0${code}\0${at}`;
|
|
16548
|
+
for (const group of rules?.conformance?.carried ?? []) {
|
|
16549
|
+
for (const f of group.findings ?? []) {
|
|
16550
|
+
const entry = {
|
|
16551
|
+
kind: group.kind,
|
|
16552
|
+
why: group.why,
|
|
16553
|
+
code: f.code,
|
|
16554
|
+
spec: f.spec,
|
|
16555
|
+
at: f.at,
|
|
16556
|
+
covers: f.covers ?? [],
|
|
16557
|
+
fired: false,
|
|
16558
|
+
seen: /* @__PURE__ */ new Set()
|
|
16559
|
+
};
|
|
16560
|
+
carriedFindings.push(entry);
|
|
16561
|
+
const key = carriedKey(f.spec, f.code, f.at);
|
|
16562
|
+
if (!carriedLookup.has(key)) carriedLookup.set(key, entry);
|
|
16563
|
+
}
|
|
16564
|
+
}
|
|
16565
|
+
const addIssue = (defaultSeverity, code, message, specId, isDraftContext, surfaceResolved, parts) => {
|
|
16046
16566
|
if (scopeSubsystem && specId && !isSpecInScope(specId)) {
|
|
16047
16567
|
return;
|
|
16048
16568
|
}
|
|
@@ -16053,17 +16573,46 @@ function buildRuleContext(opts) {
|
|
|
16053
16573
|
}
|
|
16054
16574
|
const severity = getRuleSeverity(code, defaultSeverity, isDraftContext, owner);
|
|
16055
16575
|
if (severity === "off") return;
|
|
16576
|
+
let text = message;
|
|
16056
16577
|
if (specId) {
|
|
16057
|
-
const
|
|
16578
|
+
const key = `${specId}\0${code}`;
|
|
16579
|
+
let seen = sitesSeen.get(key);
|
|
16580
|
+
if (!seen) sitesSeen.set(key, seen = { sites: /* @__PURE__ */ new Set(), unsited: false });
|
|
16581
|
+
if (parts) seen.sites.add(parts.at);
|
|
16582
|
+
else seen.unsited = true;
|
|
16583
|
+
}
|
|
16584
|
+
let allowClaimed = false;
|
|
16585
|
+
if (specId) {
|
|
16586
|
+
const allow = (allowLookup.get(specId)?.get(code) ?? []).find((a) => parts ? a.at === parts.at : a.at === void 0);
|
|
16058
16587
|
if (allow) {
|
|
16059
16588
|
allow.used = true;
|
|
16060
|
-
|
|
16589
|
+
allowClaimed = true;
|
|
16590
|
+
const grew = (parts?.covers ?? []).filter((unit) => !(allow.covers ?? []).includes(unit));
|
|
16591
|
+
if (grew.length === 0) {
|
|
16592
|
+
if (severity === "warning") return;
|
|
16593
|
+
} else {
|
|
16594
|
+
text = `${text} A lint.allow covers this site, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Decide on them: add them to the allow's \`covers\` with a reason that is actually true, or fix them.`;
|
|
16595
|
+
}
|
|
16596
|
+
}
|
|
16597
|
+
}
|
|
16598
|
+
if (specId && parts && severity === "warning" && !allowClaimed) {
|
|
16599
|
+
const entry = carriedLookup.get(carriedKey(specId, code, parts.at));
|
|
16600
|
+
if (entry) {
|
|
16601
|
+
entry.fired = true;
|
|
16602
|
+
const listed = new Set(entry.covers);
|
|
16603
|
+
const grew = [];
|
|
16604
|
+
for (const unit of parts.covers ?? []) {
|
|
16605
|
+
if (listed.has(unit)) entry.seen.add(unit);
|
|
16606
|
+
else grew.push(unit);
|
|
16607
|
+
}
|
|
16608
|
+
if (grew.length === 0) return;
|
|
16609
|
+
text = `${text} The conformance debt register carries this finding, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Fix them, or add them to the entry's \`covers\` with a reason that is actually true.`;
|
|
16061
16610
|
}
|
|
16062
16611
|
}
|
|
16063
16612
|
issues.push({
|
|
16064
16613
|
severity,
|
|
16065
16614
|
code,
|
|
16066
|
-
message,
|
|
16615
|
+
message: text,
|
|
16067
16616
|
specId,
|
|
16068
16617
|
...isDraftContext ? { draftContext: true } : {},
|
|
16069
16618
|
...surfaceResolved ? { surfaceResolved: true } : {}
|
|
@@ -16139,7 +16688,10 @@ function buildRuleContext(opts) {
|
|
|
16139
16688
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
16140
16689
|
roundTripIssues: opts.roundTripIssues,
|
|
16141
16690
|
lintAllows,
|
|
16691
|
+
sitesReported,
|
|
16142
16692
|
knownIssueCodes: opts.knownIssueCodes,
|
|
16693
|
+
carriedFindings,
|
|
16694
|
+
carryableIssueCodes: opts.carryableIssueCodes ?? /* @__PURE__ */ new Set(),
|
|
16143
16695
|
addIssue
|
|
16144
16696
|
};
|
|
16145
16697
|
return ctx;
|
|
@@ -17556,6 +18108,9 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
17556
18108
|
...knownIssueCodes().map((rc) => rc.code),
|
|
17557
18109
|
...extensions.assertions.map((a) => a.fullCode)
|
|
17558
18110
|
]);
|
|
18111
|
+
const carryableCodes = new Set(
|
|
18112
|
+
knownIssueCodes().filter((rc) => rc.carryable).map((rc) => rc.code)
|
|
18113
|
+
);
|
|
17559
18114
|
const ctx = buildRuleContext({
|
|
17560
18115
|
system,
|
|
17561
18116
|
subsystems,
|
|
@@ -17575,6 +18130,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
17575
18130
|
codeModel,
|
|
17576
18131
|
roundTripIssues,
|
|
17577
18132
|
knownIssueCodes: knownCodes,
|
|
18133
|
+
carryableIssueCodes: carryableCodes,
|
|
17578
18134
|
issues
|
|
17579
18135
|
});
|
|
17580
18136
|
for (const rule of sequence) {
|
|
@@ -18435,10 +18991,10 @@ function findChainingParent(childRoot, ceiling) {
|
|
|
18435
18991
|
if (bound2 && !isWithin(bound2, dir)) break;
|
|
18436
18992
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
18437
18993
|
if (pathExists(specsDir)) {
|
|
18438
|
-
for (const file of
|
|
18994
|
+
for (const file of listSpecFiles(specsDir)) {
|
|
18439
18995
|
let raw;
|
|
18440
18996
|
try {
|
|
18441
|
-
raw =
|
|
18997
|
+
raw = readSpecFile(file);
|
|
18442
18998
|
} catch {
|
|
18443
18999
|
continue;
|
|
18444
19000
|
}
|
|
@@ -18470,10 +19026,10 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
|
18470
19026
|
const walk2 = (projectDir, prefix, ancestors, depth) => {
|
|
18471
19027
|
const specsDir = aiPathsAt(projectDir).specsDir();
|
|
18472
19028
|
if (!pathExists(specsDir)) return;
|
|
18473
|
-
for (const file of
|
|
19029
|
+
for (const file of listSpecFiles(specsDir)) {
|
|
18474
19030
|
let raw;
|
|
18475
19031
|
try {
|
|
18476
|
-
raw =
|
|
19032
|
+
raw = readSpecFile(file);
|
|
18477
19033
|
} catch {
|
|
18478
19034
|
continue;
|
|
18479
19035
|
}
|
|
@@ -18657,7 +19213,7 @@ function computeSpecTreeSignature(dirs) {
|
|
|
18657
19213
|
parts.push(`${dir}:missing`);
|
|
18658
19214
|
continue;
|
|
18659
19215
|
}
|
|
18660
|
-
for (const f of
|
|
19216
|
+
for (const f of listSpecFiles(dir)) {
|
|
18661
19217
|
try {
|
|
18662
19218
|
const st = fs11.statSync(f);
|
|
18663
19219
|
parts.push(`${f}:${st.mtimeMs}:${st.size}`);
|
|
@@ -18687,9 +19243,11 @@ function identityKeyOf(field, item) {
|
|
|
18687
19243
|
return `${String(o.topic)} ${o.event === void 0 ? "" : String(o.event)}`;
|
|
18688
19244
|
case "trustedLinks":
|
|
18689
19245
|
return str(o.subsystem);
|
|
19246
|
+
// lint.allow: an allow is one decision about one OCCURRENCE, so several
|
|
19247
|
+
// may share a code on one spec, each naming its own site. Keyed by code
|
|
19248
|
+
// alone a delta for one site would overwrite its neighbours.
|
|
18690
19249
|
case "allow":
|
|
18691
|
-
return
|
|
18692
|
-
// lint.allow
|
|
19250
|
+
return `${String(o.code)} ${o.at === void 0 ? "" : String(o.at)}`;
|
|
18693
19251
|
case "findings":
|
|
18694
19252
|
return str(o.code);
|
|
18695
19253
|
// an interface method's findings
|
|
@@ -18777,6 +19335,7 @@ function identityFieldsOf(field) {
|
|
|
18777
19335
|
case "trustedLinks":
|
|
18778
19336
|
return ["subsystem"];
|
|
18779
19337
|
case "allow":
|
|
19338
|
+
return ["code", "at"];
|
|
18780
19339
|
case "findings":
|
|
18781
19340
|
return ["code"];
|
|
18782
19341
|
case "invariants":
|
|
@@ -19117,6 +19676,7 @@ var init_specs2 = __esm({
|
|
|
19117
19676
|
init_canonical_json();
|
|
19118
19677
|
init_lockfile();
|
|
19119
19678
|
init_yaml();
|
|
19679
|
+
init_spec_files();
|
|
19120
19680
|
init_models();
|
|
19121
19681
|
init_narrative_labels();
|
|
19122
19682
|
init_diagram();
|
|
@@ -19195,7 +19755,7 @@ var init_specs2 = __esm({
|
|
|
19195
19755
|
const specsDir = projectPaths.specsDir();
|
|
19196
19756
|
this.scanVisitedSpecDirs.push(specsDir);
|
|
19197
19757
|
if (!pathExists(specsDir)) return index;
|
|
19198
|
-
const files =
|
|
19758
|
+
const files = listSpecFiles(specsDir);
|
|
19199
19759
|
const systemYaml = path16.normalize(projectPaths.specsSystem());
|
|
19200
19760
|
const localSubprojects = [];
|
|
19201
19761
|
for (const file of files) {
|
|
@@ -19204,7 +19764,7 @@ var init_specs2 = __esm({
|
|
|
19204
19764
|
let detectedType = "spec";
|
|
19205
19765
|
let rawId;
|
|
19206
19766
|
try {
|
|
19207
|
-
const raw =
|
|
19767
|
+
const raw = readSpecFile(file);
|
|
19208
19768
|
if (raw === null || typeof raw !== "object") {
|
|
19209
19769
|
this.loaderIssues.push({
|
|
19210
19770
|
severity: "error",
|
|
@@ -19687,7 +20247,7 @@ var init_specs2 = __esm({
|
|
|
19687
20247
|
const p = this.paths.specsSystem();
|
|
19688
20248
|
if (!pathExists(p)) return null;
|
|
19689
20249
|
try {
|
|
19690
|
-
const raw =
|
|
20250
|
+
const raw = readSpecFile(p);
|
|
19691
20251
|
return SystemSpecSchema.parse(raw);
|
|
19692
20252
|
} catch (e) {
|
|
19693
20253
|
this.loaderIssues.push({
|
|
@@ -19702,7 +20262,7 @@ var init_specs2 = __esm({
|
|
|
19702
20262
|
saveSystemSpec(spec) {
|
|
19703
20263
|
const p = this.paths.specsSystem();
|
|
19704
20264
|
ensureDir(path16.dirname(p));
|
|
19705
|
-
|
|
20265
|
+
writeSpecFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
|
|
19706
20266
|
invalidateSpecCache();
|
|
19707
20267
|
}
|
|
19708
20268
|
// -------------------------------------------------------------------------
|
|
@@ -19718,7 +20278,7 @@ var init_specs2 = __esm({
|
|
|
19718
20278
|
const p = this.getSubsystemPath(id);
|
|
19719
20279
|
if (!pathExists(p)) return null;
|
|
19720
20280
|
try {
|
|
19721
|
-
const raw =
|
|
20281
|
+
const raw = readSpecFile(p);
|
|
19722
20282
|
return SubsystemSpecSchema.parse(raw);
|
|
19723
20283
|
} catch (e) {
|
|
19724
20284
|
this.loaderIssues.push({
|
|
@@ -19852,7 +20412,7 @@ var init_specs2 = __esm({
|
|
|
19852
20412
|
}
|
|
19853
20413
|
}
|
|
19854
20414
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
19855
|
-
|
|
20415
|
+
writeSpecFile(p, parseOrThrow(SubsystemSpecSchema, specToWrite, "subsystem", spec.id));
|
|
19856
20416
|
invalidateSpecCache();
|
|
19857
20417
|
}
|
|
19858
20418
|
deleteSubsystemSpec(id) {
|
|
@@ -19876,7 +20436,7 @@ var init_specs2 = __esm({
|
|
|
19876
20436
|
const p = this.getComponentPath(id);
|
|
19877
20437
|
if (!pathExists(p)) return null;
|
|
19878
20438
|
try {
|
|
19879
|
-
const raw =
|
|
20439
|
+
const raw = readSpecFile(p);
|
|
19880
20440
|
return ComponentSpecSchema.parse(raw);
|
|
19881
20441
|
} catch (e) {
|
|
19882
20442
|
this.loaderIssues.push({
|
|
@@ -19913,7 +20473,7 @@ var init_specs2 = __esm({
|
|
|
19913
20473
|
);
|
|
19914
20474
|
}
|
|
19915
20475
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
19916
|
-
|
|
20476
|
+
writeSpecFile(p, parseOrThrow(ComponentSpecSchema, specToWrite, "component", spec.id));
|
|
19917
20477
|
invalidateSpecCache();
|
|
19918
20478
|
this.normalizeComponentLayout();
|
|
19919
20479
|
return notices;
|
|
@@ -19974,7 +20534,7 @@ var init_specs2 = __esm({
|
|
|
19974
20534
|
const p = this.getInterfacePath(id);
|
|
19975
20535
|
if (!pathExists(p)) return null;
|
|
19976
20536
|
try {
|
|
19977
|
-
const raw =
|
|
20537
|
+
const raw = readSpecFile(p);
|
|
19978
20538
|
return InterfaceSpecSchema.parse(raw);
|
|
19979
20539
|
} catch (e) {
|
|
19980
20540
|
this.loaderIssues.push({
|
|
@@ -20005,7 +20565,7 @@ var init_specs2 = __esm({
|
|
|
20005
20565
|
if (!pathExists(p)) return;
|
|
20006
20566
|
let occupantId;
|
|
20007
20567
|
try {
|
|
20008
|
-
occupantId =
|
|
20568
|
+
occupantId = readSpecFile(p)?.id;
|
|
20009
20569
|
} catch {
|
|
20010
20570
|
return;
|
|
20011
20571
|
}
|
|
@@ -20042,7 +20602,7 @@ var init_specs2 = __esm({
|
|
|
20042
20602
|
}
|
|
20043
20603
|
}
|
|
20044
20604
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
20045
|
-
|
|
20605
|
+
writeSpecFile(p, parseOrThrow(InterfaceSpecSchema, specToWrite, "interface", spec.id));
|
|
20046
20606
|
invalidateSpecCache();
|
|
20047
20607
|
return notices;
|
|
20048
20608
|
}
|
|
@@ -20067,7 +20627,7 @@ var init_specs2 = __esm({
|
|
|
20067
20627
|
const p = this.getImplementationPath(id);
|
|
20068
20628
|
if (!pathExists(p)) return null;
|
|
20069
20629
|
try {
|
|
20070
|
-
const raw =
|
|
20630
|
+
const raw = readSpecFile(p);
|
|
20071
20631
|
return ImplementationSpecSchema.parse(raw);
|
|
20072
20632
|
} catch (e) {
|
|
20073
20633
|
this.loaderIssues.push({
|
|
@@ -20099,7 +20659,7 @@ var init_specs2 = __esm({
|
|
|
20099
20659
|
}
|
|
20100
20660
|
}
|
|
20101
20661
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
20102
|
-
|
|
20662
|
+
writeSpecFile(p, parseOrThrow(ImplementationSpecSchema, specToWrite, "implementation", spec.id));
|
|
20103
20663
|
invalidateSpecCache();
|
|
20104
20664
|
return notices;
|
|
20105
20665
|
}
|
|
@@ -20152,7 +20712,7 @@ var init_specs2 = __esm({
|
|
|
20152
20712
|
}
|
|
20153
20713
|
}
|
|
20154
20714
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
20155
|
-
|
|
20715
|
+
writeSpecFile(p, parseOrThrow(TypeSpecSchema, specToWrite, "type", spec.id));
|
|
20156
20716
|
invalidateSpecCache();
|
|
20157
20717
|
return notices;
|
|
20158
20718
|
}
|
|
@@ -20183,7 +20743,7 @@ var init_specs2 = __esm({
|
|
|
20183
20743
|
specToWrite.createdAt = existing.createdAt;
|
|
20184
20744
|
}
|
|
20185
20745
|
specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
20186
|
-
|
|
20746
|
+
writeSpecFile(p, parseOrThrow(GroupSpecSchema, specToWrite, "group", spec.id));
|
|
20187
20747
|
invalidateSpecCache();
|
|
20188
20748
|
}
|
|
20189
20749
|
deleteGroupSpec(id) {
|
|
@@ -20347,7 +20907,7 @@ var init_specs2 = __esm({
|
|
|
20347
20907
|
findLegacySpecFiles() {
|
|
20348
20908
|
const specsDir = this.paths.specsDir();
|
|
20349
20909
|
if (!pathExists(specsDir)) return [];
|
|
20350
|
-
const files =
|
|
20910
|
+
const files = listSpecFiles(specsDir);
|
|
20351
20911
|
const legacy = [];
|
|
20352
20912
|
for (const f of files) {
|
|
20353
20913
|
const base = path16.basename(f);
|
|
@@ -26491,6 +27051,9 @@ __export(src_exports, {
|
|
|
26491
27051
|
BuiltinTargetSchema: () => BuiltinTargetSchema,
|
|
26492
27052
|
BundleNotFoundError: () => BundleNotFoundError,
|
|
26493
27053
|
CONTEXT_PATHS: () => CONTEXT_PATHS,
|
|
27054
|
+
CarriedDebtKindSchema: () => CarriedDebtKindSchema,
|
|
27055
|
+
CarriedDebtSchema: () => CarriedDebtSchema,
|
|
27056
|
+
CarriedFindingSchema: () => CarriedFindingSchema,
|
|
26494
27057
|
CatchClauseSchema: () => CatchClauseSchema,
|
|
26495
27058
|
ClaudeExporter: () => ClaudeExporter,
|
|
26496
27059
|
ComplexityRuleConfigSchema: () => ComplexityRuleConfigSchema,
|
|
@@ -26625,6 +27188,7 @@ __export(src_exports, {
|
|
|
26625
27188
|
buildRuleContext: () => buildRuleContext,
|
|
26626
27189
|
buildServerInstructions: () => buildServerInstructions2,
|
|
26627
27190
|
buildTreeArchive: () => buildTreeArchive2,
|
|
27191
|
+
callSitesOf: () => callSitesOf,
|
|
26628
27192
|
captureApprovedSpecs: () => captureApprovedSpecs,
|
|
26629
27193
|
checkSkillFreshness: () => checkSkillFreshness,
|
|
26630
27194
|
clearLoaderIssues: () => clearLoaderIssues,
|
|
@@ -26684,6 +27248,7 @@ __export(src_exports, {
|
|
|
26684
27248
|
extractTypeIdentifiers: () => extractTypeIdentifiers,
|
|
26685
27249
|
factsFor: () => factsFor,
|
|
26686
27250
|
fieldTypeRefs: () => fieldTypeRefs,
|
|
27251
|
+
fieldTypesOf: () => fieldTypesOf,
|
|
26687
27252
|
findChainingParent: () => findChainingParent,
|
|
26688
27253
|
findChainingSubprojectsMissingConfig: () => findChainingSubprojectsMissingConfig,
|
|
26689
27254
|
findDomain: () => findDomain,
|
|
@@ -26718,10 +27283,12 @@ __export(src_exports, {
|
|
|
26718
27283
|
hasArchitectureContext: () => hasArchitectureContext,
|
|
26719
27284
|
hasAuthoredSpecs: () => hasAuthoredSpecs,
|
|
26720
27285
|
hasContext: () => hasContext,
|
|
27286
|
+
hasFunctionBody: () => hasFunctionBody,
|
|
26721
27287
|
hasSpecTree: () => hasSpecTree,
|
|
26722
27288
|
headNoun: () => headNoun,
|
|
26723
27289
|
holdsState: () => holdsState,
|
|
26724
27290
|
implementationSourceFiles: () => implementationSourceFiles,
|
|
27291
|
+
importBindingOf: () => importBindingOf,
|
|
26725
27292
|
importSpecTree: () => importSpecTree,
|
|
26726
27293
|
importSurface: () => importSurface,
|
|
26727
27294
|
ineffectiveDeltaPaths: () => ineffectiveDeltaPaths,
|
|
@@ -26877,6 +27444,7 @@ __export(src_exports, {
|
|
|
26877
27444
|
toMarkdown: () => toMarkdown,
|
|
26878
27445
|
toOpenApi: () => toOpenApi,
|
|
26879
27446
|
toOpenApiSet: () => toOpenApiSet,
|
|
27447
|
+
typeBindingOf: () => typeBindingOf,
|
|
26880
27448
|
typeGenericParameters: () => typeGenericParameters,
|
|
26881
27449
|
typeMatchesRef: () => typeMatchesRef,
|
|
26882
27450
|
typeSourceFiles: () => typeSourceFiles,
|
|
@@ -27221,6 +27789,9 @@ function loadProjectConfig4() {
|
|
|
27221
27789
|
BuiltinTargetSchema,
|
|
27222
27790
|
BundleNotFoundError,
|
|
27223
27791
|
CONTEXT_PATHS,
|
|
27792
|
+
CarriedDebtKindSchema,
|
|
27793
|
+
CarriedDebtSchema,
|
|
27794
|
+
CarriedFindingSchema,
|
|
27224
27795
|
CatchClauseSchema,
|
|
27225
27796
|
ClaudeExporter,
|
|
27226
27797
|
ComplexityRuleConfigSchema,
|
|
@@ -27355,6 +27926,7 @@ function loadProjectConfig4() {
|
|
|
27355
27926
|
buildRuleContext,
|
|
27356
27927
|
buildServerInstructions,
|
|
27357
27928
|
buildTreeArchive,
|
|
27929
|
+
callSitesOf,
|
|
27358
27930
|
captureApprovedSpecs,
|
|
27359
27931
|
checkSkillFreshness,
|
|
27360
27932
|
clearLoaderIssues,
|
|
@@ -27414,6 +27986,7 @@ function loadProjectConfig4() {
|
|
|
27414
27986
|
extractTypeIdentifiers,
|
|
27415
27987
|
factsFor,
|
|
27416
27988
|
fieldTypeRefs,
|
|
27989
|
+
fieldTypesOf,
|
|
27417
27990
|
findChainingParent,
|
|
27418
27991
|
findChainingSubprojectsMissingConfig,
|
|
27419
27992
|
findDomain,
|
|
@@ -27448,10 +28021,12 @@ function loadProjectConfig4() {
|
|
|
27448
28021
|
hasArchitectureContext,
|
|
27449
28022
|
hasAuthoredSpecs,
|
|
27450
28023
|
hasContext,
|
|
28024
|
+
hasFunctionBody,
|
|
27451
28025
|
hasSpecTree,
|
|
27452
28026
|
headNoun,
|
|
27453
28027
|
holdsState,
|
|
27454
28028
|
implementationSourceFiles,
|
|
28029
|
+
importBindingOf,
|
|
27455
28030
|
importSpecTree,
|
|
27456
28031
|
importSurface,
|
|
27457
28032
|
ineffectiveDeltaPaths,
|
|
@@ -27607,6 +28182,7 @@ function loadProjectConfig4() {
|
|
|
27607
28182
|
toMarkdown,
|
|
27608
28183
|
toOpenApi,
|
|
27609
28184
|
toOpenApiSet,
|
|
28185
|
+
typeBindingOf,
|
|
27610
28186
|
typeGenericParameters,
|
|
27611
28187
|
typeMatchesRef,
|
|
27612
28188
|
typeSourceFiles,
|