@intentset/architecture 0.1.0 → 0.3.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../src/regions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAItD,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,aAAa,GACb,QAAQ,GACR,gBAAgB,GAChB,UAAU,GACV,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAEf,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,qEAAqE;IACrE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC;IACvB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,uCAAuC;IACvC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,+CAA+C;IAC/C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,sCAAsC;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,yFAAyF;IACzF,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IAC7D,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,mEAAmE;IACnE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACrC;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAOrD;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,MAAM,EAAE,kBAAkB,GACzB,KAAK,CAwHP;AAED,8HAA8H;AAC9H,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD"}
1
+ {"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../src/regions.ts"],"names":[],"mappings":"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"}
package/dist/regions.js CHANGED
@@ -67,7 +67,9 @@ export function buildModel(graph, registries, tree, config) {
67
67
  index,
68
68
  files: validatePattern(resource.path) === null ? production.filter((file) => matchPattern(resource.path, file)) : [],
69
69
  }));
70
- const underBackend = (path) => matchAny(config.backendRoots, path);
70
+ // An area's backend is backend for AMP001, AMP002 and AMP004 as the unified one is (profile §9).
71
+ const backendPatterns = [...config.backendRoots, ...config.areas.flatMap((area) => area.backend)];
72
+ const underBackend = (path) => matchAny(backendPatterns, path);
71
73
  const regions = new Map();
72
74
  const regionOf = (path) => {
73
75
  const cached = regions.get(path);
@@ -1 +1 @@
1
- {"version":3,"file":"regions.js","sourceRoot":"","sources":["../src/regions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"regions.js","sourceRoot":"","sources":["../src/regions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentset/architecture",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Traceable VSA checks: path claims, import graph, layer matrix, resources, exceptions, baseline.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@intentset/core": "^0.1.0"
31
+ "@intentset/core": "^0.3.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "typescript": "^7.0.2"
package/src/areas.ts ADDED
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Areas (profile §9): a backend split into several Amplify backends, each its
3
+ * own CloudFormation deployment and source API, behind one AppSync Merged API.
4
+ * Nothing here runs unless `.intentset/architecture.yaml` declares areas.
5
+ *
6
+ * AMP007 a slice names a declared area as its domain, and its claims stay in that area
7
+ * AMP008 no area backend imports another; the shared backend package imports no area
8
+ * AMP009 each model, enum and operation is declared in one area's schema (a custom type: a warning)
9
+ * AMP010 no relationship or reference names another area's type
10
+ * AMP011 only the schema bridge calls generateClient (its schema imports are checked with AMP001)
11
+ *
12
+ * AMP012 (cross-area access and an acyclic deploy order) and AMP013 (one
13
+ * authorizer on the Merged API) are review assertions: neither is visible in
14
+ * source, so they are listed for review and never reported as passing.
15
+ */
16
+ import type { AreaConfig } from "./config.ts";
17
+ import { extractSchema, findCalls, type SchemaKind } from "./extract.ts";
18
+ import { type Finding, finding, listSome } from "./finding.ts";
19
+ import type { ImportEdge } from "./imports.ts";
20
+ import { isCode } from "./paths.ts";
21
+ import { compareStrings, matchAny } from "./patterns.ts";
22
+ import type { Model } from "./regions.ts";
23
+
24
+ export const AREA_REVIEW_REQUIRED = [
25
+ "AMP012 backend code reaches another area's data through published resource names and the AWS SDK, and deploy-time dependencies between areas are acyclic: deployment wiring, not source",
26
+ "AMP013 one authorizer on the Merged API decides every request to every area and denies what it does not list: an authorizer's policy, not source",
27
+ ];
28
+
29
+ export interface AreaCheck {
30
+ findings: Finding[];
31
+ /** What could not be read: never a pass (invariant 6). */
32
+ unresolved: string[];
33
+ }
34
+
35
+ /** The area whose backend holds a path, or null. */
36
+ export function backendAreaOf(areas: readonly AreaConfig[], path: string): AreaConfig | null {
37
+ return areas.find((area) => matchAny(area.backend, path)) ?? null;
38
+ }
39
+
40
+ /** The area whose backend or frontend holds a path, or null. */
41
+ export function areaOf(areas: readonly AreaConfig[], path: string): AreaConfig | null {
42
+ return backendAreaOf(areas, path) ?? areas.find((area) => matchAny(area.frontend, path)) ?? null;
43
+ }
44
+
45
+ /** True when the path is one of the declared areas' schema files. */
46
+ export function isAreaSchema(areas: readonly AreaConfig[], path: string): boolean {
47
+ return areas.some((area) => area.schema === path);
48
+ }
49
+
50
+ const OPERATION_KINDS = new Set<SchemaKind | null>(["model", "enum", "query", "mutation", "subscription", null]);
51
+
52
+ export function checkAreas(model: Model, edges: readonly ImportEdge[]): AreaCheck {
53
+ const { areas, sharedBackend, schemaBridge } = model.config;
54
+ const findings: Finding[] = [];
55
+ const unresolved: string[] = [];
56
+ if (areas.length === 0) return { findings, unresolved };
57
+ const names = areas.map((area) => area.name).join(", ");
58
+
59
+ // AMP007: every slice in a declared area, and its claims inside it.
60
+ for (const slice of model.slices) {
61
+ const area = areas.find((candidate) => candidate.name === slice.meta.domain);
62
+ if (area === undefined) {
63
+ findings.push(
64
+ finding({
65
+ code: "AMP007",
66
+ artifact: slice.id,
67
+ path: slice.path,
68
+ field: "/intentset/slice/domain",
69
+ message: `${slice.id} names the domain "${slice.meta.domain}", which is not a declared area; the areas are ${names}.`,
70
+ remediation:
71
+ "Set domain to the area whose backend serves the slice, or declare the area in .intentset/architecture.yaml (profile §9, AMP007).",
72
+ }),
73
+ );
74
+ continue;
75
+ }
76
+ slice.meta.claims.forEach((claim, i) => {
77
+ const foreign = slice.claimFiles[i].filter((file) => {
78
+ const other = areaOf(areas, file);
79
+ return other !== null && other.name !== area.name;
80
+ });
81
+ if (foreign.length === 0) return;
82
+ const others = [...new Set(foreign.map((file) => areaOf(areas, file)?.name as string))].sort(compareStrings);
83
+ findings.push(
84
+ finding({
85
+ code: "AMP007",
86
+ artifact: slice.id,
87
+ path: slice.path,
88
+ field: `/intentset/slice/claims/${i}/path`,
89
+ message: `The ${claim.kind} claim ${claim.path} of ${slice.id}, a slice of area ${area.name}, reaches into area ${others.join(" and ")}: ${listSome(foreign)}.`,
90
+ remediation:
91
+ "Narrow the claim to the slice's own area, or move the slice to the area that holds those files (profile §9, AMP007).",
92
+ paths: foreign,
93
+ }),
94
+ );
95
+ });
96
+ }
97
+
98
+ // AMP008: area backends deploy apart, so neither imports the other; shared backend code imports no area.
99
+ for (const edge of edges) {
100
+ if (edge.to === null || !model.inScope(edge.from)) continue;
101
+ const to = backendAreaOf(areas, edge.to);
102
+ if (to === null) continue;
103
+ const from = backendAreaOf(areas, edge.from);
104
+ const location = { line: edge.line };
105
+ if (from !== null && from.name !== to.name) {
106
+ findings.push(
107
+ finding({
108
+ code: "AMP008",
109
+ artifact: null,
110
+ path: edge.from,
111
+ location,
112
+ message: `${edge.from}, in the ${from.name} backend, imports ${edge.to} from the ${to.name} backend, and area backends deploy separately.`,
113
+ remediation:
114
+ "Move what both need into the shared backend package, and reach the other area's data through its published resource names (profile §9, AMP008, AMP012).",
115
+ edges: [{ from: edge.from, to: edge.to }],
116
+ }),
117
+ );
118
+ } else if (from === null && matchAny(sharedBackend, edge.from)) {
119
+ findings.push(
120
+ finding({
121
+ code: "AMP008",
122
+ artifact: null,
123
+ path: edge.from,
124
+ location,
125
+ message: `${edge.from}, in the shared backend package, imports ${edge.to} from the ${to.name} backend, and the shared package must stay domain-neutral.`,
126
+ remediation:
127
+ "Pass what the shared code needs in as a parameter, or move the code into the area that owns it (profile §9, AMP008).",
128
+ edges: [{ from: edge.from, to: edge.to }],
129
+ }),
130
+ );
131
+ }
132
+ }
133
+
134
+ // AMP009 and AMP010: one owner per name across the areas' schemas, and no reference across them.
135
+ const declared = new Map<string, { area: string; kind: SchemaKind | null; schema: string; line: number }[]>();
136
+ const read = new Map<string, ReturnType<typeof extractSchema>>();
137
+ for (const area of areas) {
138
+ const text = model.files.get(area.schema);
139
+ if (text === undefined) {
140
+ findings.push(
141
+ finding({
142
+ code: "AMP009",
143
+ artifact: null,
144
+ path: area.schema,
145
+ message: `The schema of area ${area.name}, ${area.schema}, is not a file in the repository, so its declarations were not checked.`,
146
+ remediation: "Correct the area's schema path in .intentset/architecture.yaml (profile §9).",
147
+ }),
148
+ );
149
+ continue;
150
+ }
151
+ const extraction = extractSchema(area.schema, text);
152
+ read.set(area.name, extraction);
153
+ if (!extraction.found) {
154
+ unresolved.push(
155
+ `${area.schema}: no <builder>.schema({ ... }) call, so area ${area.name}'s declarations were not read`,
156
+ );
157
+ }
158
+ for (const problem of extraction.problems) unresolved.push(`${area.schema}:${problem.line}: ${problem.message}`);
159
+ for (const declaration of extraction.declarations) {
160
+ const list = declared.get(declaration.name) ?? [];
161
+ list.push({ area: area.name, kind: declaration.kind, schema: area.schema, line: declaration.line });
162
+ declared.set(declaration.name, list);
163
+ }
164
+ }
165
+ for (const name of [...declared.keys()].sort(compareStrings)) {
166
+ const owners = declared.get(name) ?? [];
167
+ const inAreas = [...new Set(owners.map((owner) => owner.area))].sort(compareStrings);
168
+ if (inAreas.length < 2) continue;
169
+ const later = owners.find((owner) => owner.area === inAreas[1]) ?? owners[1];
170
+ const operation = owners.some((owner) => OPERATION_KINDS.has(owner.kind));
171
+ findings.push(
172
+ finding({
173
+ code: "AMP009",
174
+ ...(operation ? {} : { severity: "warning" as const }),
175
+ artifact: null,
176
+ path: later.schema,
177
+ location: { line: later.line },
178
+ message: operation
179
+ ? `${name} is declared in the schemas of areas ${inAreas.join(" and ")}; the Merged API joins every area into one namespace and refuses a field two sources resolve.`
180
+ : `The custom type ${name} is declared in the schemas of areas ${inAreas.join(" and ")}, which share one type namespace in the Merged API.`,
181
+ remediation: operation
182
+ ? `Keep ${name} in the one area whose functions read and write it, and link to it from the others by ID (profile §9, AMP009).`
183
+ : `Prefix the type with its area's name, or name it after the operation that returns it (profile §9, AMP009).`,
184
+ paths: [...new Set(owners.map((owner) => owner.schema))].sort(compareStrings),
185
+ }),
186
+ );
187
+ }
188
+ for (const area of areas) {
189
+ const extraction = read.get(area.name);
190
+ if (extraction === undefined) continue;
191
+ const own = new Set(extraction.declarations.map((declaration) => declaration.name));
192
+ for (const reference of extraction.references) {
193
+ if (own.has(reference.target)) continue;
194
+ const elsewhere = (declared.get(reference.target) ?? []).map((owner) => owner.area);
195
+ if (elsewhere.length === 0) continue;
196
+ findings.push(
197
+ finding({
198
+ code: "AMP010",
199
+ artifact: null,
200
+ path: area.schema,
201
+ location: { line: reference.line },
202
+ message: `${area.schema} ${reference.via}("${reference.target}") names a type that area ${elsewhere.sort(compareStrings)[0]} declares; relationships and references do not cross areas.`,
203
+ remediation:
204
+ "Store the other area's ID as a plain field and resolve it in a function of this area (profile §9, AMP010).",
205
+ }),
206
+ );
207
+ }
208
+ }
209
+
210
+ // AMP011: one client, on the Merged API, made in one place.
211
+ for (const [path, text] of model.files) {
212
+ if (path === schemaBridge || !isCode(path) || model.isTest(path) || !model.inScope(path)) continue;
213
+ if (model.underBackend(path) || matchAny(sharedBackend, path)) continue;
214
+ if (!text.includes("generateClient")) continue;
215
+ const lines = findCalls(path, text, "generateClient");
216
+ if (lines.length === 0) continue;
217
+ findings.push(
218
+ finding({
219
+ code: "AMP011",
220
+ artifact: null,
221
+ path,
222
+ location: { line: lines[0] },
223
+ message:
224
+ schemaBridge === null
225
+ ? `${path} calls generateClient, and this repository declares no schema bridge to make the one client on the Merged API.`
226
+ : `${path} calls generateClient; only the schema bridge ${schemaBridge} makes the client, once, on the Merged API.`,
227
+ remediation:
228
+ "Declare schemaBridge in .intentset/architecture.yaml and import the client from it everywhere else (profile §9, AMP011).",
229
+ }),
230
+ );
231
+ }
232
+ return { findings, unresolved };
233
+ }
package/src/boundaries.ts CHANGED
@@ -38,6 +38,7 @@
38
38
  * so a narrow scope cannot hide its consumers and they do not fail it
39
39
  * (VSA §9). They count toward no edge total and no slice dependency.
40
40
  */
41
+ import { isAreaSchema } from "./areas.ts";
41
42
  import { type Finding, finding, listSome } from "./finding.ts";
42
43
  import type { ImportEdge } from "./imports.ts";
43
44
  import { inSeam, isPure, knownLayer, layerAllows, layerOf } from "./layers.ts";
@@ -281,9 +282,27 @@ export function checkBoundaries(model: Model, edges: readonly ImportEdge[], reso
281
282
  if (!model.inScope(to) && target === undefined) continue;
282
283
  const subject = { edges: [{ from, to }, ...(source && target ? [{ from: source.id, to: target.id }] : [])] };
283
284
 
284
- // 1. AMP001
285
- if (model.underBackend(to) && !model.underBackend(from)) {
285
+ // 1. AMP001, and AMP011 for an area repository's schema bridge (profile §9). The shared backend
286
+ // package is backend code: what it may import is AMP008's question.
287
+ if (model.underBackend(to) && !model.underBackend(from) && !matchAny(config.sharedBackend, from)) {
286
288
  const seam = source !== undefined && inSeam(source, from);
289
+ if (edge.typeOnly && isAreaSchema(config.areas, to)) {
290
+ if (from !== config.schemaBridge) {
291
+ findings.push(
292
+ finding({
293
+ code: "AMP011",
294
+ artifact,
295
+ path: from,
296
+ location,
297
+ message: `${from} ${how(edge)}, the schema of an area, and only the schema bridge${config.schemaBridge === null ? ", which this repository has not declared," : ` ${config.schemaBridge}`} may.`,
298
+ remediation:
299
+ "Import the client and its types from the schema bridge, which joins every area's schema into one type (profile §9, AMP011).",
300
+ ...subject,
301
+ }),
302
+ );
303
+ }
304
+ continue;
305
+ }
287
306
  if (!(seam && matchAny(config.responseParserModules, to))) {
288
307
  findings.push(
289
308
  finding({
package/src/check.ts CHANGED
@@ -11,7 +11,16 @@
11
11
  * migration mode. What could not be checked is listed in `unresolved` and,
12
12
  * for imports, reported as a TS004 warning: never a pass (invariant 6).
13
13
  */
14
- import { type Diagnostic, type Graph, type Level, type Registries, sortDiagnostics } from "@intentset/core";
14
+ import {
15
+ type Diagnostic,
16
+ type Graph,
17
+ type Level,
18
+ type OwnershipEntry,
19
+ type OwnershipReportSection,
20
+ type Registries,
21
+ sortDiagnostics,
22
+ } from "@intentset/core";
23
+ import { AREA_REVIEW_REQUIRED, checkAreas } from "./areas.ts";
15
24
  import { applyBaseline, type BaselineEntry, type Mode } from "./baseline.ts";
16
25
  import { checkBoundaries, checkUnclassified } from "./boundaries.ts";
17
26
  import { checkClaims } from "./claims.ts";
@@ -76,6 +85,14 @@ export interface ArchitectureResult {
76
85
  summary: ArchitectureSummary;
77
86
  /** The resolved import graph, for reports and impact views. */
78
87
  edges: ImportEdge[];
88
+ /**
89
+ * Where every file falls, sorted by path: each production file's region and
90
+ * owner, every test file with the slice its claims match, and nothing for a
91
+ * file in the external region (root configuration, scripts, documents that
92
+ * nothing claims). Out-of-scope files are attributed too: scope decides what
93
+ * is reported, not who owns what.
94
+ */
95
+ ownership: OwnershipEntry[];
79
96
  }
80
97
 
81
98
  /** VSA §2: the rules that are review assertions, snapshot diffs or contract diffs rather than structure. */
@@ -149,6 +166,9 @@ export function checkArchitecture(
149
166
  const boundaries = checkBoundaries(model, imports.edges, resolver);
150
167
  findings.push(...boundaries.findings);
151
168
  findings.push(...checkUnclassified(model));
169
+ const areaCheck = checkAreas(model, imports.edges);
170
+ findings.push(...areaCheck.findings);
171
+ unresolved.push(...areaCheck.unresolved);
152
172
  const dependencies = checkDependencies(model, graph, boundaries.sliceEdges);
153
173
  findings.push(...dependencies.findings);
154
174
 
@@ -191,7 +211,35 @@ export function checkArchitecture(
191
211
  baselined: baseline.baselined,
192
212
  baselineStale: baseline.stale.length,
193
213
  unresolved: [...new Set(unresolved)].sort(compareStrings),
194
- reviewRequired: [...REVIEW_REQUIRED],
214
+ reviewRequired: [...REVIEW_REQUIRED, ...(config.areas.length > 0 ? AREA_REVIEW_REQUIRED : [])],
215
+ };
216
+ const ownership: OwnershipEntry[] = [];
217
+ for (const path of model.files.keys()) {
218
+ if (model.isTest(path)) {
219
+ ownership.push({ path, region: "test", owner: model.testOwner(path) });
220
+ continue;
221
+ }
222
+ const region = model.regionOf(path);
223
+ if (region.kind === "external") continue;
224
+ ownership.push({ path, region: region.kind, owner: region.owner ?? null });
225
+ }
226
+ ownership.sort((a, b) => compareStrings(a.path, b.path));
227
+ return { diagnostics, summary, edges: imports.edges, ownership };
228
+ }
229
+
230
+ /**
231
+ * `reports.ownership` for the export (spec/export.md §4.4): the check's file
232
+ * attribution, stamped with the snapshot it was read at, so a consumer can
233
+ * turn a file from a stack frame into a slice, its behaviors and its owner.
234
+ */
235
+ export function ownershipReport(
236
+ result: ArchitectureResult,
237
+ snapshot: { commit: string | null; graphHash: string },
238
+ ): OwnershipReportSection {
239
+ return {
240
+ commit: snapshot.commit,
241
+ graphHash: snapshot.graphHash,
242
+ files: result.ownership.map((entry) => ({ ...entry })),
243
+ withheld: 0,
195
244
  };
196
- return { diagnostics, summary, edges: imports.edges };
197
245
  }
package/src/claims.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Path and resource ownership (VSA §3, §7; VSA009; profile AMP004).
3
3
  *
4
- * - A nonempty claim that matches no file is VSA009.
4
+ * - A nonempty claim that matches no file is VSA009: a warning while the
5
+ * slice is draft (planned, not yet owned), an error once it is not.
5
6
  * - Two slices resolving one file is VSA009, once per pair of claims, naming
6
7
  * both slices, both claims and the files.
7
8
  * - A file claimed by a slice and recorded as a registry resource has two
@@ -46,13 +47,17 @@ export function checkClaims(model: Model, graph: Graph, registriesPath: string):
46
47
  return;
47
48
  }
48
49
  if (slice.claimFiles[i].length === 0) {
50
+ const planned = slice.artifact.meta.status === "draft";
49
51
  findings.push(
50
52
  finding({
51
53
  code: "VSA009",
54
+ ...(planned ? { severity: "warning" as const } : {}),
52
55
  artifact: slice.id,
53
56
  path: slice.path,
54
57
  field,
55
- message: `The ${claim.kind} claim ${claim.path} of ${slice.id} matches no file in the repository.`,
58
+ message: planned
59
+ ? `The ${claim.kind} claim ${claim.path} of draft slice ${slice.id} matches no file yet; it is planned, and an error once the slice leaves draft.`
60
+ : `The ${claim.kind} claim ${claim.path} of ${slice.id} matches no file in the repository.`,
56
61
  remediation:
57
62
  "Correct the path, add the files it promises, or remove the claim; an empty claim is not ownership (VSA §3).",
58
63
  }),
package/src/config.ts CHANGED
@@ -11,6 +11,21 @@ import { compareStrings, validatePattern } from "./patterns.ts";
11
11
 
12
12
  export const ARCHITECTURE_CONFIG_PATH = ".intentset/architecture.yaml";
13
13
 
14
+ /** One area (profile §9): an Amplify backend of its own and the frontend slices it serves. */
15
+ export interface AreaConfig {
16
+ /** Lowercase name; a slice names its area in `domain`. */
17
+ name: string;
18
+ /** Patterns for the area's backend: its own CloudFormation deployment and source API. */
19
+ backend: string[];
20
+ /** The area's schema file, `data/resource.ts`: the one place its models and operations are declared (AMP009). */
21
+ schema: string;
22
+ /** Patterns for the area's frontend slices, such as src/features/<area>/**. May be empty. */
23
+ frontend: string[];
24
+ }
25
+
26
+ /** AppSync joins at most this many source APIs into one Merged API. */
27
+ export const MAX_AREAS = 10;
28
+
14
29
  export interface ArchitectureConfig {
15
30
  /** Declared scope (VSA §9, Core §11): files outside it are counted, never reported one by one. */
16
31
  scope: string[];
@@ -40,6 +55,12 @@ export interface ArchitectureConfig {
40
55
  layerMatrix: Record<string, string[]>;
41
56
  /** Export conditions tried first when resolving a workspace package's entry, before types, import and default. */
42
57
  conditions: string[];
58
+ /** Areas (profile §9). Empty for a repository with one backend, which none of AMP007 to AMP013 concern. */
59
+ areas: AreaConfig[];
60
+ /** Domain-neutral backend code every area may import, which imports no area (AMP008). */
61
+ sharedBackend: string[];
62
+ /** The one frontend file that may import area schemas, type-only, and call generateClient (AMP011). */
63
+ schemaBridge: string | null;
43
64
  }
44
65
 
45
66
  export const DEFAULT_ARCHITECTURE_CONFIG: ArchitectureConfig = {
@@ -63,6 +84,9 @@ export const DEFAULT_ARCHITECTURE_CONFIG: ArchitectureConfig = {
63
84
  external: ["external", "model"],
64
85
  },
65
86
  conditions: [],
87
+ areas: [],
88
+ sharedBackend: [],
89
+ schemaBridge: null,
66
90
  };
67
91
 
68
92
  const LIST_KEYS = [
@@ -78,6 +102,7 @@ const LIST_KEYS = [
78
102
  "tests",
79
103
  "backendSdks",
80
104
  "conditions",
105
+ "sharedBackend",
81
106
  ] as const;
82
107
 
83
108
  /** Keys whose entries are path patterns and must pass validatePattern. */
@@ -93,9 +118,10 @@ const PATTERN_KEYS = new Set<string>([
93
118
  "ignore",
94
119
  "tests",
95
120
  "screensGlob",
121
+ "sharedBackend",
96
122
  ]);
97
123
 
98
- const KNOWN_KEYS = [...LIST_KEYS, "screensGlob", "layerMatrix"].sort(compareStrings);
124
+ const KNOWN_KEYS = [...LIST_KEYS, "screensGlob", "layerMatrix", "areas", "schemaBridge"].sort(compareStrings);
99
125
 
100
126
  /** The defaults with a partial configuration laid over them; a key given replaces the default whole. */
101
127
  export function resolveConfig(...layers: (Partial<ArchitectureConfig> | undefined)[]): ArchitectureConfig {
@@ -166,6 +192,25 @@ export function readArchitectureConfig(
166
192
  } else config.screensGlob = value as string;
167
193
  continue;
168
194
  }
195
+ if (key === "schemaBridge") {
196
+ const why = typeof value === "string" ? literalPath(value) : "is not a string";
197
+ if (why !== null) {
198
+ report(
199
+ field,
200
+ at(field),
201
+ `schemaBridge ${why}.`,
202
+ "Name the one file, such as src/infrastructure/amplify/client.ts.",
203
+ );
204
+ } else config.schemaBridge = value as string;
205
+ continue;
206
+ }
207
+ if (key === "areas") {
208
+ const read = readAreas(value);
209
+ if (typeof read === "string") {
210
+ report(field, at(field), `areas ${read}.`, AREAS_REMEDIATION);
211
+ } else config.areas = read;
212
+ continue;
213
+ }
169
214
  if (key === "layerMatrix") {
170
215
  const matrix = readMatrix(value);
171
216
  if (matrix === null) {
@@ -210,3 +255,49 @@ function readMatrix(value: unknown): Record<string, string[]> | null {
210
255
  }
211
256
  return matrix;
212
257
  }
258
+
259
+ const AREAS_REMEDIATION =
260
+ "Write each area as a mapping of name, backend (a list of patterns), schema (one file) and frontend (a list of patterns), as profile §9 shows.";
261
+ const AREA_NAME = /^[a-z][a-z0-9-]*$/;
262
+
263
+ /** `areas` as a list of AreaConfig, or why it is not one. */
264
+ function readAreas(value: unknown): AreaConfig[] | string {
265
+ if (!Array.isArray(value)) return "must be a list of areas";
266
+ if (value.length > MAX_AREAS) {
267
+ return `names ${value.length} areas, and an AppSync Merged API joins at most ${MAX_AREAS} source APIs`;
268
+ }
269
+ const areas: AreaConfig[] = [];
270
+ for (const [i, entry] of value.entries()) {
271
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return `entry ${i} is not a mapping`;
272
+ const record = entry as Record<string, unknown>;
273
+ const unknown = Object.keys(record).filter((key) => !["name", "backend", "schema", "frontend"].includes(key));
274
+ if (unknown.length > 0) return `entry ${i} has the unknown key \`${unknown[0]}\``;
275
+ const { name, backend, schema, frontend = [] } = record;
276
+ if (typeof name !== "string" || !AREA_NAME.test(name)) return `entry ${i} needs a lowercase name such as platform`;
277
+ if (areas.some((area) => area.name === name)) return `name the area ${name} twice`;
278
+ const patterns = (list: unknown, what: string, required: boolean): string[] | string => {
279
+ if (!Array.isArray(list) || !list.every((item) => typeof item === "string" && item !== "")) {
280
+ return `entry ${name} needs ${what} as a list of patterns`;
281
+ }
282
+ if (required && list.length === 0) return `entry ${name} needs at least one ${what} pattern`;
283
+ const bad = (list as string[]).find((pattern) => validatePattern(pattern) !== null);
284
+ return bad === undefined
285
+ ? [...(list as string[])]
286
+ : `entry ${name}: the ${what} pattern ${bad} ${validatePattern(bad)}`;
287
+ };
288
+ const backendList = patterns(backend, "backend", true);
289
+ if (typeof backendList === "string") return backendList;
290
+ const frontendList = patterns(frontend, "frontend", false);
291
+ if (typeof frontendList === "string") return frontendList;
292
+ const why = typeof schema === "string" ? literalPath(schema) : "is missing";
293
+ if (why !== null) return `entry ${name}: schema ${why}`;
294
+ areas.push({ name, backend: backendList, schema: schema as string, frontend: frontendList });
295
+ }
296
+ return areas;
297
+ }
298
+
299
+ /** Null for one repository-relative file path with no wildcard, or why it is not one. */
300
+ function literalPath(value: string): string | null {
301
+ if (value.includes("*")) return "must name one file, not a pattern";
302
+ return validatePattern(value);
303
+ }