@principal-ai/subsystems-core 0.51.3 → 0.51.4

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/README.md CHANGED
@@ -9,6 +9,12 @@ Subsystem model types/schema and agent-session tooling for Principal AI.
9
9
  | `@principal-ai/subsystems-core` | Browser-safe subsystem model/topic types, reference links + `isSubsystemModelDocument` |
10
10
  | `@principal-ai/subsystems-core/node` | OpenCode store, agent-session list/fetch/normalize, topic store, fixtures |
11
11
  | `@principal-ai/subsystems-core/pipeline` | Bun-safe session normalize/accumulate (no `better-sqlite3`) |
12
+ | `@principal-ai/subsystems-core/audit` | Audit-facing Graphify provider contract and per-pass graph-load cache |
13
+
14
+ The audit entry point deliberately defines a port rather than selecting a
15
+ Graphify transport. A caller can supply local extraction/cache or a future MCP
16
+ adapter. The shared audit engine and its persistence/repository adapters are
17
+ being extracted separately; this entry point alone does not run an audit.
12
18
 
13
19
  ## Schema
14
20
 
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Graphify port used by subsystem-model audits.
3
+ *
4
+ * The audit should depend on graph data and freshness semantics, not on how a
5
+ * graph was produced or served. Implementations may read the local cache, run
6
+ * Graphify on demand, or call a Graphify MCP service.
7
+ */
8
+ export interface GraphifyAuditModel {
9
+ components: Array<{
10
+ purl?: string;
11
+ construct?: string;
12
+ }>;
13
+ }
14
+ export interface GraphifyAuditEnsureResult {
15
+ ensured: string[];
16
+ failed: Array<{
17
+ purl: string;
18
+ error: string;
19
+ }>;
20
+ }
21
+ export interface GraphifyAuditPurlReadiness {
22
+ purl: string;
23
+ status: 'ready' | 'missing' | 'building' | 'unavailable';
24
+ repoRoot?: string;
25
+ }
26
+ export interface GraphifyAuditReadiness {
27
+ status: 'possible' | 'partial' | 'not_ready' | 'running' | 'unavailable';
28
+ purls: GraphifyAuditPurlReadiness[];
29
+ }
30
+ export interface GraphifyAuditSnapshot {
31
+ nodes: unknown[];
32
+ edges: unknown[];
33
+ }
34
+ export interface GraphifyAuditProvider {
35
+ /** Ensure current-HEAD(+dirty) graphs for the model's local repositories. */
36
+ ensureCurrent(model: GraphifyAuditModel): Promise<GraphifyAuditEnsureResult>;
37
+ /** Describe current-HEAD(+dirty) graph availability without building. */
38
+ readiness(model: GraphifyAuditModel, buildingPurls?: ReadonlySet<string>): Promise<GraphifyAuditReadiness>;
39
+ /** Resolve a repo PURL to a local checkout, when one is available. */
40
+ resolveRepoRoot(purl: string): string | null;
41
+ /** Load the graph for a PURL after ensure/readiness has been checked. */
42
+ loadGraph(purl: string, options?: {
43
+ repoRoot?: string;
44
+ }): Promise<GraphifyAuditSnapshot | null>;
45
+ }
46
+ /**
47
+ * Keep one Graphify read/parse per repository for a single audit pass.
48
+ * Rejected reads are evicted so a later attempt can recover.
49
+ */
50
+ export declare function cacheGraphifyAuditProvider(provider: GraphifyAuditProvider): GraphifyAuditProvider;
51
+ //# sourceMappingURL=graphify-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphify-provider.d.ts","sourceRoot":"","sources":["../../src/audit/graphify-provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAAA;IACxE,KAAK,EAAE,0BAA0B,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,KAAK,EAAE,OAAO,EAAE,CAAA;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,6EAA6E;IAC7E,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;IAC5E,yEAAyE;IACzE,SAAS,CACP,KAAK,EAAE,kBAAkB,EACzB,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAClC,sEAAsE;IACtE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAC5C,yEAAyE;IACzE,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9B,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAA;CACzC;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,qBAAqB,GAC9B,qBAAqB,CAiBvB"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Graphify port used by subsystem-model audits.
4
+ *
5
+ * The audit should depend on graph data and freshness semantics, not on how a
6
+ * graph was produced or served. Implementations may read the local cache, run
7
+ * Graphify on demand, or call a Graphify MCP service.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.cacheGraphifyAuditProvider = void 0;
11
+ /**
12
+ * Keep one Graphify read/parse per repository for a single audit pass.
13
+ * Rejected reads are evicted so a later attempt can recover.
14
+ */
15
+ function cacheGraphifyAuditProvider(provider) {
16
+ const snapshots = new Map();
17
+ return {
18
+ ensureCurrent: (model) => provider.ensureCurrent(model),
19
+ readiness: (model, buildingPurls) => provider.readiness(model, buildingPurls),
20
+ resolveRepoRoot: (purl) => provider.resolveRepoRoot(purl),
21
+ loadGraph(purl, options) {
22
+ const existing = snapshots.get(purl);
23
+ if (existing)
24
+ return existing;
25
+ const pending = provider.loadGraph(purl, options);
26
+ snapshots.set(purl, pending);
27
+ void pending.catch(() => {
28
+ if (snapshots.get(purl) === pending)
29
+ snapshots.delete(purl);
30
+ });
31
+ return pending;
32
+ },
33
+ };
34
+ }
35
+ exports.cacheGraphifyAuditProvider = cacheGraphifyAuditProvider;
36
+ //# sourceMappingURL=graphify-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphify-provider.js","sourceRoot":"","sources":["../../src/audit/graphify-provider.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AA+CH;;;GAGG;AACH,SAAgB,0BAA0B,CACxC,QAA+B;IAE/B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAA;IAC1E,OAAO;QACL,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;QACvD,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC;QAC7E,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC;QACzD,SAAS,CAAC,IAAI,EAAE,OAAO;YACrB,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACpC,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAA;YAC7B,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACjD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC5B,KAAK,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;gBACtB,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,OAAO;oBAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC7D,CAAC,CAAC,CAAA;YACF,OAAO,OAAO,CAAA;QAChB,CAAC;KACF,CAAA;AACH,CAAC;AAnBD,gEAmBC"}
@@ -0,0 +1,2 @@
1
+ export { cacheGraphifyAuditProvider, type GraphifyAuditModel, type GraphifyAuditEnsureResult, type GraphifyAuditPurlReadiness, type GraphifyAuditReadiness, type GraphifyAuditSnapshot, type GraphifyAuditProvider, } from './graphify-provider';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cacheGraphifyAuditProvider = void 0;
4
+ var graphify_provider_1 = require("./graphify-provider");
5
+ Object.defineProperty(exports, "cacheGraphifyAuditProvider", { enumerable: true, get: function () { return graphify_provider_1.cacheGraphifyAuditProvider; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":";;;AAAA,yDAQ4B;AAP1B,+HAAA,0BAA0B,OAAA"}
package/dist/index.d.ts CHANGED
@@ -15,4 +15,5 @@ export type { DraftTopic, TopicAsset, TopicCreator, TopicSubsystemModelRef, Topi
15
15
  export { formatTopicReferenceLink, parseTopicReferenceLink } from './storage/topic-reference-links';
16
16
  export { validateSubsystemModelCrossField } from './validation';
17
17
  export type { SubsystemValidationProblem } from './validation';
18
+ export * from './audit';
18
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,SAAS,CAAC;AAExB,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAKpG,OAAO,EAAE,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,SAAS,CAAC;AAExB,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAKpG,OAAO,EAAE,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAI/D,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -36,4 +36,7 @@ Object.defineProperty(exports, "parseTopicReferenceLink", { enumerable: true, ge
36
36
  // `schemas/subsystem-model.schema.json`.
37
37
  var validation_1 = require("./validation");
38
38
  Object.defineProperty(exports, "validateSubsystemModelCrossField", { enumerable: true, get: function () { return validation_1.validateSubsystemModelCrossField; } });
39
+ // Audit ports and graph-load caching are runtime-neutral; no Node or Graphify
40
+ // dependencies are included here.
41
+ __exportStar(require("./audit"), exports);
39
42
  //# 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;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AASxB,yEAAoG;AAA3F,iIAAA,wBAAwB,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAE1D,+EAA+E;AAC/E,sDAAsD;AACtD,yCAAyC;AACzC,2CAAgE;AAAvD,8HAAA,gCAAgC,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AASxB,yEAAoG;AAA3F,iIAAA,wBAAwB,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAE1D,+EAA+E;AAC/E,sDAAsD;AACtD,yCAAyC;AACzC,2CAAgE;AAAvD,8HAAA,gCAAgC,OAAA;AAGzC,8EAA8E;AAC9E,kCAAkC;AAClC,0CAAwB"}
package/dist/node.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * (no better-sqlite3 native binding).
8
8
  */
9
9
  export * from './types';
10
+ export * from './audit';
10
11
  export { TopicStore, PRINCIPAL_DIR, TOPICS_DIR, LEGACY_TOPICS_BLOB, } from './storage/topicStore';
11
12
  export type { TopicIndexEntry, TopicCreate, TopicUpdate, MigrationResult, } from './storage/topicStore';
12
13
  export type { DraftTopic, TopicAsset, TopicCreator, TopicSubsystemModelRef, TopicReference, } from './storage/topic-types';
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,eAAe,EACf,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAGpG,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3F,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,4BAA4B,EAC5B,eAAe,EACf,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,eAAe,EACf,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAGpG,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC1F,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3F,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,4BAA4B,EAC5B,eAAe,EACf,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/node.js CHANGED
@@ -24,6 +24,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.opencodeRowsToUniversalEvents = exports.collectRepositories = exports.accumulateEvents = exports.normalizeEventsWithAdapter = exports.normalizeEvents = exports.NodePathNormalizationAdapter = exports.buildAgentSessionFixture = exports.fetchRawEvents = exports.detectAgent = exports.listAgentSessions = exports.defaultOpenCodeDBPath = exports.OpenCodeEventStore = exports.parseTopicReferenceLink = exports.formatTopicReferenceLink = exports.LEGACY_TOPICS_BLOB = exports.TOPICS_DIR = exports.PRINCIPAL_DIR = exports.TopicStore = void 0;
26
26
  __exportStar(require("./types"), exports);
27
+ __exportStar(require("./audit"), exports);
27
28
  // File-per-topic store (~/.principal/topics) — node:fs only
28
29
  var topicStore_1 = require("./storage/topicStore");
29
30
  Object.defineProperty(exports, "TopicStore", { enumerable: true, get: function () { return topicStore_1.TopicStore; } });
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AAExB,4DAA4D;AAC5D,mDAK8B;AAJ5B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,wGAAA,UAAU,OAAA;AACV,gHAAA,kBAAkB,OAAA;AAepB,yEAAoG;AAA3F,iIAAA,wBAAwB,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAE1D,mDAAmD;AACnD,oEAA0F;AAAjF,wHAAA,kBAAkB,OAAA;AAAE,2HAAA,qBAAqB,OAAA;AAUlD,iEAAiE;AACjE,4DAA2F;AAAlF,mHAAA,iBAAiB,OAAA;AAAE,6GAAA,WAAW,OAAA;AAAE,gHAAA,cAAc,OAAA;AAOvD,mDAAmD;AACnD,oDAAoE;AAA3D,mHAAA,wBAAwB,OAAA;AAWjC,sEAAsE;AACtE,gDAO6B;AAN3B,wHAAA,4BAA4B,OAAA;AAC5B,2GAAA,eAAe,OAAA;AACf,sHAAA,0BAA0B,OAAA;AAC1B,4GAAA,gBAAgB,OAAA;AAChB,+GAAA,mBAAmB,OAAA;AACnB,yHAAA,6BAA6B,OAAA"}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AACxB,0CAAwB;AAExB,4DAA4D;AAC5D,mDAK8B;AAJ5B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,wGAAA,UAAU,OAAA;AACV,gHAAA,kBAAkB,OAAA;AAepB,yEAAoG;AAA3F,iIAAA,wBAAwB,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAE1D,mDAAmD;AACnD,oEAA0F;AAAjF,wHAAA,kBAAkB,OAAA;AAAE,2HAAA,qBAAqB,OAAA;AAUlD,iEAAiE;AACjE,4DAA2F;AAAlF,mHAAA,iBAAiB,OAAA;AAAE,6GAAA,WAAW,OAAA;AAAE,gHAAA,cAAc,OAAA;AAOvD,mDAAmD;AACnD,oDAAoE;AAA3D,mHAAA,wBAAwB,OAAA;AAWjC,sEAAsE;AACtE,gDAO6B;AAN3B,wHAAA,4BAA4B,OAAA;AAC5B,2GAAA,eAAe,OAAA;AACf,sHAAA,0BAA0B,OAAA;AAC1B,4GAAA,gBAAgB,OAAA;AAChB,+GAAA,mBAAmB,OAAA;AACnB,yHAAA,6BAA6B,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/subsystems-core",
3
- "version": "0.51.3",
3
+ "version": "0.51.4",
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",
@@ -23,6 +23,12 @@
23
23
  "require": "./dist/opencode/pipeline.js",
24
24
  "default": "./dist/opencode/pipeline.js"
25
25
  },
26
+ "./audit": {
27
+ "types": "./dist/audit/index.d.ts",
28
+ "import": "./dist/audit/index.js",
29
+ "require": "./dist/audit/index.js",
30
+ "default": "./dist/audit/index.js"
31
+ },
26
32
  "./schemas/subsystem-model.schema.json": "./schemas/subsystem-model.schema.json"
27
33
  },
28
34
  "files": [
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import {
3
+ cacheGraphifyAuditProvider,
4
+ type GraphifyAuditProvider,
5
+ type GraphifyAuditSnapshot,
6
+ } from './graphify-provider'
7
+
8
+ function provider(loadGraph: GraphifyAuditProvider["loadGraph"]): GraphifyAuditProvider {
9
+ return {
10
+ ensureCurrent: async () => ({ ensured: [], failed: [] }),
11
+ readiness: async () => ({ status: 'possible', purls: [] }),
12
+ resolveRepoRoot: () => null,
13
+ loadGraph,
14
+ }
15
+ }
16
+
17
+ describe('cacheGraphifyAuditProvider', () => {
18
+ test('deduplicates concurrent graph loads by PURL', async () => {
19
+ let loads = 0
20
+ let resolve!: (graph: GraphifyAuditSnapshot) => void
21
+ const source = provider(
22
+ () => {
23
+ loads++
24
+ return new Promise((done) => {
25
+ resolve = done
26
+ })
27
+ },
28
+ )
29
+ const cached = cacheGraphifyAuditProvider(source)
30
+ const first = cached.loadGraph('pkg:github/acme/app')
31
+ const second = cached.loadGraph('pkg:github/acme/app')
32
+
33
+ expect(loads).toBe(1)
34
+ const graph = { nodes: [], edges: [] }
35
+ resolve(graph)
36
+ expect(await first).toBe(graph)
37
+ expect(await second).toBe(graph)
38
+ })
39
+
40
+ test('evicts a rejected load so a later attempt can retry', async () => {
41
+ let loads = 0
42
+ const source = provider(async () => {
43
+ loads++
44
+ if (loads === 1) throw new Error('temporary read failure')
45
+ return { nodes: [], edges: [] }
46
+ })
47
+ const cached = cacheGraphifyAuditProvider(source)
48
+
49
+ await expect(cached.loadGraph('pkg:github/acme/app')).rejects.toThrow(
50
+ 'temporary read failure',
51
+ )
52
+ expect(await cached.loadGraph('pkg:github/acme/app')).toEqual({
53
+ nodes: [],
54
+ edges: [],
55
+ })
56
+ expect(loads).toBe(2)
57
+ })
58
+ })
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Graphify port used by subsystem-model audits.
3
+ *
4
+ * The audit should depend on graph data and freshness semantics, not on how a
5
+ * graph was produced or served. Implementations may read the local cache, run
6
+ * Graphify on demand, or call a Graphify MCP service.
7
+ */
8
+
9
+ export interface GraphifyAuditModel {
10
+ components: Array<{
11
+ purl?: string
12
+ construct?: string
13
+ }>
14
+ }
15
+
16
+ export interface GraphifyAuditEnsureResult {
17
+ ensured: string[]
18
+ failed: Array<{ purl: string; error: string }>
19
+ }
20
+
21
+ export interface GraphifyAuditPurlReadiness {
22
+ purl: string
23
+ status: 'ready' | 'missing' | 'building' | 'unavailable'
24
+ repoRoot?: string
25
+ }
26
+
27
+ export interface GraphifyAuditReadiness {
28
+ status: 'possible' | 'partial' | 'not_ready' | 'running' | 'unavailable'
29
+ purls: GraphifyAuditPurlReadiness[]
30
+ }
31
+
32
+ export interface GraphifyAuditSnapshot {
33
+ nodes: unknown[]
34
+ edges: unknown[]
35
+ }
36
+
37
+ export interface GraphifyAuditProvider {
38
+ /** Ensure current-HEAD(+dirty) graphs for the model's local repositories. */
39
+ ensureCurrent(model: GraphifyAuditModel): Promise<GraphifyAuditEnsureResult>
40
+ /** Describe current-HEAD(+dirty) graph availability without building. */
41
+ readiness(
42
+ model: GraphifyAuditModel,
43
+ buildingPurls?: ReadonlySet<string>,
44
+ ): Promise<GraphifyAuditReadiness>
45
+ /** Resolve a repo PURL to a local checkout, when one is available. */
46
+ resolveRepoRoot(purl: string): string | null
47
+ /** Load the graph for a PURL after ensure/readiness has been checked. */
48
+ loadGraph(
49
+ purl: string,
50
+ options?: { repoRoot?: string },
51
+ ): Promise<GraphifyAuditSnapshot | null>
52
+ }
53
+
54
+ /**
55
+ * Keep one Graphify read/parse per repository for a single audit pass.
56
+ * Rejected reads are evicted so a later attempt can recover.
57
+ */
58
+ export function cacheGraphifyAuditProvider(
59
+ provider: GraphifyAuditProvider,
60
+ ): GraphifyAuditProvider {
61
+ const snapshots = new Map<string, Promise<GraphifyAuditSnapshot | null>>()
62
+ return {
63
+ ensureCurrent: (model) => provider.ensureCurrent(model),
64
+ readiness: (model, buildingPurls) => provider.readiness(model, buildingPurls),
65
+ resolveRepoRoot: (purl) => provider.resolveRepoRoot(purl),
66
+ loadGraph(purl, options) {
67
+ const existing = snapshots.get(purl)
68
+ if (existing) return existing
69
+ const pending = provider.loadGraph(purl, options)
70
+ snapshots.set(purl, pending)
71
+ void pending.catch(() => {
72
+ if (snapshots.get(purl) === pending) snapshots.delete(purl)
73
+ })
74
+ return pending
75
+ },
76
+ }
77
+ }
@@ -0,0 +1,9 @@
1
+ export {
2
+ cacheGraphifyAuditProvider,
3
+ type GraphifyAuditModel,
4
+ type GraphifyAuditEnsureResult,
5
+ type GraphifyAuditPurlReadiness,
6
+ type GraphifyAuditReadiness,
7
+ type GraphifyAuditSnapshot,
8
+ type GraphifyAuditProvider,
9
+ } from './graphify-provider'
package/src/index.ts CHANGED
@@ -27,3 +27,7 @@ export { formatTopicReferenceLink, parseTopicReferenceLink } from './storage/top
27
27
  // `schemas/subsystem-model.schema.json`.
28
28
  export { validateSubsystemModelCrossField } from './validation';
29
29
  export type { SubsystemValidationProblem } from './validation';
30
+
31
+ // Audit ports and graph-load caching are runtime-neutral; no Node or Graphify
32
+ // dependencies are included here.
33
+ export * from './audit';
package/src/node.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  export * from './types';
11
+ export * from './audit';
11
12
 
12
13
  // File-per-topic store (~/.principal/topics) — node:fs only
13
14
  export {