genesis-compiler 1.5.2 → 1.6.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.
- package/README.md +59 -1
- package/package.json +1 -1
- package/prompts/adopt.txt +15 -0
- package/prompts/describe.txt +16 -1
- package/prompts/program.txt +16 -1
- package/prompts/review.txt +2 -0
- package/prompts/work.txt +15 -0
- package/skills/genesis-program/SKILL.md +57 -1
- package/skills/genesis-project/SKILL.md +17 -0
- package/src/cli.js +8 -4
- package/src/index/code-index.js +8 -16
- package/src/index/context.js +6 -0
- package/src/index/contracts.js +1 -0
- package/src/index/init.js +2 -0
- package/src/index/program.js +13 -6
- package/src/index/project-inspection.js +1 -0
- package/src/index/prompt.js +2 -0
- package/src/index/session-context.js +2 -0
- package/src/index/subsystems.js +106 -0
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -28,7 +28,8 @@ genesis/
|
|
|
28
28
|
engineering.md selected engineering profile and project-specific requirements
|
|
29
29
|
stack.md selected components, resources, verification, and project-owned operation contracts
|
|
30
30
|
stack/ optional per-component Description, Guidance, Adoption, Post-change, and Deslop customization
|
|
31
|
-
|
|
31
|
+
subsystems.md authored responsibilities, Program membership, and data ownership/use
|
|
32
|
+
program/ concise public-operation explanations
|
|
32
33
|
.agents/skills/
|
|
33
34
|
genesis-* Genesis workflow skills
|
|
34
35
|
<technology>/ authoritative skills installed by selected Stack pieces
|
|
@@ -977,3 +978,60 @@ project operations, and records exact verification evidence. Code, tests, Git
|
|
|
977
978
|
review, and the user's chosen agent remain responsible for implementation. It
|
|
978
979
|
does not claim universal semantic convergence, exhaustive coverage, or
|
|
979
980
|
whole-product correctness.
|
|
981
|
+
|
|
982
|
+
## Authored subsystems (version zero)
|
|
983
|
+
|
|
984
|
+
`genesis/subsystems.md` is the single authored map connecting architectural
|
|
985
|
+
responsibilities to Program operations and data. Program directories are only
|
|
986
|
+
navigation; Genesis never infers subsystem membership from them. Blueprint
|
|
987
|
+
remains non-technical product intent. Source, migrations and runtime behavior
|
|
988
|
+
remain evidence; the map does not define application behavior or database DDL.
|
|
989
|
+
|
|
990
|
+
```markdown
|
|
991
|
+
# Subsystems
|
|
992
|
+
|
|
993
|
+
## `forms` Forms
|
|
994
|
+
|
|
995
|
+
Owns reusable form authoring, rendering, and answer validation.
|
|
996
|
+
|
|
997
|
+
### Program
|
|
998
|
+
- `genesis/program/forms/render.md`
|
|
999
|
+
|
|
1000
|
+
### Data owned
|
|
1001
|
+
- Table `database` `default` `form_definitions`
|
|
1002
|
+
|
|
1003
|
+
### Data used
|
|
1004
|
+
- Nothing.
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
Each subsystem has a unique lowercase hyphenated id, title, responsibility
|
|
1008
|
+
prose, and exactly the three shown sections. Each Program module is assigned
|
|
1009
|
+
exactly once, by its complete project-relative path. Each table has at most one
|
|
1010
|
+
owner. Data used names a table owned by another declared subsystem. Empty
|
|
1011
|
+
sections explicitly say `- Nothing.`; an empty initialized map contains
|
|
1012
|
+
`# Subsystems` followed by `- Nothing.`. Data-only and code-only subsystems are
|
|
1013
|
+
valid. No subsystem hierarchy is inferred from paths.
|
|
1014
|
+
|
|
1015
|
+
Table references contain three distinct values: declared Stack resource **id**,
|
|
1016
|
+
schema, and table. `default` selects that resource's default schema; other schema
|
|
1017
|
+
names are literal. Never put host database names or environment values here.
|
|
1018
|
+
Genesis validates declarations and resource references without opening a database.
|
|
1019
|
+
Hosts resolve table references against their inspected schema and show missing
|
|
1020
|
+
references and unassigned tables explicitly; the map is not a complete schema
|
|
1021
|
+
inventory. Table/column descriptions and relationships remain database metadata.
|
|
1022
|
+
|
|
1023
|
+
`inspectSubsystems({ projectRoot })` and `genesis inspect subsystems --json`
|
|
1024
|
+
return `genesis.subsystems.v0`, normalized entries, status and a deterministic
|
|
1025
|
+
identity. Program inspection additionally validates membership and referenced
|
|
1026
|
+
modules. Context includes the relevant subsystem through its Program Sources;
|
|
1027
|
+
`context .` includes all subsystems. Program City uses authored titles and table
|
|
1028
|
+
associations, including data-only districts, and changes its hash when the map
|
|
1029
|
+
changes. Opening context stays concise; detailed associations load on demand.
|
|
1030
|
+
|
|
1031
|
+
Adoption, Program refresh, work prompts, session guidance and installed workflow
|
|
1032
|
+
skills maintain the map alongside Program in the same turn. Review checks it
|
|
1033
|
+
read-only; Blueprint-only work leaves it alone. Ordinary private helper edits
|
|
1034
|
+
need no map change. Existing projects do not need a bulk update: missing or empty maps preserve
|
|
1035
|
+
Program access and instruct the agent to author the map from source before its
|
|
1036
|
+
next implementation task. Until then, operations have no declared subsystem;
|
|
1037
|
+
there is no folder-based compatibility reader or automatic ownership inference. Initialization creates an empty map without replacing one.
|
package/package.json
CHANGED
package/prompts/adopt.txt
CHANGED
|
@@ -60,3 +60,18 @@ execute them. Finish with a clear inventory of what was imported, what remains
|
|
|
60
60
|
unconfigured or blocked, and what would require a separately approved
|
|
61
61
|
implementation port. Never claim the project is ready merely because Stack
|
|
62
62
|
components were selected.
|
|
63
|
+
|
|
64
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
65
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
66
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
67
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
68
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
69
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
70
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
71
|
+
|
|
72
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
73
|
+
application, create it from the actual source, schema, and existing Program
|
|
74
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
75
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
76
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
77
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
package/prompts/describe.txt
CHANGED
|
@@ -12,6 +12,21 @@ If the codebase is too large for one careful pass, use subagents as you judge
|
|
|
12
12
|
appropriate, reconcile their findings, and remain responsible for one coherent
|
|
13
13
|
result. Identify anything not inspected or genuinely uncertain.
|
|
14
14
|
|
|
15
|
-
Edit only `genesis/blueprint.md` and explanatory Markdown below
|
|
15
|
+
Edit only `genesis/blueprint.md`, `genesis/subsystems.md`, and explanatory Markdown below
|
|
16
16
|
`genesis/program/`. Summarize every explanatory file created, updated, or
|
|
17
17
|
removed.
|
|
18
|
+
|
|
19
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
20
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
21
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
22
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
23
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
24
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
25
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
26
|
+
|
|
27
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
28
|
+
application, create it from the actual source, schema, and existing Program
|
|
29
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
30
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
31
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
32
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
package/prompts/program.txt
CHANGED
|
@@ -4,9 +4,24 @@ in complete Program-refresh mode.
|
|
|
4
4
|
Explain the code that currently exists using the smallest useful
|
|
5
5
|
subsystem-oriented Program. Read implementation, tests, Blueprint, and current
|
|
6
6
|
Program material. Use Machine City as a navigation aid, then verify relevant
|
|
7
|
-
source directly. Edit only explanatory Markdown below `genesis/program/`.
|
|
7
|
+
source directly. Edit only `genesis/subsystems.md` and explanatory Markdown below `genesis/program/`.
|
|
8
8
|
Delete stale or duplicate modules and do not edit code, tests, configuration,
|
|
9
9
|
Blueprint, Stack, or `.genesis/`.
|
|
10
10
|
|
|
11
11
|
When finished, summarize Program files created, updated, or removed and report
|
|
12
12
|
genuine ambiguity.
|
|
13
|
+
|
|
14
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
15
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
16
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
17
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
18
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
19
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
20
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
21
|
+
|
|
22
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
23
|
+
application, create it from the actual source, schema, and existing Program
|
|
24
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
25
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
26
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
27
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
package/prompts/review.txt
CHANGED
|
@@ -10,3 +10,5 @@ evidence from inference and state questions that require human intent.
|
|
|
10
10
|
|
|
11
11
|
Do not edit any file. Return a concise report ordered by impact with exact file
|
|
12
12
|
references, the evidence inspected, and the smallest sensible correction.
|
|
13
|
+
|
|
14
|
+
Review `genesis/subsystems.md` responsibilities, Program membership, and data ownership/use against source and schema. Report missing, stale, or conflicting associations without editing them.
|
package/prompts/work.txt
CHANGED
|
@@ -38,3 +38,18 @@ explicit decision, and add the selection with the Genesis
|
|
|
38
38
|
`stack add <piece...>` operation before relying on technology-specific
|
|
39
39
|
operations. Do not end after only updating
|
|
40
40
|
or summarizing the Blueprint while the Stack remains empty.
|
|
41
|
+
|
|
42
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
43
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
44
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
45
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
46
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
47
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
48
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
49
|
+
|
|
50
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
51
|
+
application, create it from the actual source, schema, and existing Program
|
|
52
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
53
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
54
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
55
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
|
@@ -21,7 +21,8 @@ private design choice, or ambiguity into product intent.
|
|
|
21
21
|
|
|
22
22
|
## Program
|
|
23
23
|
|
|
24
|
-
Organize Program
|
|
24
|
+
Organize Program in readable directories; membership is declared only in
|
|
25
|
+
`genesis/subsystems.md`, independently of directory names:
|
|
25
26
|
|
|
26
27
|
```text
|
|
27
28
|
genesis/program/billing/invoices.md
|
|
@@ -64,3 +65,58 @@ The caller determines whether this is initial description, complete Program
|
|
|
64
65
|
refresh, focused post-change reconciliation, Blueprint-only work, or read-only
|
|
65
66
|
review. Respect the caller's edit boundary. Report ambiguity rather than
|
|
66
67
|
inventing intent.
|
|
68
|
+
|
|
69
|
+
## Subsystems
|
|
70
|
+
|
|
71
|
+
Maintain `genesis/subsystems.md` alongside Program in description and refresh
|
|
72
|
+
work. Blueprint-only work does not change the map; review remains read-only.
|
|
73
|
+
The map is the authored association authority, but responsibilities and table
|
|
74
|
+
references remain fallible explanations of application source and schema.
|
|
75
|
+
|
|
76
|
+
Use this exact version-zero grammar:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
# Subsystems
|
|
80
|
+
|
|
81
|
+
## `forms` Forms
|
|
82
|
+
|
|
83
|
+
Owns reusable form rendering, validation, and submission capture.
|
|
84
|
+
|
|
85
|
+
### Program
|
|
86
|
+
- `genesis/program/forms/render.md`
|
|
87
|
+
|
|
88
|
+
### Data owned
|
|
89
|
+
- Table `database` `default` `form_definitions`
|
|
90
|
+
|
|
91
|
+
### Data used
|
|
92
|
+
- Nothing.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use one stable lowercase hyphenated id and a human title per subsystem. Each
|
|
96
|
+
entry needs responsibility prose and exactly the three sections shown. Empty
|
|
97
|
+
sections say `- Nothing.`; a new empty map contains only `# Subsystems` and
|
|
98
|
+
`- Nothing.`. Every Program module belongs to exactly one subsystem. No folder
|
|
99
|
+
inference, globs, duplicated operations, or duplicated table owners.
|
|
100
|
+
|
|
101
|
+
A table identity is three separate exact backticked values: the declared Stack
|
|
102
|
+
resource id, schema, and table. `default` means the resource's default schema;
|
|
103
|
+
use an explicit schema where applicable. Never record a host database name,
|
|
104
|
+
credential, SQL expression, or environment value. Data used references a table
|
|
105
|
+
owned by another declared subsystem. A subsystem may own data without Program
|
|
106
|
+
operations, or operations without data. Read the real schema/migrations before
|
|
107
|
+
assigning ownership; report uncertainty instead of inventing associations.
|
|
108
|
+
|
|
109
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
110
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
111
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
112
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
113
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
114
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
115
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
116
|
+
|
|
117
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
118
|
+
application, create it from the actual source, schema, and existing Program
|
|
119
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
120
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
121
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
122
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
|
@@ -152,3 +152,20 @@ required inputs and resources, declared project operations, and focused evidence
|
|
|
152
152
|
with what actually exists. Report files changed, checks actually run,
|
|
153
153
|
anything not proven, and anything still requiring attention. Never claim that
|
|
154
154
|
an unrun check passed or that passing checks prove the whole product.
|
|
155
|
+
|
|
156
|
+
## Subsystem maintenance
|
|
157
|
+
|
|
158
|
+
Maintain `genesis/subsystems.md` in the same implementation turn when a change
|
|
159
|
+
adds, removes, or changes a subsystem responsibility, Program membership, or
|
|
160
|
+
data ownership/use. Read the map before changing related code. Reuse existing
|
|
161
|
+
subsystems unless a distinct responsibility is evidenced. Declare each Program
|
|
162
|
+
module exactly once; folders do not determine membership. Keep table references
|
|
163
|
+
grounded in schema/migrations; Genesis does not inspect databases. Private
|
|
164
|
+
helper changes need no map edit when these associations remain unchanged.
|
|
165
|
+
|
|
166
|
+
If `genesis/subsystems.md` is missing, or is empty in an existing explained
|
|
167
|
+
application, create it from the actual source, schema, and existing Program
|
|
168
|
+
before implementation. This is ordinary explanatory adoption, not a blocker or
|
|
169
|
+
a reason to ask permission again. Preserve the existing Program and source;
|
|
170
|
+
inspect the relevant responsibilities and complete the smallest truthful map.
|
|
171
|
+
Initialization can create the empty file, but only the agent authors its meaning.
|
package/src/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
inspectEngineering,
|
|
15
15
|
inspectEnvironment,
|
|
16
16
|
inspectStackSection,
|
|
17
|
+
inspectSubsystems,
|
|
17
18
|
inspectProject,
|
|
18
19
|
inspectSkills,
|
|
19
20
|
installCodex,
|
|
@@ -62,6 +63,7 @@ const USAGE = `Usage:
|
|
|
62
63
|
genesis context <path...>
|
|
63
64
|
genesis index [function-or-path...]
|
|
64
65
|
genesis migrate
|
|
66
|
+
genesis inspect subsystems
|
|
65
67
|
genesis inspect environment
|
|
66
68
|
genesis inspect project
|
|
67
69
|
genesis inspect skills
|
|
@@ -182,12 +184,12 @@ function parseCommand(argv) {
|
|
|
182
184
|
} else if (command === 'context' && operands.length === 0) {
|
|
183
185
|
fail('CONTEXT_PATH_REQUIRED', 'Command context requires at least one project path.');
|
|
184
186
|
} else if (command === 'inspect') {
|
|
185
|
-
const ordinaryInspection = operands.length === 1 && ['environment', 'project', 'skills'].includes(operands[0]);
|
|
187
|
+
const ordinaryInspection = operands.length === 1 && ['environment', 'project', 'skills', 'subsystems'].includes(operands[0]);
|
|
186
188
|
const sectionInspection = operands.length >= 2 && operands[0] === 'section';
|
|
187
189
|
if (!ordinaryInspection && !sectionInspection) {
|
|
188
190
|
fail(
|
|
189
191
|
'CLI_INSPECT_TARGET_REQUIRED',
|
|
190
|
-
'Command inspect requires project, environment, skills, or section <name>.',
|
|
192
|
+
'Command inspect requires project, environment, skills, subsystems, or section <name>.',
|
|
191
193
|
);
|
|
192
194
|
}
|
|
193
195
|
} else if (command === 'hook' && (operands.length !== 1 || !['discover', 'session', 'turn'].includes(operands[0]))) {
|
|
@@ -272,7 +274,9 @@ function writeInspection(result) {
|
|
|
272
274
|
line(process.stdout, `${title}: ${result.status}`);
|
|
273
275
|
namedItems('Components', result.components);
|
|
274
276
|
namedItems('Runtimes', result.runtimeRequirements);
|
|
275
|
-
if (result.inspection === '
|
|
277
|
+
if (result.inspection === 'subsystems') {
|
|
278
|
+
namedItems('Subsystems', result.subsystems.map(({ id, title }) => `${id}: ${title}`));
|
|
279
|
+
} else if (result.inspection === 'environment') {
|
|
276
280
|
namedItems('Public defaults', result.environmentDefaults.map(({ name, value }) => `${name}=${value}`));
|
|
277
281
|
namedItems('Environment files', result.files.map(({ format, path: filePath }) => `${format}: ${filePath}`));
|
|
278
282
|
} else if (result.inspection === 'section') {
|
|
@@ -511,7 +515,7 @@ async function execute({ command, operands, options }, { signal } = {}) {
|
|
|
511
515
|
}),
|
|
512
516
|
};
|
|
513
517
|
}
|
|
514
|
-
const inspections = { environment: inspectEnvironment, project: inspectProject, skills: inspectSkills };
|
|
518
|
+
const inspections = { subsystems: inspectSubsystems, environment: inspectEnvironment, project: inspectProject, skills: inspectSkills };
|
|
515
519
|
return {
|
|
516
520
|
inspection: operands[0],
|
|
517
521
|
...await inspections[operands[0]]({ projectRoot, stackPackages }),
|
package/src/index/code-index.js
CHANGED
|
@@ -43,7 +43,7 @@ function subsystemId(value) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function operationId(module) {
|
|
46
|
-
return `operation:${module.
|
|
46
|
+
return `operation:${module.path}`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function parentDirectory(value) {
|
|
@@ -70,18 +70,10 @@ function directoryRecords(paths) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function subsystemRecords(subsystems) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
paths.add(segments.slice(0, count).join('/'));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return [...paths].sort().map((subsystem) => ({
|
|
81
|
-
id: subsystemId(subsystem),
|
|
82
|
-
path: subsystem,
|
|
83
|
-
title: path.posix.basename(subsystem).replace(/-/gu, ' '),
|
|
84
|
-
parentId: subsystem.includes('/') ? subsystemId(parentDirectory(subsystem)) : null,
|
|
73
|
+
return subsystems.map((entry) => ({
|
|
74
|
+
id: subsystemId(entry.id), path: entry.id, title: entry.title,
|
|
75
|
+
description: entry.description, parentId: null,
|
|
76
|
+
dataOwned: entry.dataOwned, dataUsed: entry.dataUsed,
|
|
85
77
|
}));
|
|
86
78
|
}
|
|
87
79
|
|
|
@@ -195,7 +187,7 @@ async function programCity(projectRoot) {
|
|
|
195
187
|
implementationMap: module.implementationMap,
|
|
196
188
|
path: module.path,
|
|
197
189
|
subsystem: module.subsystem,
|
|
198
|
-
districtId: subsystemId(module.subsystem),
|
|
190
|
+
districtId: module.subsystem === null ? null : subsystemId(module.subsystem),
|
|
199
191
|
sources: module.sources,
|
|
200
192
|
sourceFileIds: module.sources.map(fileId),
|
|
201
193
|
}));
|
|
@@ -203,9 +195,9 @@ async function programCity(projectRoot) {
|
|
|
203
195
|
schema: 'genesis.program-city.v1',
|
|
204
196
|
schemaVersion: 1,
|
|
205
197
|
status: program.status,
|
|
206
|
-
programHash: sha256(stableJson(operations)),
|
|
198
|
+
programHash: sha256(stableJson({ operations, subsystems: program.subsystemMap })),
|
|
207
199
|
diagnostics: [],
|
|
208
|
-
districts: subsystemRecords(program.subsystems),
|
|
200
|
+
districts: subsystemRecords(program.subsystemMap.subsystems),
|
|
209
201
|
buildings: operations,
|
|
210
202
|
links: operations.flatMap((operation) => operation.sourceFileIds.map((targetId) => ({
|
|
211
203
|
kind: 'implemented-by',
|
package/src/index/context.js
CHANGED
|
@@ -111,6 +111,11 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
|
|
|
111
111
|
'',
|
|
112
112
|
'Use Program as a concise, fallible explanation. Code, tests, and runtime behavior remain the evidence.',
|
|
113
113
|
'',
|
|
114
|
+
'## Relevant subsystems',
|
|
115
|
+
'',
|
|
116
|
+
...(['missing', 'empty'].includes(program.subsystemMap?.status) ? ['The subsystem map needs authoring. Before implementation, create genesis/subsystems.md from source, schema, and existing Program; preserve existing explanations and do not infer ownership from folders.'] : []),
|
|
117
|
+
...((program.subsystemMap?.subsystems || []).filter((entry) => targets.includes('') || modules.some((module) => module.subsystem === entry.id)).map((entry) => JSON.stringify(entry, null, 2))),
|
|
118
|
+
'',
|
|
114
119
|
'## Relevant Program',
|
|
115
120
|
'',
|
|
116
121
|
...programDetails,
|
|
@@ -147,6 +152,7 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
|
|
|
147
152
|
status: 'ready',
|
|
148
153
|
paths: targets,
|
|
149
154
|
modules: moduleSources.map(({ source: _source, ...module }) => module),
|
|
155
|
+
subsystems: (program.subsystemMap?.subsystems || []).filter((entry) => targets.includes('') || modules.some((module) => module.subsystem === entry.id)),
|
|
150
156
|
components: stack.components.map(({ id }) => id),
|
|
151
157
|
engineeringProfile: engineering.profile.id,
|
|
152
158
|
verificationCommands: stack.verificationCommands.map(({ label, argv }) => ({ label, argv })),
|
package/src/index/contracts.js
CHANGED
package/src/index/init.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SUBSYSTEMS_PATH, EMPTY_SUBSYSTEMS_SOURCE } from './subsystems.js';
|
|
1
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
|
|
@@ -40,6 +41,7 @@ export async function initializeProject({ projectRoot, stackPackages = [] } = {}
|
|
|
40
41
|
const projectFormat = await inspectProjectFormatAtRoot(root);
|
|
41
42
|
requireCurrentProjectFormat(projectFormat, { allowUninitialized: true });
|
|
42
43
|
const created = (await Promise.all([
|
|
44
|
+
createIfMissing(root, SUBSYSTEMS_PATH, EMPTY_SUBSYSTEMS_SOURCE),
|
|
43
45
|
createIfMissing(root, BLUEPRINT_PATH, BLUEPRINT_SKELETON_SOURCE),
|
|
44
46
|
createIfMissing(root, COLLABORATION_PATH, COLLABORATION_SKELETON_SOURCE),
|
|
45
47
|
createIfMissing(root, ENGINEERING_PATH, ENGINEERING_SKELETON_SOURCE),
|
package/src/index/program.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
import { inspectSubsystems } from './subsystems.js';
|
|
4
5
|
import { GenesisError } from './errors.js';
|
|
5
6
|
import { isProjectContentPath, PROGRAM_ROOT } from './paths.js';
|
|
6
7
|
import { normalizeRelative } from './utils.js';
|
|
@@ -55,16 +56,15 @@ function moduleIdentity(programPath) {
|
|
|
55
56
|
const relative = programPath.slice(`${PROGRAM_ROOT}/`.length, -3);
|
|
56
57
|
const normalized = normalizeRelative(relative);
|
|
57
58
|
const segments = normalized.split('/');
|
|
58
|
-
if (segments.
|
|
59
|
+
if (segments.some((segment) => !CONCEPT_NAME.test(segment))) {
|
|
59
60
|
throw new GenesisError(
|
|
60
61
|
'PROGRAM_INVALID',
|
|
61
|
-
`Program module needs lowercase
|
|
62
|
+
`Program module needs lowercase path names: ${programPath}.`,
|
|
62
63
|
{ path: programPath },
|
|
63
64
|
);
|
|
64
65
|
}
|
|
65
66
|
return {
|
|
66
67
|
name: segments.at(-1),
|
|
67
|
-
subsystem: segments.slice(0, -1).join('/'),
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -136,8 +136,13 @@ function moduleContents(source, programPath) {
|
|
|
136
136
|
|
|
137
137
|
export async function inspectProgram(projectRoot) {
|
|
138
138
|
const { files, found } = await markdownFiles(projectRoot);
|
|
139
|
+
const subsystemMap = await inspectSubsystems({ projectRoot });
|
|
140
|
+
const assignments = new Map(subsystemMap.subsystems.flatMap((entry) => entry.program.map((file) => [file, entry.id])));
|
|
141
|
+
for (const file of assignments.keys()) {
|
|
142
|
+
if (!files.includes(file)) throw new GenesisError('SUBSYSTEMS_PROGRAM_MISSING', `Subsystem cites missing Program module: ${file}.`);
|
|
143
|
+
}
|
|
139
144
|
if (!found || files.length === 0) {
|
|
140
|
-
return { status: 'missing', files: [], modules: [], subsystems:
|
|
145
|
+
return { status: subsystemMap.status === 'valid' ? 'valid' : 'missing', files: [], modules: [], subsystems: subsystemMap.subsystems.map(({ id }) => id), subsystemMap };
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
const modules = [];
|
|
@@ -150,6 +155,7 @@ export async function inspectProgram(projectRoot) {
|
|
|
150
155
|
}
|
|
151
156
|
const identity = moduleIdentity(programPath);
|
|
152
157
|
const contents = moduleContents(source, programPath);
|
|
158
|
+
if (subsystemMap.status === 'valid' && !assignments.has(programPath)) throw new GenesisError('SUBSYSTEMS_PROGRAM_UNASSIGNED', `Program module has no declared subsystem: ${programPath}.`);
|
|
153
159
|
const { sources } = contents;
|
|
154
160
|
for (const sourcePath of sources) {
|
|
155
161
|
if (!sourcePath || !isProjectContentPath(sourcePath) || !await sourceExists(projectRoot, sourcePath)) {
|
|
@@ -164,7 +170,7 @@ export async function inspectProgram(projectRoot) {
|
|
|
164
170
|
path: programPath,
|
|
165
171
|
name: identity.name,
|
|
166
172
|
sources,
|
|
167
|
-
subsystem:
|
|
173
|
+
subsystem: assignments.get(programPath) ?? null,
|
|
168
174
|
title: contents.title,
|
|
169
175
|
description: contents.description,
|
|
170
176
|
publicContract: contents.publicContract,
|
|
@@ -176,6 +182,7 @@ export async function inspectProgram(projectRoot) {
|
|
|
176
182
|
status: 'valid',
|
|
177
183
|
files,
|
|
178
184
|
modules,
|
|
179
|
-
subsystems:
|
|
185
|
+
subsystems: subsystemMap.subsystems.map(({ id }) => id),
|
|
186
|
+
subsystemMap,
|
|
180
187
|
};
|
|
181
188
|
}
|
package/src/index/prompt.js
CHANGED
|
@@ -74,6 +74,7 @@ function programContext(program) {
|
|
|
74
74
|
return {
|
|
75
75
|
status: program.status,
|
|
76
76
|
files: program.files,
|
|
77
|
+
subsystemMap: program.subsystemMap,
|
|
77
78
|
subsystems: program.subsystems,
|
|
78
79
|
modules: program.modules.map(({ path, name, sources, subsystem }) => ({
|
|
79
80
|
path,
|
|
@@ -90,6 +91,7 @@ function startProgramContext(program) {
|
|
|
90
91
|
status: program.status,
|
|
91
92
|
subsystems: program.subsystems,
|
|
92
93
|
moduleCount: program.modules.length,
|
|
94
|
+
subsystemMapStatus: program.subsystemMap?.status,
|
|
93
95
|
...(program.diagnostic ? { diagnostic: program.diagnostic } : {}),
|
|
94
96
|
};
|
|
95
97
|
}
|
|
@@ -149,6 +149,8 @@ export async function projectSessionContext({
|
|
|
149
149
|
...skills.diagnostics.map(({ message }) => `- Agent Skill maintenance: ${message}`),
|
|
150
150
|
...(skills.diagnostics.length > 0 ? ['- Skill inspection is read-only. Synchronization is an explicit source change; follow the current task authorization and host write boundary before running it.'] : []),
|
|
151
151
|
'- After locating source, run `genesis context <path...>`; before adding a helper or public operation, run `genesis index <name-or-path...>` and reuse an existing owner.',
|
|
152
|
+
'- If genesis/subsystems.md is missing or empty in an existing explained application, author it from source, schema and existing Program before implementation. This is ordinary explanatory adoption, not a startup blocker; do not run tests or application commands merely to create the map.',
|
|
153
|
+
'- Read genesis/subsystems.md for declared subsystem responsibilities, Program membership, and data ownership/use. Maintain it in the same implementation turn when those facts change; reuse existing boundaries, verify source/schema, and never infer membership from folders.',
|
|
152
154
|
'- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
|
|
153
155
|
'- Keep Blueprint and affected Program explanations aligned with intentional observable product behavior in the same implementation turn. Private restructuring may need only source citations or no explanatory change.',
|
|
154
156
|
'- Before reporting completion, compare the requested observable behavior, required inputs and resources, declared project operations, and focused evidence with what actually exists. State anything not proven.',
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { gitContext } from './git.js';
|
|
4
|
+
import { readStack } from './stack.js';
|
|
5
|
+
import { GenesisError } from './errors.js';
|
|
6
|
+
import { normalizeRelative, sha256, stableJson } from './utils.js';
|
|
7
|
+
|
|
8
|
+
export const SUBSYSTEMS_PATH = 'genesis/subsystems.md';
|
|
9
|
+
export const EMPTY_SUBSYSTEMS_SOURCE = '# Subsystems\n\n- Nothing.\n';
|
|
10
|
+
|
|
11
|
+
// Table identities are declarations, never database queries or SQL identifiers.
|
|
12
|
+
export async function inspectSubsystems({ projectRoot = process.cwd() } = {}) {
|
|
13
|
+
projectRoot = (await gitContext(projectRoot)).repositoryRoot;
|
|
14
|
+
let source;
|
|
15
|
+
try { source = await readFile(path.join(projectRoot, SUBSYSTEMS_PATH), 'utf8'); }
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error.code !== 'ENOENT') throw error;
|
|
18
|
+
return { contract: 'genesis.subsystems.v0', status: 'missing', path: SUBSYSTEMS_PATH, subsystems: [] };
|
|
19
|
+
}
|
|
20
|
+
const invalid = (message) => { throw new GenesisError('SUBSYSTEMS_INVALID', `${SUBSYSTEMS_PATH}: ${message}`); };
|
|
21
|
+
const lines = source.replace(/\r\n?/gu, '\n').trim().split('\n');
|
|
22
|
+
if (lines.shift() !== '# Subsystems') invalid('Expected # Subsystems.');
|
|
23
|
+
const subsystems = [];
|
|
24
|
+
let current;
|
|
25
|
+
let section;
|
|
26
|
+
const sections = new Set();
|
|
27
|
+
const finish = () => {
|
|
28
|
+
if (!current) return;
|
|
29
|
+
if (!current.description.trim() || sections.size !== 3) invalid(`Subsystem ${current.id} needs a responsibility and Program, Data owned, Data used sections.`);
|
|
30
|
+
current.description = current.description.trim();
|
|
31
|
+
subsystems.push(current);
|
|
32
|
+
};
|
|
33
|
+
const empty = lines.join('\n').trim() === '- Nothing.';
|
|
34
|
+
if (!empty && !lines.some((line) => line.trim())) invalid('An empty map must say - Nothing.');
|
|
35
|
+
if (!empty) for (const line of lines) {
|
|
36
|
+
if (!line.trim()) continue;
|
|
37
|
+
const heading = line.match(/^## `([a-z0-9]+(?:-[a-z0-9]+)*)` (\S.*)$/u);
|
|
38
|
+
if (heading) {
|
|
39
|
+
finish();
|
|
40
|
+
current = { id: heading[1], title: heading[2], description: '', program: [], dataOwned: [], dataUsed: [] };
|
|
41
|
+
section = null;
|
|
42
|
+
sections.clear();
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (!current) invalid('Expected a subsystem heading or - Nothing.');
|
|
46
|
+
const sectionHeading = line.match(/^### (Program|Data owned|Data used)$/u);
|
|
47
|
+
if (sectionHeading) {
|
|
48
|
+
section = { Program: 'program', 'Data owned': 'dataOwned', 'Data used': 'dataUsed' }[sectionHeading[1]];
|
|
49
|
+
if (sections.has(section)) invalid(`Duplicate ${sectionHeading[1]} section.`);
|
|
50
|
+
sections.add(section);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (line.startsWith('#')) invalid(`Unexpected heading: ${line}`);
|
|
54
|
+
if (!section) { current.description += `${line}\n`; continue; }
|
|
55
|
+
if (line === '- Nothing.') {
|
|
56
|
+
if (current[section].length || current[`${section}Empty`]) invalid('Nothing must be the only section entry.');
|
|
57
|
+
current[`${section}Empty`] = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (current[`${section}Empty`]) invalid('Nothing must be the only section entry.');
|
|
61
|
+
if (section === 'program') {
|
|
62
|
+
const match = line.match(/^- `([^`]+)`$/u);
|
|
63
|
+
if (!match || !/^genesis\/program\/[a-z0-9-]+(?:\/[a-z0-9-]+)*\.md$/u.test(match[1]) || normalizeRelative(match[1]) !== match[1]) invalid(`Invalid Program reference: ${line}`);
|
|
64
|
+
current.program.push(match[1]);
|
|
65
|
+
} else {
|
|
66
|
+
const match = line.match(/^- Table `([^`]+)` `([^`]+)` `([^`]+)`$/u);
|
|
67
|
+
if (!match) invalid(`Expected Table resource, schema, and table: ${line}`);
|
|
68
|
+
current[section].push({ resource: match[1], schema: match[2], table: match[3] });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finish();
|
|
72
|
+
const ids = new Set();
|
|
73
|
+
const programs = new Set();
|
|
74
|
+
const owners = new Map();
|
|
75
|
+
for (const subsystem of subsystems) {
|
|
76
|
+
if (ids.has(subsystem.id)) invalid(`Duplicate subsystem ${subsystem.id}.`);
|
|
77
|
+
ids.add(subsystem.id);
|
|
78
|
+
for (const key of ['program', 'dataOwned', 'dataUsed']) {
|
|
79
|
+
if (!subsystem[key].length && !subsystem[`${key}Empty`]) invalid(`${subsystem.id}: empty sections must say - Nothing.`);
|
|
80
|
+
delete subsystem[`${key}Empty`];
|
|
81
|
+
const entries = subsystem[key].map((entry) => JSON.stringify(entry));
|
|
82
|
+
if (new Set(entries).size !== entries.length) invalid(`${subsystem.id}: duplicate ${key} entry.`);
|
|
83
|
+
}
|
|
84
|
+
for (const file of subsystem.program) {
|
|
85
|
+
if (programs.has(file)) invalid(`Program operation has multiple owners: ${file}.`);
|
|
86
|
+
programs.add(file);
|
|
87
|
+
}
|
|
88
|
+
for (const table of subsystem.dataOwned) {
|
|
89
|
+
const key = JSON.stringify(table);
|
|
90
|
+
if (owners.has(key)) invalid(`Table has multiple owners: ${key}.`);
|
|
91
|
+
owners.set(key, subsystem.id);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const tables = subsystems.flatMap((entry) => [...entry.dataOwned, ...entry.dataUsed]);
|
|
95
|
+
if (tables.length) {
|
|
96
|
+
const stack = await readStack(projectRoot);
|
|
97
|
+
const resources = new Set(stack.resources.map(({ resource }) => resource.id));
|
|
98
|
+
for (const table of tables) if (!resources.has(table.resource)) invalid(`Undeclared Stack resource: ${table.resource}.`);
|
|
99
|
+
}
|
|
100
|
+
for (const subsystem of subsystems) for (const table of subsystem.dataUsed) {
|
|
101
|
+
const owner = owners.get(JSON.stringify(table));
|
|
102
|
+
if (!owner || owner === subsystem.id) invalid(`${subsystem.id}: Data used must reference a table owned by another declared subsystem.`);
|
|
103
|
+
}
|
|
104
|
+
return { contract: 'genesis.subsystems.v0', status: subsystems.length ? 'valid' : 'empty', path: SUBSYSTEMS_PATH,
|
|
105
|
+
identity: sha256(stableJson(subsystems)), subsystems };
|
|
106
|
+
}
|
package/src/index.js
CHANGED
|
@@ -223,3 +223,5 @@ export async function syncSkills(options) {
|
|
|
223
223
|
guidance: 'Review changed skills in the ordinary Git diff. An assistant that already loaded them needs its supported context refresh before using the new guidance.',
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
|
+
|
|
227
|
+
export { inspectSubsystems } from './index/subsystems.js';
|