@wairon/cli 5.1.1-dev.38 → 5.1.1-dev.39
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 +307 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +301 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.39";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -770,7 +770,7 @@ function activeTargetTypes(config) {
|
|
|
770
770
|
const targets = config.targets;
|
|
771
771
|
return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
|
|
772
772
|
}
|
|
773
|
-
var import_zod4, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
773
|
+
var import_zod4, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
|
|
774
774
|
var init_project = __esm({
|
|
775
775
|
"src/models/project.ts"() {
|
|
776
776
|
"use strict";
|
|
@@ -867,6 +867,30 @@ var init_project = __esm({
|
|
|
867
867
|
*/
|
|
868
868
|
maxCognitiveLevel: import_zod4.z.string().optional()
|
|
869
869
|
});
|
|
870
|
+
ConformanceRuleConfigSchema = import_zod4.z.object({
|
|
871
|
+
/**
|
|
872
|
+
* Project-relative paths holding this project's own source code; a
|
|
873
|
+
* directory is walked recursively, a file names itself. Absolute or
|
|
874
|
+
* parent-escaping entries are refused by containment, exactly as a
|
|
875
|
+
* sourcePath is.
|
|
876
|
+
*/
|
|
877
|
+
sourceRoots: import_zod4.z.array(import_zod4.z.string()).optional(),
|
|
878
|
+
/**
|
|
879
|
+
* Paths inside a source root that are not this project's code to claim —
|
|
880
|
+
* vendored libraries, generated output, a chained subproject's own
|
|
881
|
+
* directory (the child claims its files in its own run). A file at, or
|
|
882
|
+
* under, one of these is never walked, so it is neither reported nor
|
|
883
|
+
* carried as debt.
|
|
884
|
+
*/
|
|
885
|
+
exclude: import_zod4.z.array(import_zod4.z.string()).optional(),
|
|
886
|
+
/**
|
|
887
|
+
* The files under the source roots that no spec names yet, frozen. A listed
|
|
888
|
+
* file is not reported; an unlisted one is UNCLAIMED_SOURCE_FILE; an entry
|
|
889
|
+
* that is now claimed, proven a barrel, or no longer found is
|
|
890
|
+
* STALE_UNCLAIMED_ENTRY.
|
|
891
|
+
*/
|
|
892
|
+
unclaimed: import_zod4.z.array(import_zod4.z.string()).optional()
|
|
893
|
+
});
|
|
870
894
|
DesignDepthSchema = import_zod4.z.enum(["components", "interfaces", "implementations", "narratives"]);
|
|
871
895
|
RulesConfigSchema = import_zod4.z.object({
|
|
872
896
|
/**
|
|
@@ -917,6 +941,8 @@ var init_project = __esm({
|
|
|
917
941
|
documentation: DocumentationRuleConfigSchema.optional(),
|
|
918
942
|
/** Dynamic structural complexity caps (method limit, step limit, dependency limit) */
|
|
919
943
|
complexity: ComplexityRuleConfigSchema.optional(),
|
|
944
|
+
/** Where this project's own source lives and what of it no spec claims yet (see ConformanceRuleConfigSchema) */
|
|
945
|
+
conformance: ConformanceRuleConfigSchema.optional(),
|
|
920
946
|
/** Project-default design depth (see DesignDepthSchema); subsystems may override. */
|
|
921
947
|
designDepth: DesignDepthSchema.optional()
|
|
922
948
|
});
|
|
@@ -1228,6 +1254,15 @@ function implementationSourceFiles(impl) {
|
|
|
1228
1254
|
for (const method2 of impl.methods ?? []) add(method2.sourcePath);
|
|
1229
1255
|
return files;
|
|
1230
1256
|
}
|
|
1257
|
+
function typeSourceFiles(type) {
|
|
1258
|
+
const files = [];
|
|
1259
|
+
const add = (file) => {
|
|
1260
|
+
if (file && !files.includes(file)) files.push(file);
|
|
1261
|
+
};
|
|
1262
|
+
add(type.sourcePath);
|
|
1263
|
+
for (const method2 of type.methods ?? []) add(method2.sourcePath);
|
|
1264
|
+
return files;
|
|
1265
|
+
}
|
|
1231
1266
|
var import_zod7, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
|
|
1232
1267
|
var init_specs = __esm({
|
|
1233
1268
|
"src/models/specs.ts"() {
|
|
@@ -1854,7 +1889,22 @@ var init_specs = __esm({
|
|
|
1854
1889
|
name: import_zod7.z.string(),
|
|
1855
1890
|
signature: import_zod7.z.string(),
|
|
1856
1891
|
returns: import_zod7.z.string(),
|
|
1857
|
-
description: import_zod7.z.string().optional()
|
|
1892
|
+
description: import_zod7.z.string().optional(),
|
|
1893
|
+
/**
|
|
1894
|
+
* Source file realizing this method when it is not the type's own
|
|
1895
|
+
* sourcePath. A type's pure methods routinely live apart from its
|
|
1896
|
+
* declaration: the declaration is a struct or an interface, the methods are
|
|
1897
|
+
* free functions, and a language without methods-on-data has nowhere else
|
|
1898
|
+
* to put them.
|
|
1899
|
+
*/
|
|
1900
|
+
sourcePath: import_zod7.z.string().optional(),
|
|
1901
|
+
/**
|
|
1902
|
+
* The code-level name realizing this method, when it legitimately differs
|
|
1903
|
+
* from the method name — e.g. `narrative_step.foreignFields` realized by
|
|
1904
|
+
* `narrativeStepForeignFields`, the free-function form a pure type method
|
|
1905
|
+
* takes in a language whose data carries no methods.
|
|
1906
|
+
*/
|
|
1907
|
+
symbol: import_zod7.z.string().optional()
|
|
1858
1908
|
});
|
|
1859
1909
|
InvariantSchema = import_zod7.z.object({
|
|
1860
1910
|
/** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
|
|
@@ -1899,6 +1949,23 @@ var init_specs = __esm({
|
|
|
1899
1949
|
* logical system entity type it maps to.
|
|
1900
1950
|
*/
|
|
1901
1951
|
linkedEntity: import_zod7.z.string().optional(),
|
|
1952
|
+
/**
|
|
1953
|
+
* The source file holding this type's declaration, and the default for
|
|
1954
|
+
* every method that names no sourcePath of its own. Relative to the root of
|
|
1955
|
+
* the project that holds the spec.
|
|
1956
|
+
*
|
|
1957
|
+
* Naming one turns the type into a CLAIM on code, judged exactly as an
|
|
1958
|
+
* implementation's sourcePath is: the file must resolve, and the
|
|
1959
|
+
* declaration must be anchored in it (UNREALIZED_TYPE). A type that names
|
|
1960
|
+
* none claims nothing and is never reported.
|
|
1961
|
+
*/
|
|
1962
|
+
sourcePath: import_zod7.z.string().optional(),
|
|
1963
|
+
/**
|
|
1964
|
+
* The code-level name realizing this type's declaration, when it
|
|
1965
|
+
* legitimately differs from `name` — a type named "Invoice Line" declared
|
|
1966
|
+
* as `InvoiceLine`.
|
|
1967
|
+
*/
|
|
1968
|
+
symbol: import_zod7.z.string().optional(),
|
|
1902
1969
|
/** Per-spec lint suppressions (see LintConfigSchema). */
|
|
1903
1970
|
lint: LintConfigSchema.optional(),
|
|
1904
1971
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
@@ -3692,11 +3759,11 @@ function buildCanvasModel(issues = []) {
|
|
|
3692
3759
|
if (pi.component && pi.id) apiTagOf.set(pi.component, pi.id);
|
|
3693
3760
|
}
|
|
3694
3761
|
const patternTypes = /* @__PURE__ */ new Set(["Repository", "FeatureComponent", "RouterComponent"]);
|
|
3695
|
-
const
|
|
3762
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
3696
3763
|
for (const comp of components) {
|
|
3697
3764
|
if (!patternTypes.has(comp.componentType)) continue;
|
|
3698
3765
|
for (const memberId of comp.owns) {
|
|
3699
|
-
if (componentIds.has(memberId))
|
|
3766
|
+
if (componentIds.has(memberId)) ownerOf2.set(memberId, comp.id);
|
|
3700
3767
|
}
|
|
3701
3768
|
}
|
|
3702
3769
|
const modelComponents = components.map((comp) => {
|
|
@@ -3752,7 +3819,7 @@ function buildCanvasModel(issues = []) {
|
|
|
3752
3819
|
...comp.status ? { status: comp.status } : {},
|
|
3753
3820
|
public: publicComponents.has(comp.id),
|
|
3754
3821
|
...apiTagOf.has(comp.id) ? { apiTag: apiTagOf.get(comp.id) } : {},
|
|
3755
|
-
...
|
|
3822
|
+
...ownerOf2.has(comp.id) ? { owner: ownerOf2.get(comp.id) } : {},
|
|
3756
3823
|
owns: comp.owns.filter((o) => componentIds.has(o)),
|
|
3757
3824
|
dependsOn: comp.dependsOn,
|
|
3758
3825
|
...comp.externalLinks && comp.externalLinks.length ? { externalLinks: comp.externalLinks } : {},
|
|
@@ -13342,9 +13409,9 @@ var init_dependency_conformance = __esm({
|
|
|
13342
13409
|
};
|
|
13343
13410
|
const mappedImplsOf = (compId) => realization.implementationsOf(compId).filter((impl) => implementationSourceFiles(impl).some((f) => isExact(pathKey(f))));
|
|
13344
13411
|
const dependsOrOwns = (from, toId) => from.dependsOn.includes(toId) || (from.owns ?? []).includes(toId);
|
|
13345
|
-
const
|
|
13412
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
13346
13413
|
for (const c of ctx.components) {
|
|
13347
|
-
for (const member of c.owns ?? [])
|
|
13414
|
+
for (const member of c.owns ?? []) ownerOf2.set(member, c);
|
|
13348
13415
|
}
|
|
13349
13416
|
const declaresSurfaceEdge = (from, subsystemId) => {
|
|
13350
13417
|
const published = ctx.publicSet.get(subsystemId);
|
|
@@ -13357,9 +13424,9 @@ var init_dependency_conformance = __esm({
|
|
|
13357
13424
|
if (cf.id === cg.id) return true;
|
|
13358
13425
|
if (dependsOrOwns(cf, cg.id)) return true;
|
|
13359
13426
|
if (cf.subsystem === cg.subsystem && dependsOrOwns(cg, cf.id) && (cg.componentType === "Portal" || cg.componentType === "Observer")) return true;
|
|
13360
|
-
const ownerG =
|
|
13427
|
+
const ownerG = ownerOf2.get(cg.id);
|
|
13361
13428
|
if (ownerG && (dependsOrOwns(cf, ownerG.id) || cf.id === ownerG.id)) return true;
|
|
13362
|
-
const ownerF =
|
|
13429
|
+
const ownerF = ownerOf2.get(cf.id);
|
|
13363
13430
|
if (ownerF && (dependsOrOwns(ownerF, cg.id) || ownerF.id === cg.id)) return true;
|
|
13364
13431
|
if (ownerF && ownerG && ownerF.id === ownerG.id) return true;
|
|
13365
13432
|
if (cf.subsystem !== cg.subsystem && declaresSurfaceEdge(cf, cg.subsystem)) return true;
|
|
@@ -13613,6 +13680,150 @@ var init_integration_sim_coverage = __esm({
|
|
|
13613
13680
|
}
|
|
13614
13681
|
});
|
|
13615
13682
|
|
|
13683
|
+
// src/core/rules/conformance/type-realization.ts
|
|
13684
|
+
function ownerOf(type, file) {
|
|
13685
|
+
if (type.sourcePath && pathKey(type.sourcePath) === pathKey(file)) return `Type "${type.id}"`;
|
|
13686
|
+
const users = (type.methods ?? []).filter((m) => m.sourcePath && pathKey(m.sourcePath) === pathKey(file)).map((m) => m.name);
|
|
13687
|
+
if (users.length === 0) return `Type "${type.id}"`;
|
|
13688
|
+
return `Type "${type.id}" method${users.length === 1 ? "" : "s"} ${users.map((n) => `"${n}"`).join(", ")}`;
|
|
13689
|
+
}
|
|
13690
|
+
var FILE_PROBLEMS, typeRealizationRule;
|
|
13691
|
+
var init_type_realization = __esm({
|
|
13692
|
+
"src/core/rules/conformance/type-realization.ts"() {
|
|
13693
|
+
"use strict";
|
|
13694
|
+
init_models();
|
|
13695
|
+
FILE_PROBLEMS = {
|
|
13696
|
+
escaped: {
|
|
13697
|
+
code: "SOURCE_PATH_ESCAPES_ROOT",
|
|
13698
|
+
severity: "error",
|
|
13699
|
+
detail: "is absolute or escapes the project root \u2014 sourcePaths must stay inside the project."
|
|
13700
|
+
},
|
|
13701
|
+
missing: {
|
|
13702
|
+
code: "MISSING_SOURCE_FILE",
|
|
13703
|
+
severity: "error",
|
|
13704
|
+
detail: "does not resolve to a file \u2014 the spec names code that does not exist."
|
|
13705
|
+
},
|
|
13706
|
+
unreadable: {
|
|
13707
|
+
code: "CONFORMANCE_ANALYSIS_SKIPPED",
|
|
13708
|
+
severity: "warning",
|
|
13709
|
+
detail: "could not be analyzed (binary or unreadable) \u2014 what it would have realized was not checked."
|
|
13710
|
+
}
|
|
13711
|
+
};
|
|
13712
|
+
typeRealizationRule = {
|
|
13713
|
+
name: "type-realization",
|
|
13714
|
+
description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code, so every file it names \u2014 its own and each method's \u2014 must resolve to a real, readable file inside the project root, its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath claims nothing and is never reported. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible, a file that escapes the root, is missing or could not be analyzed is reported once and blocks only what it would have realized, and types under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
13715
|
+
codes: [
|
|
13716
|
+
{ code: "UNREALIZED_TYPE", defaultSeverity: "warning", summary: "A type names a sourcePath but its declaration is nowhere in that file \u2014 the claim points at code that does not hold it" },
|
|
13717
|
+
{ code: "UNREALIZED_TYPE_METHOD", defaultSeverity: "warning", summary: "A pure method of a claimed type is nowhere in its own source file (the method's sourcePath, else the type's)" },
|
|
13718
|
+
{ code: "MISSING_SOURCE_FILE", defaultSeverity: "error", summary: "A source file a type or one of its methods names does not resolve to a file on disk" },
|
|
13719
|
+
{ code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file a type or one of its methods names is absolute or escapes the project root (containment refusal)" },
|
|
13720
|
+
{ code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file a type or one of its methods names could not be analyzed (binary/unreadable) \u2014 what it would have realized was not checked" }
|
|
13721
|
+
],
|
|
13722
|
+
check(ctx) {
|
|
13723
|
+
const code = ctx.codeIndex();
|
|
13724
|
+
for (const type of ctx.types) {
|
|
13725
|
+
if (!type.sourcePath) continue;
|
|
13726
|
+
if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
|
|
13727
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
13728
|
+
for (const file of typeSourceFiles(type)) {
|
|
13729
|
+
const key = pathKey(file);
|
|
13730
|
+
const facts = code.factsAt(key);
|
|
13731
|
+
if (!facts || facts.status === "analyzed") continue;
|
|
13732
|
+
blocked.add(key);
|
|
13733
|
+
const problem = FILE_PROBLEMS[facts.status];
|
|
13734
|
+
if (!problem) continue;
|
|
13735
|
+
ctx.addIssue(problem.severity, problem.code, `${ownerOf(type, file)} sourcePath "${file}" ${problem.detail}`, type.id);
|
|
13736
|
+
}
|
|
13737
|
+
const typeKey = pathKey(type.sourcePath);
|
|
13738
|
+
const typeFacts = code.factsAt(typeKey);
|
|
13739
|
+
if (typeFacts && !blocked.has(typeKey)) {
|
|
13740
|
+
const symbol = type.symbol ?? type.name;
|
|
13741
|
+
const published = typeFacts.analysisGrade === "exact" ? new Set(typeFacts.reexportOnly ? [] : typeFacts.exportedNames) : code.declarationsAt(typeKey);
|
|
13742
|
+
if (!published.has(symbol)) {
|
|
13743
|
+
const label = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
|
|
13744
|
+
ctx.addIssue(
|
|
13745
|
+
"warning",
|
|
13746
|
+
"UNREALIZED_TYPE",
|
|
13747
|
+
`Type ${label} is not published by "${type.sourcePath}" (analysis grade: ${typeFacts.analysisGrade})${typeFacts.reexportOnly ? ", which is a pure re-export barrel and declares nothing of its own" : ""} \u2014 name the file the declaration lives in, or bind the code-level name with \`symbol\`.`,
|
|
13748
|
+
type.id
|
|
13749
|
+
);
|
|
13750
|
+
}
|
|
13751
|
+
}
|
|
13752
|
+
for (const method2 of type.methods ?? []) {
|
|
13753
|
+
const file = method2.sourcePath ?? type.sourcePath;
|
|
13754
|
+
const key = pathKey(file);
|
|
13755
|
+
if (blocked.has(key)) continue;
|
|
13756
|
+
const facts = code.factsAt(key);
|
|
13757
|
+
if (!facts || facts.status !== "analyzed") continue;
|
|
13758
|
+
const symbol = method2.symbol ?? method2.name;
|
|
13759
|
+
if (code.declarationsAt(key).has(symbol)) continue;
|
|
13760
|
+
const label = method2.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
|
|
13761
|
+
ctx.addIssue(
|
|
13762
|
+
"warning",
|
|
13763
|
+
"UNREALIZED_TYPE_METHOD",
|
|
13764
|
+
`Method ${label} of type "${type.id}" is not declared in "${file}" (analysis grade: ${facts.analysisGrade}) \u2014 a pure type method is often a free function named after its type; bind that name with \`symbol\`, or name the file it lives in with the method's own \`sourcePath\`.`,
|
|
13765
|
+
type.id
|
|
13766
|
+
);
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
}
|
|
13770
|
+
};
|
|
13771
|
+
}
|
|
13772
|
+
});
|
|
13773
|
+
|
|
13774
|
+
// src/core/rules/conformance/unclaimed-source.ts
|
|
13775
|
+
var unclaimedSourceRule;
|
|
13776
|
+
var init_unclaimed_source = __esm({
|
|
13777
|
+
"src/core/rules/conformance/unclaimed-source.ts"() {
|
|
13778
|
+
"use strict";
|
|
13779
|
+
init_models();
|
|
13780
|
+
unclaimedSourceRule = {
|
|
13781
|
+
name: "unclaimed-source",
|
|
13782
|
+
description: "Code\u2194spec Level 1, asked the other way round: every source file under the project's declared source roots must be named by some spec \u2014 an implementation's sourcePath, a method's, a simPath, or a type's \u2014 or be carried in the frozen `rules.conformance.unclaimed` list. A pure re-export barrel declares nothing of its own and only republishes other modules, so it has no code to claim and is exempt (exact grade only: a weaker grade cannot tell a barrel from a file it failed to parse). Declaring no source roots leaves the check silent, which is what makes it opt-in; the unclaimed list is a one-way debt register, so an entry the walk no longer finds unclaimed must be deleted and the list can only shrink.",
|
|
13783
|
+
codes: [
|
|
13784
|
+
{ code: "UNCLAIMED_SOURCE_FILE", defaultSeverity: "warning", summary: "A source file under a declared source root that no spec names and the frozen unclaimed list does not carry \u2014 code nobody designed" },
|
|
13785
|
+
{ code: "STALE_UNCLAIMED_ENTRY", defaultSeverity: "warning", summary: "An entry of the frozen unclaimed list names a file that is now claimed, or that the source-root walk no longer finds \u2014 delete it, the list only shrinks" }
|
|
13786
|
+
],
|
|
13787
|
+
check(ctx) {
|
|
13788
|
+
const walked = ctx.codeModel.rootFiles;
|
|
13789
|
+
const listed = (ctx.rules?.conformance?.unclaimed ?? []).map(pathKey);
|
|
13790
|
+
if (walked.length === 0) return;
|
|
13791
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
13792
|
+
for (const impl of ctx.implementations) {
|
|
13793
|
+
for (const file of implementationSourceFiles(impl)) claimed.add(pathKey(file));
|
|
13794
|
+
if (impl.simPath) claimed.add(pathKey(impl.simPath));
|
|
13795
|
+
}
|
|
13796
|
+
for (const type of ctx.types) {
|
|
13797
|
+
for (const file of typeSourceFiles(type)) claimed.add(pathKey(file));
|
|
13798
|
+
}
|
|
13799
|
+
const code = ctx.codeIndex();
|
|
13800
|
+
const isBarrel = (file) => code.factsAt(file)?.reexportOnly === true;
|
|
13801
|
+
const isLiveDebt = (file) => !claimed.has(file) && !isBarrel(file);
|
|
13802
|
+
const listedSet = new Set(listed);
|
|
13803
|
+
for (const file of walked) {
|
|
13804
|
+
if (!isLiveDebt(file)) continue;
|
|
13805
|
+
if (listedSet.has(file)) continue;
|
|
13806
|
+
ctx.addIssue(
|
|
13807
|
+
"warning",
|
|
13808
|
+
"UNCLAIMED_SOURCE_FILE",
|
|
13809
|
+
`Source file "${file}" is under a declared source root but no spec names it \u2014 give it to a spec as a sourcePath (an implementation's, a method's, or a type's), or carry it in \`rules.conformance.unclaimed\` until it is designed.`
|
|
13810
|
+
);
|
|
13811
|
+
}
|
|
13812
|
+
const walkedSet = new Set(walked);
|
|
13813
|
+
for (const entry of listed) {
|
|
13814
|
+
if (walkedSet.has(entry) && isLiveDebt(entry)) continue;
|
|
13815
|
+
const why = !walkedSet.has(entry) ? "the source-root walk no longer finds it (deleted, renamed, excluded, or outside the declared roots)" : isBarrel(entry) ? "it is a pure re-export barrel, which has nothing to claim" : "a spec now names it";
|
|
13816
|
+
ctx.addIssue(
|
|
13817
|
+
"warning",
|
|
13818
|
+
"STALE_UNCLAIMED_ENTRY",
|
|
13819
|
+
`"${entry}" is carried in \`rules.conformance.unclaimed\` but ${why} \u2014 delete the entry; the list only shrinks.`
|
|
13820
|
+
);
|
|
13821
|
+
}
|
|
13822
|
+
}
|
|
13823
|
+
};
|
|
13824
|
+
}
|
|
13825
|
+
});
|
|
13826
|
+
|
|
13616
13827
|
// src/core/rules/heuristic/coupling-health.ts
|
|
13617
13828
|
var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
|
|
13618
13829
|
var init_coupling_health = __esm({
|
|
@@ -13956,14 +14167,14 @@ var init_technology_boundaries = __esm({
|
|
|
13956
14167
|
{ code: "VENDOR_NAME_IN_CONTRACT", defaultSeverity: "warning", summary: "Technology name in L3 contract identifiers" }
|
|
13957
14168
|
],
|
|
13958
14169
|
check(ctx) {
|
|
13959
|
-
const
|
|
13960
|
-
for (const c of ctx.components) for (const m of c.owns)
|
|
14170
|
+
const ownerOf2 = /* @__PURE__ */ new Map();
|
|
14171
|
+
for (const c of ctx.components) for (const m of c.owns) ownerOf2.set(m, c.id);
|
|
13961
14172
|
const ownershipRoot = (id) => {
|
|
13962
14173
|
const seen = /* @__PURE__ */ new Set();
|
|
13963
14174
|
let cur = id;
|
|
13964
|
-
while (
|
|
14175
|
+
while (ownerOf2.has(cur) && !seen.has(cur)) {
|
|
13965
14176
|
seen.add(cur);
|
|
13966
|
-
cur =
|
|
14177
|
+
cur = ownerOf2.get(cur);
|
|
13967
14178
|
}
|
|
13968
14179
|
return cur;
|
|
13969
14180
|
};
|
|
@@ -14856,6 +15067,8 @@ var init_repository = __esm({
|
|
|
14856
15067
|
init_integration_sim_file();
|
|
14857
15068
|
init_integration_sim_wiring();
|
|
14858
15069
|
init_integration_sim_coverage();
|
|
15070
|
+
init_type_realization();
|
|
15071
|
+
init_unclaimed_source();
|
|
14859
15072
|
init_coupling_health();
|
|
14860
15073
|
init_signature_language_builtins();
|
|
14861
15074
|
init_narrative_language_constructs();
|
|
@@ -15020,6 +15233,11 @@ var init_repository = __esm({
|
|
|
15020
15233
|
integrationSimFileRule,
|
|
15021
15234
|
integrationSimWiringRule,
|
|
15022
15235
|
integrationSimCoverageRule,
|
|
15236
|
+
// The data model's own claim on code, and the question no spec can ask
|
|
15237
|
+
// from the spec side: which files are named by nothing at all. Last in
|
|
15238
|
+
// the family because the second one reads what all the others named.
|
|
15239
|
+
typeRealizationRule,
|
|
15240
|
+
unclaimedSourceRule,
|
|
15023
15241
|
couplingRule,
|
|
15024
15242
|
// Target-language fit in two questions: what a CONTRACT may name, and what
|
|
15025
15243
|
// a NARRATIVE may describe.
|
|
@@ -15052,7 +15270,7 @@ var init_repository = __esm({
|
|
|
15052
15270
|
|
|
15053
15271
|
// src/core/source-analysis.ts
|
|
15054
15272
|
function emptyCodeModel() {
|
|
15055
|
-
return { files: [], projectRoot: "" };
|
|
15273
|
+
return { files: [], projectRoot: "", rootFiles: [] };
|
|
15056
15274
|
}
|
|
15057
15275
|
function stripComments(text2, patterns) {
|
|
15058
15276
|
let out = text2;
|
|
@@ -15292,7 +15510,8 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
15292
15510
|
ts.forEachChild(node, visit);
|
|
15293
15511
|
};
|
|
15294
15512
|
visit(sf);
|
|
15295
|
-
|
|
15513
|
+
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
15514
|
+
return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings, reexportOnly };
|
|
15296
15515
|
}
|
|
15297
15516
|
function resolveRelativeModule(fromFile, specifier) {
|
|
15298
15517
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -15350,15 +15569,65 @@ function looksBinary(buffer) {
|
|
|
15350
15569
|
const probe2 = buffer.subarray(0, Math.min(buffer.length, 4096));
|
|
15351
15570
|
return probe2.includes(0);
|
|
15352
15571
|
}
|
|
15353
|
-
function
|
|
15572
|
+
function walkSourceRoots(sourceRoots, exclude, projectRoot2) {
|
|
15573
|
+
const found = [];
|
|
15574
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15575
|
+
const excluded = exclude.map(pathKey);
|
|
15576
|
+
const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
|
|
15577
|
+
const record2 = (absolute) => {
|
|
15578
|
+
const key = pathKey(path13.relative(projectRoot2, absolute));
|
|
15579
|
+
if (seen.has(key) || isExcluded(key)) return;
|
|
15580
|
+
if (!EXTENSION_LANGUAGE[path13.extname(key).toLowerCase()]) return;
|
|
15581
|
+
seen.add(key);
|
|
15582
|
+
found.push(key);
|
|
15583
|
+
};
|
|
15584
|
+
const descend = (dir) => {
|
|
15585
|
+
let entries;
|
|
15586
|
+
try {
|
|
15587
|
+
entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
15588
|
+
} catch {
|
|
15589
|
+
return;
|
|
15590
|
+
}
|
|
15591
|
+
for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
15592
|
+
const child = path13.join(dir, entry.name);
|
|
15593
|
+
if (entry.isDirectory()) {
|
|
15594
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
15595
|
+
if (isExcluded(pathKey(path13.relative(projectRoot2, child)))) continue;
|
|
15596
|
+
descend(child);
|
|
15597
|
+
} else if (entry.isFile()) {
|
|
15598
|
+
record2(child);
|
|
15599
|
+
}
|
|
15600
|
+
}
|
|
15601
|
+
};
|
|
15602
|
+
for (const root of sourceRoots) {
|
|
15603
|
+
const key = pathKey(root);
|
|
15604
|
+
if (path13.isAbsolute(key) || path13.normalize(key).split(path13.sep)[0] === "..") continue;
|
|
15605
|
+
const absolute = path13.resolve(projectRoot2, key);
|
|
15606
|
+
if (path13.relative(projectRoot2, absolute).startsWith("..")) continue;
|
|
15607
|
+
if (isExcluded(key)) continue;
|
|
15608
|
+
let stat;
|
|
15609
|
+
try {
|
|
15610
|
+
stat = fs10.statSync(absolute);
|
|
15611
|
+
} catch {
|
|
15612
|
+
continue;
|
|
15613
|
+
}
|
|
15614
|
+
if (stat.isDirectory()) descend(absolute);
|
|
15615
|
+
else if (stat.isFile()) record2(absolute);
|
|
15616
|
+
}
|
|
15617
|
+
return found;
|
|
15618
|
+
}
|
|
15619
|
+
function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [], exclude = []) {
|
|
15354
15620
|
const files = [];
|
|
15355
15621
|
const seen = /* @__PURE__ */ new Set();
|
|
15356
15622
|
const exactCache = /* @__PURE__ */ new Map();
|
|
15623
|
+
const rootFiles = walkSourceRoots(sourceRoots, exclude, projectRoot2);
|
|
15357
15624
|
const declaredPaths = [];
|
|
15358
15625
|
for (const impl of implementations) {
|
|
15359
15626
|
declaredPaths.push(...implementationSourceFiles(impl));
|
|
15360
15627
|
if (impl.simPath) declaredPaths.push(impl.simPath);
|
|
15361
15628
|
}
|
|
15629
|
+
for (const type of types) declaredPaths.push(...typeSourceFiles(type));
|
|
15630
|
+
declaredPaths.push(...rootFiles);
|
|
15362
15631
|
for (const declared of declaredPaths) {
|
|
15363
15632
|
const sourcePath = pathKey(declared);
|
|
15364
15633
|
if (seen.has(sourcePath)) continue;
|
|
@@ -15407,7 +15676,8 @@ function buildCodeModel(implementations, projectRoot2) {
|
|
|
15407
15676
|
reexports: [...facts.reexports],
|
|
15408
15677
|
functionComplexity: Object.fromEntries(facts.complexity),
|
|
15409
15678
|
functionCalls: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v]])),
|
|
15410
|
-
topLevelMutableBindings: [...facts.mutableBindings]
|
|
15679
|
+
topLevelMutableBindings: [...facts.mutableBindings],
|
|
15680
|
+
reexportOnly: facts.reexportOnly
|
|
15411
15681
|
};
|
|
15412
15682
|
} catch {
|
|
15413
15683
|
analyzed = analyzeGeneric(text2);
|
|
@@ -15422,7 +15692,7 @@ function buildCodeModel(implementations, projectRoot2) {
|
|
|
15422
15692
|
}
|
|
15423
15693
|
files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
|
|
15424
15694
|
}
|
|
15425
|
-
return { files, projectRoot: projectRoot2 };
|
|
15695
|
+
return { files, projectRoot: projectRoot2, rootFiles };
|
|
15426
15696
|
}
|
|
15427
15697
|
var fs10, path13, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
|
|
15428
15698
|
var init_source_analysis = __esm({
|
|
@@ -22405,7 +22675,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
22405
22675
|
const implementations = loadImplementationSpecs();
|
|
22406
22676
|
const types = loadTypeSpecs();
|
|
22407
22677
|
const surfaceSnapshots = loadSurfaceSnapshots();
|
|
22408
|
-
const
|
|
22678
|
+
const conformance = rules?.conformance;
|
|
22679
|
+
const codeModel = buildCodeModel(
|
|
22680
|
+
implementations,
|
|
22681
|
+
types,
|
|
22682
|
+
getProjectRoot(),
|
|
22683
|
+
conformance?.sourceRoots ?? [],
|
|
22684
|
+
conformance?.exclude ?? []
|
|
22685
|
+
);
|
|
22409
22686
|
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
|
|
22410
22687
|
const statusSnapshot = statusBearing.map((s) => s.status);
|
|
22411
22688
|
for (const s of statusBearing) s.status = "complete";
|
|
@@ -27985,7 +28262,9 @@ function createMcpServer(options = {}) {
|
|
|
27985
28262
|
name: import_zod11.z.string(),
|
|
27986
28263
|
signature: import_zod11.z.string(),
|
|
27987
28264
|
returns: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The type the method answers with")),
|
|
27988
|
-
description: import_zod11.z.string().optional()
|
|
28265
|
+
description: import_zod11.z.string().optional(),
|
|
28266
|
+
sourcePath: import_zod11.z.string().optional().describe("Source file realizing this method when the type's own sourcePath does not hold it \u2014 a pure type method often lives apart from the declaration"),
|
|
28267
|
+
symbol: import_zod11.z.string().optional().describe("Code-level name realizing this method when it differs from the method name, e.g. narrative_step.foreignFields realized by narrativeStepForeignFields")
|
|
27989
28268
|
}).strict()).optional().describe("Pure intrinsic methods only"),
|
|
27990
28269
|
componentClass: import_zod11.z.string().optional().describe("Optional component id that implements or owns this logical entity"),
|
|
27991
28270
|
invariants: import_zod11.z.array(import_zod11.z.object({
|
|
@@ -27994,18 +28273,20 @@ function createMcpServer(options = {}) {
|
|
|
27994
28273
|
}).strict()).optional().describe("Declared domain invariants (entities): every write-effect method of the componentClass must carry a narrative step asserting each (assertsInvariants) \u2014 declarations checked, enforcement never proven"),
|
|
27995
28274
|
database: import_zod11.z.string().optional().describe("Optional database id for table-schema types"),
|
|
27996
28275
|
table: import_zod11.z.string().optional().describe("Optional database table name for table-schema types"),
|
|
27997
|
-
linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type")
|
|
28276
|
+
linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type"),
|
|
28277
|
+
sourcePath: import_zod11.z.string().optional().describe("Source file holding the declaration of this type (project-relative). Naming one turns the type into a claim on code: the file must resolve and the declaration must be anchored in it (UNREALIZED_TYPE)"),
|
|
28278
|
+
symbol: import_zod11.z.string().optional().describe('Code-level name realizing the declaration when it differs from name, e.g. a type named "Invoice Line" declared as InvoiceLine')
|
|
27998
28279
|
};
|
|
27999
28280
|
const typeInputFields = Object.keys(typeInput);
|
|
28000
28281
|
reg(
|
|
28001
28282
|
server,
|
|
28002
28283
|
"sdd_add_type",
|
|
28003
28284
|
{
|
|
28004
|
-
description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument. Re-defining an existing id REPLACES fields/methods/invariants (an omitted list is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
|
|
28285
|
+
description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument. A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
|
|
28005
28286
|
inputSchema: typeInput,
|
|
28006
28287
|
outputSchema: specWriteReceiptOutput
|
|
28007
28288
|
},
|
|
28008
|
-
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity }) => {
|
|
28289
|
+
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol }) => {
|
|
28009
28290
|
try {
|
|
28010
28291
|
const { loadTypeSpec: loadTypeSpec2, saveTypeSpec: saveTypeSpec2 } = requireSpecs();
|
|
28011
28292
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -28031,6 +28312,8 @@ function createMcpServer(options = {}) {
|
|
|
28031
28312
|
...database ? { database } : {},
|
|
28032
28313
|
...table ? { table } : {},
|
|
28033
28314
|
...linkedEntity ? { linkedEntity } : {},
|
|
28315
|
+
...sourcePath ? { sourcePath } : {},
|
|
28316
|
+
...symbol ? { symbol } : {},
|
|
28034
28317
|
createdAt: now,
|
|
28035
28318
|
updatedAt: now
|
|
28036
28319
|
};
|