@principal-ai/subsystems-core 0.34.1 → 0.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/types/subsystem-model.d.ts +7 -30
- package/dist/types/subsystem-model.d.ts.map +1 -1
- package/dist/types/subsystem-model.js +2 -0
- package/dist/types/subsystem-model.js.map +1 -1
- package/dist/validation.d.ts +24 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +85 -0
- package/dist/validation.js.map +1 -0
- package/package.json +4 -3
- package/schemas/subsystem-model.schema.json +3 -3
- package/src/index.ts +6 -0
- package/src/types/subsystem-model.ts +7 -33
- package/src/validation.test.ts +99 -0
- package/src/validation.ts +99 -0
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,6 @@
|
|
|
11
11
|
* import { ... } from '@principal-ai/subsystems-core/pipeline'
|
|
12
12
|
*/
|
|
13
13
|
export * from './types';
|
|
14
|
+
export { validateSubsystemModelCrossField } from './validation';
|
|
15
|
+
export type { SubsystemValidationProblem } from './validation';
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,SAAS,CAAC;AAKxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,5 +26,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
26
26
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.validateSubsystemModelCrossField = void 0;
|
|
29
30
|
__exportStar(require("./types"), exports);
|
|
31
|
+
// Cross-field validation (the rules the JSON Schema can't express). Structural
|
|
32
|
+
// (schema) validation is enforced per surface against
|
|
33
|
+
// `schemas/subsystem-model.schema.json`.
|
|
34
|
+
var validation_1 = require("./validation");
|
|
35
|
+
Object.defineProperty(exports, "validateSubsystemModelCrossField", { enumerable: true, get: function () { return validation_1.validateSubsystemModelCrossField; } });
|
|
30
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AAExB,+EAA+E;AAC/E,sDAAsD;AACtD,yCAAyC;AACzC,2CAAgE;AAAvD,8HAAA,gCAAgC,OAAA"}
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* framework pattern it plays, role = where it sits, process = where it runs,
|
|
16
16
|
* module = which source file/module the export belongs to,
|
|
17
17
|
* proposed = not yet in source (design / migration placeholder).
|
|
18
|
+
* Package (repo) frames are derived from component `purl` when a graph spans
|
|
19
|
+
* multiple repos — there is no separate `package` field.
|
|
18
20
|
* `symbol` is the code identity; `name` is the display label.
|
|
19
21
|
*/
|
|
20
22
|
/** What the node IS as a declaration. `module` is not an authored construct —
|
|
@@ -206,7 +208,7 @@ export interface SubsystemComponent {
|
|
|
206
208
|
construct: SubsystemConstruct;
|
|
207
209
|
/** Repo-root-relative source path. */
|
|
208
210
|
file: string;
|
|
209
|
-
/** PURL for repo/package grouping. */
|
|
211
|
+
/** PURL for repo/package grouping. Multi-repo graphs draw package frames from distinct purl repo keys. */
|
|
210
212
|
purl: string;
|
|
211
213
|
purpose?: string;
|
|
212
214
|
role?: SubsystemComponentRole;
|
|
@@ -304,10 +306,6 @@ export interface SubsystemWalkthrough {
|
|
|
304
306
|
title: string;
|
|
305
307
|
steps: SubsystemWalkthroughStep[];
|
|
306
308
|
}
|
|
307
|
-
export interface SubsystemRepoRef {
|
|
308
|
-
owner: string;
|
|
309
|
-
name: string;
|
|
310
|
-
}
|
|
311
309
|
/**
|
|
312
310
|
* Portable subsystem model — the shareable standard.
|
|
313
311
|
* No host paths, provenance, document-level repo, store ids, or verification.
|
|
@@ -328,33 +326,12 @@ export interface SubsystemModelDocument {
|
|
|
328
326
|
walkthroughs?: SubsystemWalkthrough[];
|
|
329
327
|
}
|
|
330
328
|
/**
|
|
331
|
-
*
|
|
332
|
-
* Not part of the portable standard — viewers and stores own this envelope.
|
|
333
|
-
*/
|
|
334
|
-
export interface SubsystemModelHostBinding {
|
|
335
|
-
/** Provenance of the model (e.g. `agent:<name>`, `manual`). */
|
|
336
|
-
source?: string;
|
|
337
|
-
/**
|
|
338
|
-
* Optional document-level repo summary for host indexes/listings.
|
|
339
|
-
* Portable identity lives on each component's `purl`.
|
|
340
|
-
*/
|
|
341
|
-
repo?: SubsystemRepoRef;
|
|
342
|
-
/**
|
|
343
|
-
* Local filesystem root component `file` paths resolve against.
|
|
344
|
-
* Opt-in; sandboxed reads for single-repo models.
|
|
345
|
-
*/
|
|
346
|
-
repoRoot?: string;
|
|
347
|
-
/**
|
|
348
|
-
* Per-repo local roots for multi-repo models, keyed by purl repo key
|
|
349
|
-
* (`pkg:github/owner/name`, fragment stripped).
|
|
350
|
-
*/
|
|
351
|
-
repoRoots?: Record<string, string>;
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Hydrated model: portable document + host binding + optional store metadata.
|
|
329
|
+
* Hydrated model: portable document + store metadata.
|
|
355
330
|
* Shape used after a host accepts/persists a model for viewing on a machine.
|
|
331
|
+
* Local path binding is resolved from Alexandria (via each component's purl)
|
|
332
|
+
* and repo identity is derived from purls — neither is stored here.
|
|
356
333
|
*/
|
|
357
|
-
export interface SubsystemModelHydrated extends SubsystemModelDocument
|
|
334
|
+
export interface SubsystemModelHydrated extends SubsystemModelDocument {
|
|
358
335
|
/** Store-assigned id when persisted. */
|
|
359
336
|
id?: string;
|
|
360
337
|
createdAt?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subsystem-model.d.ts","sourceRoot":"","sources":["../../src/types/subsystem-model.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"subsystem-model.d.ts","sourceRoot":"","sources":["../../src/types/subsystem-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* framework pattern it plays, role = where it sits, process = where it runs,
|
|
17
17
|
* module = which source file/module the export belongs to,
|
|
18
18
|
* proposed = not yet in source (design / migration placeholder).
|
|
19
|
+
* Package (repo) frames are derived from component `purl` when a graph spans
|
|
20
|
+
* multiple repos — there is no separate `package` field.
|
|
19
21
|
* `symbol` is the code identity; `name` is the display label.
|
|
20
22
|
*/
|
|
21
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subsystem-model.js","sourceRoot":"","sources":["../../src/types/subsystem-model.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"subsystem-model.js","sourceRoot":"","sources":["../../src/types/subsystem-model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AA+YH;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,KAIT,CAAC;IACF,OAAO,CACL,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAC3B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;QAC3B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAC3B,CAAC;AACJ,CAAC;AAZD,4DAYC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,GAA2B;IAE3B,MAAM,GAAG,GAA2B;QAClC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAC;IACF,IAAI,GAAG,CAAC,OAAO;QAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC3C,IAAI,GAAG,CAAC,WAAW;QAAE,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IACvD,IAAI,GAAG,CAAC,YAAY;QAAE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC;AAZD,gDAYC;AAED,6EAA6E;AAC7E,SAAgB,kBAAkB,CAChC,IAAY,EACZ,EAAU,EACV,SAAiC;IAEjC,OAAO,GAAG,IAAI,KAAK,SAAS,MAAM,EAAE,EAAE,CAAC;AACzC,CAAC;AAND,gDAMC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,GAGhC;IACC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkC,CAAC;IACvD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE;QACnC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,kBAAkB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YACjB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;gBACX,EAAE;gBACF,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,SAAS,EAAE,CAAC,CAAC,YAAY;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;aACb,CAAC,CAAC;SACJ;KACF;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,YAAY,IAAI,EAAE,EAAE;QACtC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE;YAC1B,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAClE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;gBACjB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;oBACX,EAAE;oBACF,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC,CAAC;aACJ;SACF;KACF;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5B,CAAC;AA/BD,4CA+BC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-field validation for a subsystem model document.
|
|
3
|
+
*
|
|
4
|
+
* These are the rules the JSON Schema (`schemas/subsystem-model.schema.json`)
|
|
5
|
+
* cannot express — anything that spans fields or arrays: id uniqueness,
|
|
6
|
+
* referential integrity between relations/walkthroughs and components, and the
|
|
7
|
+
* `module` implies `file` invariant. Structural checks (types, `required`,
|
|
8
|
+
* enums, ranges, closed objects) belong to the schema and are enforced per
|
|
9
|
+
* surface; this module owns only what the schema can't.
|
|
10
|
+
*
|
|
11
|
+
* Pure and dependency-free (browser-safe) — no JSON Schema validator here.
|
|
12
|
+
*/
|
|
13
|
+
import type { SubsystemModelDocument } from './types/subsystem-model';
|
|
14
|
+
export interface SubsystemValidationProblem {
|
|
15
|
+
/** JSON-pointer-ish location, e.g. `/components/2` or `/relations/0/from`. */
|
|
16
|
+
path: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate the cross-field rules of a subsystem model document. Returns an
|
|
21
|
+
* empty array when the document is consistent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateSubsystemModelCrossField(doc: SubsystemModelDocument): SubsystemValidationProblem[];
|
|
24
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,0BAA0B;IACzC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,sBAAsB,GAC1B,0BAA0B,EAAE,CA+D9B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-field validation for a subsystem model document.
|
|
4
|
+
*
|
|
5
|
+
* These are the rules the JSON Schema (`schemas/subsystem-model.schema.json`)
|
|
6
|
+
* cannot express — anything that spans fields or arrays: id uniqueness,
|
|
7
|
+
* referential integrity between relations/walkthroughs and components, and the
|
|
8
|
+
* `module` implies `file` invariant. Structural checks (types, `required`,
|
|
9
|
+
* enums, ranges, closed objects) belong to the schema and are enforced per
|
|
10
|
+
* surface; this module owns only what the schema can't.
|
|
11
|
+
*
|
|
12
|
+
* Pure and dependency-free (browser-safe) — no JSON Schema validator here.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.validateSubsystemModelCrossField = void 0;
|
|
16
|
+
function isUngrounded(c) {
|
|
17
|
+
return c.construct === 'external' || c.proposed === true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate the cross-field rules of a subsystem model document. Returns an
|
|
21
|
+
* empty array when the document is consistent.
|
|
22
|
+
*/
|
|
23
|
+
function validateSubsystemModelCrossField(doc) {
|
|
24
|
+
const problems = [];
|
|
25
|
+
const components = doc.components ?? [];
|
|
26
|
+
const relations = doc.relations ?? [];
|
|
27
|
+
const walkthroughs = doc.walkthroughs ?? [];
|
|
28
|
+
// Component ids must be unique, and the set is the referential target for
|
|
29
|
+
// relations and walkthrough steps.
|
|
30
|
+
const ids = new Set();
|
|
31
|
+
components.forEach((c, i) => {
|
|
32
|
+
if (ids.has(c.id)) {
|
|
33
|
+
problems.push({
|
|
34
|
+
path: `/components/${i}/id`,
|
|
35
|
+
message: `component ${JSON.stringify(c.id)}: duplicate id`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
ids.add(c.id);
|
|
40
|
+
}
|
|
41
|
+
// A grounded component that claims a `module` needs a `file` to ground the
|
|
42
|
+
// frame. Externals / proposed carry no file by design.
|
|
43
|
+
const module = typeof c.module === 'string' ? c.module.trim() : '';
|
|
44
|
+
const file = typeof c.file === 'string' ? c.file.trim() : '';
|
|
45
|
+
if (module && !file && !isUngrounded(c)) {
|
|
46
|
+
problems.push({
|
|
47
|
+
path: `/components/${i}/module`,
|
|
48
|
+
message: `component ${JSON.stringify(c.id)}: module ${JSON.stringify(module)} is set but file is empty — a module frame needs a file to ground it (mark the component proposed if it is not placed yet).`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
relations.forEach((r, i) => {
|
|
53
|
+
if (!ids.has(r.from)) {
|
|
54
|
+
problems.push({
|
|
55
|
+
path: `/relations/${i}/from`,
|
|
56
|
+
message: `relation ${JSON.stringify(r.id)}: from ${JSON.stringify(r.from)} does not match any component id`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (!ids.has(r.to)) {
|
|
60
|
+
problems.push({
|
|
61
|
+
path: `/relations/${i}/to`,
|
|
62
|
+
message: `relation ${JSON.stringify(r.id)}: to ${JSON.stringify(r.to)} does not match any component id`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
walkthroughs.forEach((w, wi) => {
|
|
67
|
+
(w.steps ?? []).forEach((step, si) => {
|
|
68
|
+
if (!ids.has(step.from)) {
|
|
69
|
+
problems.push({
|
|
70
|
+
path: `/walkthroughs/${wi}/steps/${si}/from`,
|
|
71
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} from ${JSON.stringify(step.from)} does not match any component id`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (!ids.has(step.to)) {
|
|
75
|
+
problems.push({
|
|
76
|
+
path: `/walkthroughs/${wi}/steps/${si}/to`,
|
|
77
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} to ${JSON.stringify(step.to)} does not match any component id`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
return problems;
|
|
83
|
+
}
|
|
84
|
+
exports.validateSubsystemModelCrossField = validateSubsystemModelCrossField;
|
|
85
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAcH,SAAS,YAAY,CAAC,CAAqB;IACzC,OAAO,CAAC,CAAC,SAAS,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAgB,gCAAgC,CAC9C,GAA2B;IAE3B,MAAM,QAAQ,GAAiC,EAAE,CAAC;IAClD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;IACtC,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IAE5C,0EAA0E;IAC1E,mCAAmC;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC1B,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;YACjB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,eAAe,CAAC,KAAK;gBAC3B,OAAO,EAAE,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB;aAC3D,CAAC,CAAC;SACJ;aAAM;YACL,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACf;QACD,2EAA2E;QAC3E,uDAAuD;QACvD,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,IAAI,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;YACvC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,eAAe,CAAC,SAAS;gBAC/B,OAAO,EAAE,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,6HAA6H;aAC1M,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;YACpB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,cAAc,CAAC,OAAO;gBAC5B,OAAO,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,kCAAkC;aAC5G,CAAC,CAAC;SACJ;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,cAAc,CAAC,KAAK;gBAC1B,OAAO,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,kCAAkC;aACxG,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,OAAO,CAAC,CAAC,CAAuB,EAAE,EAAE,EAAE,EAAE;QACnD,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE;YACnC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACvB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO;oBAC5C,OAAO,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC;iBAC7H,CAAC,CAAC;aACJ;YACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;gBACrB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK;oBAC1C,OAAO,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,kCAAkC;iBACzH,CAAC,CAAC;aACJ;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAjED,4EAiEC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principal-ai/subsystems-core",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.2",
|
|
4
4
|
"description": "Subsystem model types and agent-session tooling for Principal AI",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"import": "./dist/opencode/pipeline.js",
|
|
23
23
|
"require": "./dist/opencode/pipeline.js",
|
|
24
24
|
"default": "./dist/opencode/pipeline.js"
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
|
+
"./schemas/subsystem-model.schema.json": "./schemas/subsystem-model.schema.json"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
29
|
"dist",
|
|
@@ -64,4 +65,4 @@
|
|
|
64
65
|
"access": "public",
|
|
65
66
|
"registry": "https://registry.npmjs.org/"
|
|
66
67
|
}
|
|
67
|
-
}
|
|
68
|
+
}
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"purl": {
|
|
151
151
|
"type": "string",
|
|
152
152
|
"minLength": 1,
|
|
153
|
-
"description": "PURL identifying the repo or package (for subgraph grouping), e.g. `pkg:github/owner/name#path/to/file.ts`."
|
|
153
|
+
"description": "PURL identifying the repo or package (for subgraph grouping), e.g. `pkg:github/owner/name#path/to/file.ts`. When a graph spans multiple repos, distinct purl repo keys become package boundary frames (package → process → module nesting). Single-repo graphs do not draw package frames by default."
|
|
154
154
|
},
|
|
155
155
|
"symbol": {
|
|
156
156
|
"type": "string",
|
|
@@ -182,11 +182,11 @@
|
|
|
182
182
|
},
|
|
183
183
|
"process": {
|
|
184
184
|
"type": "string",
|
|
185
|
-
"description": "Runtime process / deployment unit membership (e.g. `principal-studio/host`). Nodes sharing a process are drawn in one boundary region; nodes without one sit outside every boundary."
|
|
185
|
+
"description": "Runtime process / deployment unit membership (e.g. `principal-studio/host`). Nodes sharing a process are drawn in one boundary region; nodes without one sit outside every process boundary. Nests inside a package frame when the graph is multi-repo."
|
|
186
186
|
},
|
|
187
187
|
"module": {
|
|
188
188
|
"type": "string",
|
|
189
|
-
"description": "Source-module membership — which file/module this export belongs to (e.g. `src/session/transcript.ts`). Nodes sharing a module are drawn in one boundary frame. Prefer anchoring each export as its real construct and setting module, rather than a module construct."
|
|
189
|
+
"description": "Source-module membership — which file/module this export belongs to (e.g. `src/session/transcript.ts`). Nodes sharing a module are drawn in one boundary frame. Prefer anchoring each export as its real construct and setting module, rather than a module construct. Nests under process (and package when multi-repo)."
|
|
190
190
|
},
|
|
191
191
|
"layer": {
|
|
192
192
|
"type": "integer",
|
package/src/index.ts
CHANGED
|
@@ -12,3 +12,9 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export * from './types';
|
|
15
|
+
|
|
16
|
+
// Cross-field validation (the rules the JSON Schema can't express). Structural
|
|
17
|
+
// (schema) validation is enforced per surface against
|
|
18
|
+
// `schemas/subsystem-model.schema.json`.
|
|
19
|
+
export { validateSubsystemModelCrossField } from './validation';
|
|
20
|
+
export type { SubsystemValidationProblem } from './validation';
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* framework pattern it plays, role = where it sits, process = where it runs,
|
|
16
16
|
* module = which source file/module the export belongs to,
|
|
17
17
|
* proposed = not yet in source (design / migration placeholder).
|
|
18
|
+
* Package (repo) frames are derived from component `purl` when a graph spans
|
|
19
|
+
* multiple repos — there is no separate `package` field.
|
|
18
20
|
* `symbol` is the code identity; `name` is the display label.
|
|
19
21
|
*/
|
|
20
22
|
|
|
@@ -276,7 +278,7 @@ export interface SubsystemComponent {
|
|
|
276
278
|
construct: SubsystemConstruct;
|
|
277
279
|
/** Repo-root-relative source path. */
|
|
278
280
|
file: string;
|
|
279
|
-
/** PURL for repo/package grouping. */
|
|
281
|
+
/** PURL for repo/package grouping. Multi-repo graphs draw package frames from distinct purl repo keys. */
|
|
280
282
|
purl: string;
|
|
281
283
|
purpose?: string;
|
|
282
284
|
role?: SubsystemComponentRole;
|
|
@@ -379,11 +381,6 @@ export interface SubsystemWalkthrough {
|
|
|
379
381
|
steps: SubsystemWalkthroughStep[];
|
|
380
382
|
}
|
|
381
383
|
|
|
382
|
-
export interface SubsystemRepoRef {
|
|
383
|
-
owner: string;
|
|
384
|
-
name: string;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
384
|
/**
|
|
388
385
|
* Portable subsystem model — the shareable standard.
|
|
389
386
|
* No host paths, provenance, document-level repo, store ids, or verification.
|
|
@@ -406,35 +403,12 @@ export interface SubsystemModelDocument {
|
|
|
406
403
|
}
|
|
407
404
|
|
|
408
405
|
/**
|
|
409
|
-
*
|
|
410
|
-
* Not part of the portable standard — viewers and stores own this envelope.
|
|
411
|
-
*/
|
|
412
|
-
export interface SubsystemModelHostBinding {
|
|
413
|
-
/** Provenance of the model (e.g. `agent:<name>`, `manual`). */
|
|
414
|
-
source?: string;
|
|
415
|
-
/**
|
|
416
|
-
* Optional document-level repo summary for host indexes/listings.
|
|
417
|
-
* Portable identity lives on each component's `purl`.
|
|
418
|
-
*/
|
|
419
|
-
repo?: SubsystemRepoRef;
|
|
420
|
-
/**
|
|
421
|
-
* Local filesystem root component `file` paths resolve against.
|
|
422
|
-
* Opt-in; sandboxed reads for single-repo models.
|
|
423
|
-
*/
|
|
424
|
-
repoRoot?: string;
|
|
425
|
-
/**
|
|
426
|
-
* Per-repo local roots for multi-repo models, keyed by purl repo key
|
|
427
|
-
* (`pkg:github/owner/name`, fragment stripped).
|
|
428
|
-
*/
|
|
429
|
-
repoRoots?: Record<string, string>;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Hydrated model: portable document + host binding + optional store metadata.
|
|
406
|
+
* Hydrated model: portable document + store metadata.
|
|
434
407
|
* Shape used after a host accepts/persists a model for viewing on a machine.
|
|
408
|
+
* Local path binding is resolved from Alexandria (via each component's purl)
|
|
409
|
+
* and repo identity is derived from purls — neither is stored here.
|
|
435
410
|
*/
|
|
436
|
-
export interface SubsystemModelHydrated
|
|
437
|
-
extends SubsystemModelDocument, SubsystemModelHostBinding {
|
|
411
|
+
export interface SubsystemModelHydrated extends SubsystemModelDocument {
|
|
438
412
|
/** Store-assigned id when persisted. */
|
|
439
413
|
id?: string;
|
|
440
414
|
createdAt?: string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { validateSubsystemModelCrossField } from './validation';
|
|
3
|
+
import type { SubsystemModelDocument } from './types/subsystem-model';
|
|
4
|
+
|
|
5
|
+
function comp(
|
|
6
|
+
id: string,
|
|
7
|
+
extra: Partial<SubsystemModelDocument['components'][number]> = {},
|
|
8
|
+
) {
|
|
9
|
+
return {
|
|
10
|
+
id,
|
|
11
|
+
name: id,
|
|
12
|
+
construct: 'function' as const,
|
|
13
|
+
file: `src/${id}.ts`,
|
|
14
|
+
purl: 'pkg:github/acme/app',
|
|
15
|
+
...extra,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function doc(partial: Partial<SubsystemModelDocument>): SubsystemModelDocument {
|
|
20
|
+
return {
|
|
21
|
+
title: 't',
|
|
22
|
+
components: [],
|
|
23
|
+
relations: [],
|
|
24
|
+
...partial,
|
|
25
|
+
} as SubsystemModelDocument;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('validateSubsystemModelCrossField', () => {
|
|
29
|
+
test('accepts a consistent document', () => {
|
|
30
|
+
const d = doc({
|
|
31
|
+
components: [comp('a'), comp('b')],
|
|
32
|
+
relations: [{ id: 'r1', from: 'a', to: 'b', relationType: 'imports' }],
|
|
33
|
+
walkthroughs: [
|
|
34
|
+
{
|
|
35
|
+
id: 'w1',
|
|
36
|
+
title: 'flow',
|
|
37
|
+
steps: [{ from: 'a', to: 'b', mechanism: 'calls', file: 'src/a.ts', line: 1 }],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
expect(validateSubsystemModelCrossField(d)).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('flags duplicate component ids', () => {
|
|
45
|
+
const problems = validateSubsystemModelCrossField(
|
|
46
|
+
doc({ components: [comp('a'), comp('a')] }),
|
|
47
|
+
);
|
|
48
|
+
expect(problems).toHaveLength(1);
|
|
49
|
+
expect(problems[0]!.message).toContain('duplicate id');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('flags relation endpoints that reference no component', () => {
|
|
53
|
+
const problems = validateSubsystemModelCrossField(
|
|
54
|
+
doc({
|
|
55
|
+
components: [comp('a')],
|
|
56
|
+
relations: [{ id: 'r1', from: 'a', to: 'ghost', relationType: 'imports' }],
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
expect(problems).toHaveLength(1);
|
|
60
|
+
expect(problems[0]!.path).toBe('/relations/0/to');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('flags walkthrough step endpoints that reference no component', () => {
|
|
64
|
+
const problems = validateSubsystemModelCrossField(
|
|
65
|
+
doc({
|
|
66
|
+
components: [comp('a')],
|
|
67
|
+
walkthroughs: [
|
|
68
|
+
{
|
|
69
|
+
id: 'w1',
|
|
70
|
+
title: 'flow',
|
|
71
|
+
steps: [{ from: 'ghost', to: 'a', mechanism: 'calls', file: 'src/a.ts', line: 1 }],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
expect(problems).toHaveLength(1);
|
|
77
|
+
expect(problems[0]!.path).toBe('/walkthroughs/0/steps/0/from');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('module implies file, exempting external/proposed', () => {
|
|
81
|
+
const bad = validateSubsystemModelCrossField(
|
|
82
|
+
doc({ components: [comp('a', { module: 'src/host', file: '' })] }),
|
|
83
|
+
);
|
|
84
|
+
expect(bad).toHaveLength(1);
|
|
85
|
+
expect(bad[0]!.message).toContain('file is empty');
|
|
86
|
+
|
|
87
|
+
expect(
|
|
88
|
+
validateSubsystemModelCrossField(
|
|
89
|
+
doc({
|
|
90
|
+
components: [
|
|
91
|
+
comp('ext', { module: 'pkg:npm/x', construct: 'external', file: '' }),
|
|
92
|
+
comp('plan', { module: 'src/host', file: '', proposed: true }),
|
|
93
|
+
comp('ok', { module: 'src/host', file: 'src/host/main.ts' }),
|
|
94
|
+
],
|
|
95
|
+
}),
|
|
96
|
+
),
|
|
97
|
+
).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-field validation for a subsystem model document.
|
|
3
|
+
*
|
|
4
|
+
* These are the rules the JSON Schema (`schemas/subsystem-model.schema.json`)
|
|
5
|
+
* cannot express — anything that spans fields or arrays: id uniqueness,
|
|
6
|
+
* referential integrity between relations/walkthroughs and components, and the
|
|
7
|
+
* `module` implies `file` invariant. Structural checks (types, `required`,
|
|
8
|
+
* enums, ranges, closed objects) belong to the schema and are enforced per
|
|
9
|
+
* surface; this module owns only what the schema can't.
|
|
10
|
+
*
|
|
11
|
+
* Pure and dependency-free (browser-safe) — no JSON Schema validator here.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
SubsystemModelDocument,
|
|
16
|
+
SubsystemComponent,
|
|
17
|
+
SubsystemWalkthrough,
|
|
18
|
+
} from './types/subsystem-model';
|
|
19
|
+
|
|
20
|
+
export interface SubsystemValidationProblem {
|
|
21
|
+
/** JSON-pointer-ish location, e.g. `/components/2` or `/relations/0/from`. */
|
|
22
|
+
path: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isUngrounded(c: SubsystemComponent): boolean {
|
|
27
|
+
return c.construct === 'external' || c.proposed === true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate the cross-field rules of a subsystem model document. Returns an
|
|
32
|
+
* empty array when the document is consistent.
|
|
33
|
+
*/
|
|
34
|
+
export function validateSubsystemModelCrossField(
|
|
35
|
+
doc: SubsystemModelDocument,
|
|
36
|
+
): SubsystemValidationProblem[] {
|
|
37
|
+
const problems: SubsystemValidationProblem[] = [];
|
|
38
|
+
const components = doc.components ?? [];
|
|
39
|
+
const relations = doc.relations ?? [];
|
|
40
|
+
const walkthroughs = doc.walkthroughs ?? [];
|
|
41
|
+
|
|
42
|
+
// Component ids must be unique, and the set is the referential target for
|
|
43
|
+
// relations and walkthrough steps.
|
|
44
|
+
const ids = new Set<string>();
|
|
45
|
+
components.forEach((c, i) => {
|
|
46
|
+
if (ids.has(c.id)) {
|
|
47
|
+
problems.push({
|
|
48
|
+
path: `/components/${i}/id`,
|
|
49
|
+
message: `component ${JSON.stringify(c.id)}: duplicate id`,
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
ids.add(c.id);
|
|
53
|
+
}
|
|
54
|
+
// A grounded component that claims a `module` needs a `file` to ground the
|
|
55
|
+
// frame. Externals / proposed carry no file by design.
|
|
56
|
+
const module = typeof c.module === 'string' ? c.module.trim() : '';
|
|
57
|
+
const file = typeof c.file === 'string' ? c.file.trim() : '';
|
|
58
|
+
if (module && !file && !isUngrounded(c)) {
|
|
59
|
+
problems.push({
|
|
60
|
+
path: `/components/${i}/module`,
|
|
61
|
+
message: `component ${JSON.stringify(c.id)}: module ${JSON.stringify(module)} is set but file is empty — a module frame needs a file to ground it (mark the component proposed if it is not placed yet).`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
relations.forEach((r, i) => {
|
|
67
|
+
if (!ids.has(r.from)) {
|
|
68
|
+
problems.push({
|
|
69
|
+
path: `/relations/${i}/from`,
|
|
70
|
+
message: `relation ${JSON.stringify(r.id)}: from ${JSON.stringify(r.from)} does not match any component id`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!ids.has(r.to)) {
|
|
74
|
+
problems.push({
|
|
75
|
+
path: `/relations/${i}/to`,
|
|
76
|
+
message: `relation ${JSON.stringify(r.id)}: to ${JSON.stringify(r.to)} does not match any component id`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
walkthroughs.forEach((w: SubsystemWalkthrough, wi) => {
|
|
82
|
+
(w.steps ?? []).forEach((step, si) => {
|
|
83
|
+
if (!ids.has(step.from)) {
|
|
84
|
+
problems.push({
|
|
85
|
+
path: `/walkthroughs/${wi}/steps/${si}/from`,
|
|
86
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} from ${JSON.stringify(step.from)} does not match any component id`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (!ids.has(step.to)) {
|
|
90
|
+
problems.push({
|
|
91
|
+
path: `/walkthroughs/${wi}/steps/${si}/to`,
|
|
92
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} to ${JSON.stringify(step.to)} does not match any component id`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return problems;
|
|
99
|
+
}
|