genesis-compiler 1.10.4 → 1.10.5

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
@@ -941,6 +941,7 @@ import {
941
941
  syncSkills,
942
942
  validateArchitectureFacts,
943
943
  validateArchitectureObservations,
944
+ validateCityDocument,
944
945
  verify,
945
946
  withTrustedGitRepository,
946
947
  } from 'genesis-compiler';
@@ -991,6 +992,12 @@ and packages; `connectArchitectureObservations()` joins exact owners and
991
992
  controller operations. `mergeArchitectureObservations()` combines distinct
992
993
  inspectors while rejecting conflicting facts or package snapshots.
993
994
  `validateArchitectureFacts()` validates the combined architecture snapshot.
995
+ `validateCityDocument(city, kind)` validates a saved or freshly built Machine
996
+ (`'machine'`) or Program (`'program'`) City document against the format Genesis
997
+ writes, including embedded architecture facts, and returns it unchanged; hosts
998
+ reading `.genesis/*-city.json` call it rather than keeping their own copy.
999
+ `MACHINE_CITY_SCHEMA`, `PROGRAM_CITY_SCHEMA` and `CITY_SCHEMA_VERSION` name
1000
+ those formats.
994
1001
  These operations neither run a framework inspector nor claim complete runtime
995
1002
  behavior.
996
1003
  `sourceExplanationGuidance()` and `sourceExplanationReviewGuidance()` supply
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.4",
3
+ "version": "1.10.5",
4
4
  "type": "module",
5
5
  "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with project agent guidance.",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.4",
3
+ "version": "1.10.5",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -0,0 +1,148 @@
1
+ import { GenesisError } from './errors.js';
2
+ import { validateArchitectureFacts } from './architecture.js';
3
+
4
+ // The Machine and Program City document formats are produced here
5
+ // (code-index.js), so their shape and validation are owned here too. A host
6
+ // reading a saved City calls validateCityDocument instead of keeping a copy.
7
+ export const MACHINE_CITY_SCHEMA = 'genesis.machine-city.v1';
8
+ export const PROGRAM_CITY_SCHEMA = 'genesis.program-city.v1';
9
+ export const CITY_SCHEMA_VERSION = 1;
10
+
11
+ const CITY_KINDS = Object.freeze({
12
+ machine: Object.freeze({ schema: MACHINE_CITY_SCHEMA, title: 'Machine City' }),
13
+ program: Object.freeze({ schema: PROGRAM_CITY_SCHEMA, title: 'Program City' }),
14
+ });
15
+
16
+ function checker(kind) {
17
+ const title = CITY_KINDS[kind].title;
18
+ const check = (condition, message) => {
19
+ if (!condition) throw new GenesisError('CITY_INVALID', `${title} ${message}`, { kind });
20
+ };
21
+ const record = (value, label) => check(value !== null && typeof value === 'object' && !Array.isArray(value), `${label} must be an object.`);
22
+ const array = (value, label) => check(Array.isArray(value), `${label} must be an array.`);
23
+ const string = (value, label) => check(typeof value === 'string', `${label} must be a string.`);
24
+ const optionalString = (value, label) => check(value === null || typeof value === 'string', `${label} must be a string or null.`);
25
+ const boolean = (value, label) => check(typeof value === 'boolean', `${label} must be a boolean.`);
26
+ const count = (value, label) => check(Number.isInteger(value) && value >= 0, `${label} must be a non-negative integer.`);
27
+ const strings = (value, label) => {
28
+ array(value, label);
29
+ value.forEach((entry, index) => string(entry, `${label}[${index}]`));
30
+ };
31
+ const diagnostics = (value) => {
32
+ array(value, 'diagnostics');
33
+ value.forEach((entry, index) => record(entry, `diagnostics[${index}]`));
34
+ };
35
+ const uniqueIds = (records, label) => {
36
+ const seen = new Set();
37
+ records.forEach((entry, index) => {
38
+ record(entry, `${label}[${index}]`);
39
+ string(entry.id, `${label}[${index}].id`);
40
+ check(!seen.has(entry.id), `${label} contains duplicate id ${entry.id}.`);
41
+ seen.add(entry.id);
42
+ });
43
+ return seen;
44
+ };
45
+ const districts = (value) => {
46
+ array(value, 'districts');
47
+ const ids = uniqueIds(value, 'districts');
48
+ value.forEach((district, index) => {
49
+ const label = `districts[${index}]`;
50
+ string(district.path, `${label}.path`);
51
+ string(district.title, `${label}.title`);
52
+ optionalString(district.parentId, `${label}.parentId`);
53
+ if (district.parentId !== null) check(ids.has(district.parentId), `${label}.parentId does not identify a district.`);
54
+ });
55
+ return ids;
56
+ };
57
+ return { array, boolean, check, count, diagnostics, districts, optionalString, record, string, strings, uniqueIds };
58
+ }
59
+
60
+ function validateMachineCity(city) {
61
+ const v = checker('machine');
62
+ v.string(city.codeHash, 'codeHash');
63
+ v.strings(city.stackComponents, 'stackComponents');
64
+ v.strings(city.indexers, 'indexers');
65
+ v.diagnostics(city.diagnostics);
66
+ const districtIds = v.districts(city.districts);
67
+
68
+ v.array(city.buildings, 'buildings');
69
+ const buildingIds = v.uniqueIds(city.buildings, 'buildings');
70
+ city.buildings.forEach((building, index) => {
71
+ const label = `buildings[${index}]`;
72
+ for (const field of ['path', 'title', 'districtId', 'language', 'role', 'hash']) v.string(building[field], `${label}.${field}`);
73
+ v.check(districtIds.has(building.districtId), `${label}.districtId does not identify a district.`);
74
+ for (const field of ['mode', 'bytes', 'lines']) v.count(building[field], `${label}.${field}`);
75
+ v.strings(building.extractors, `${label}.extractors`);
76
+ v.strings(building.functionIds, `${label}.functionIds`);
77
+ v.count(building.publicFunctionCount, `${label}.publicFunctionCount`);
78
+ v.count(building.internalFunctionCount, `${label}.internalFunctionCount`);
79
+ });
80
+
81
+ v.array(city.functions, 'functions');
82
+ const functionIds = v.uniqueIds(city.functions, 'functions');
83
+ city.functions.forEach((entry, index) => {
84
+ const label = `functions[${index}]`;
85
+ for (const field of ['name', 'qualifiedName', 'kind', 'visibility', 'path', 'fileId', 'role', 'language', 'extractor']) {
86
+ v.string(entry[field], `${label}.${field}`);
87
+ }
88
+ v.optionalString(entry.container, `${label}.container`);
89
+ v.check(buildingIds.has(entry.fileId), `${label}.fileId does not identify a building.`);
90
+ v.count(entry.line, `${label}.line`);
91
+ v.count(entry.column, `${label}.column`);
92
+ v.strings(entry.parameters, `${label}.parameters`);
93
+ for (const field of ['async', 'generator', 'static']) v.boolean(entry[field], `${label}.${field}`);
94
+ });
95
+ city.buildings.forEach((building, index) => {
96
+ for (const functionId of building.functionIds) {
97
+ v.check(functionIds.has(functionId), `buildings[${index}].functionIds contains an unknown function id.`);
98
+ }
99
+ });
100
+ if (city.architecture) validateArchitectureFacts(city.architecture);
101
+ }
102
+
103
+ function validateProgramCity(city) {
104
+ const v = checker('program');
105
+ v.string(city.programHash, 'programHash');
106
+ v.diagnostics(city.diagnostics);
107
+ const districtIds = v.districts(city.districts);
108
+
109
+ v.array(city.buildings, 'buildings');
110
+ const buildingIds = v.uniqueIds(city.buildings, 'buildings');
111
+ city.buildings.forEach((building, index) => {
112
+ const label = `buildings[${index}]`;
113
+ for (const field of ['name', 'title', 'description', 'publicContract', 'implementationMap', 'path']) v.string(building[field], `${label}.${field}`);
114
+ v.optionalString(building.subsystem, `${label}.subsystem`);
115
+ v.optionalString(building.districtId, `${label}.districtId`);
116
+ v.check(building.districtId === null || districtIds.has(building.districtId), `${label}.districtId does not identify a district.`);
117
+ v.strings(building.sources, `${label}.sources`);
118
+ if (building.mainSource !== undefined) {
119
+ v.string(building.mainSource, `${label}.mainSource`);
120
+ v.check(building.sources.includes(building.mainSource), `${label}.mainSource must be a cited source.`);
121
+ }
122
+ v.strings(building.sourceFileIds, `${label}.sourceFileIds`);
123
+ });
124
+
125
+ v.array(city.links, 'links');
126
+ city.links.forEach((link, index) => {
127
+ const label = `links[${index}]`;
128
+ v.record(link, label);
129
+ v.check(link.kind === 'implemented-by', `${label}.kind must be implemented-by.`);
130
+ v.string(link.fromId, `${label}.fromId`);
131
+ v.string(link.toId, `${label}.toId`);
132
+ v.check(buildingIds.has(link.fromId), `${label}.fromId does not identify a Program building.`);
133
+ });
134
+ }
135
+
136
+ // Validates a saved or freshly built City document of the given kind
137
+ // ('machine' or 'program') and returns it unchanged.
138
+ export function validateCityDocument(city, kind) {
139
+ if (!Object.hasOwn(CITY_KINDS, kind)) throw new GenesisError('CITY_KIND_UNSUPPORTED', `City kind is not supported: ${String(kind || '')}`);
140
+ const v = checker(kind);
141
+ v.record(city, 'document');
142
+ v.check(city.schema === CITY_KINDS[kind].schema, `schema must be ${CITY_KINDS[kind].schema}.`);
143
+ v.check(city.schemaVersion === CITY_SCHEMA_VERSION, `schemaVersion must be ${CITY_SCHEMA_VERSION}.`);
144
+ v.string(city.status, 'status');
145
+ if (kind === 'machine') validateMachineCity(city);
146
+ else validateProgramCity(city);
147
+ return city;
148
+ }
@@ -11,6 +11,7 @@ import { isProjectContentPath } from './paths.js';
11
11
  import { inspectProgram } from './program.js';
12
12
  import { inspectSubsystems } from './subsystems.js';
13
13
  import { buildArchitectureFacts } from './architecture.js';
14
+ import { CITY_SCHEMA_VERSION, MACHINE_CITY_SCHEMA, PROGRAM_CITY_SCHEMA } from './city-documents.js';
14
15
  import { gitVisibleFileStates } from './project-files.js';
15
16
  import { readStack } from './stack.js';
16
17
  import { sha256, stableJson, writeFileAtomic } from './utils.js';
@@ -162,8 +163,8 @@ function machineCity({ cityRegions, components, contributions, diagnostics, inde
162
163
  mode,
163
164
  }))));
164
165
  return {
165
- schema: 'genesis.machine-city.v1',
166
- schemaVersion: 1,
166
+ schema: MACHINE_CITY_SCHEMA,
167
+ schemaVersion: CITY_SCHEMA_VERSION,
167
168
  status: indexers.length === 0 ? 'unconfigured' : diagnostics.length > 0 ? 'completed-with-warning' : 'current',
168
169
  codeHash,
169
170
  presentationRegions: presentation.regions,
@@ -206,8 +207,8 @@ async function programCity(projectRoot) {
206
207
  sourceFileIds: module.sources.map(fileId),
207
208
  }));
208
209
  return {
209
- schema: 'genesis.program-city.v1',
210
- schemaVersion: 1,
210
+ schema: PROGRAM_CITY_SCHEMA,
211
+ schemaVersion: CITY_SCHEMA_VERSION,
211
212
  status: program.status,
212
213
  programHash: sha256(stableJson({ operations, subsystems: program.subsystemMap })),
213
214
  diagnostics: [],
@@ -221,8 +222,8 @@ async function programCity(projectRoot) {
221
222
  };
222
223
  } catch (error) {
223
224
  return {
224
- schema: 'genesis.program-city.v1',
225
- schemaVersion: 1,
225
+ schema: PROGRAM_CITY_SCHEMA,
226
+ schemaVersion: CITY_SCHEMA_VERSION,
226
227
  status: 'invalid',
227
228
  programHash: sha256('invalid'),
228
229
  diagnostics: [asDiagnostic(error)],
package/src/index.js CHANGED
@@ -248,3 +248,4 @@ export { inspectOpenCodePlugin, installOpenCodePlugin as syncOpenCodePlugin } fr
248
248
  export { ARCHITECTURE_OBSERVATIONS_SCHEMA, validateArchitectureObservations, connectArchitectureObservations, mergeArchitectureObservations } from './index/architecture-observations.js';
249
249
 
250
250
  export { validateArchitectureFacts } from './index/architecture.js';
251
+ export { CITY_SCHEMA_VERSION, MACHINE_CITY_SCHEMA, PROGRAM_CITY_SCHEMA, validateCityDocument } from './index/city-documents.js';