genesis-compiler 1.2.27 → 1.2.29

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.
@@ -11,14 +11,17 @@ import {
11
11
  writeProjectFormatVersion,
12
12
  } from './project-format.js';
13
13
  import { PROJECT_VERSION_PATH } from './paths.js';
14
- import { readStack } from './stack.js';
14
+ import {
15
+ materializeProjectStackContracts,
16
+ readLegacyStack,
17
+ } from './stack.js';
15
18
  import { uniqueSorted } from './utils.js';
16
19
 
17
20
  async function validateLegacyProject({ projectRoot, stackPackages }) {
18
21
  try {
19
22
  await readBlueprint(projectRoot, { required: true });
20
23
  await readEngineering(projectRoot);
21
- await readStack(projectRoot, { stackPackages });
24
+ await readLegacyStack(projectRoot, { stackPackages });
22
25
  try {
23
26
  await inspectProgram(projectRoot);
24
27
  } catch (error) {
@@ -33,8 +36,13 @@ async function validateLegacyProject({ projectRoot, stackPackages }) {
33
36
  }
34
37
  }
35
38
 
39
+ async function materializeProjectContracts({ projectRoot, stackPackages }) {
40
+ return materializeProjectStackContracts({ projectRoot, stackPackages });
41
+ }
42
+
36
43
  const MIGRATIONS = new Map([
37
44
  [0, validateLegacyProject],
45
+ [1, materializeProjectContracts],
38
46
  ]);
39
47
 
40
48
  export async function migrateProject({ projectRoot, stackPackages = [] } = {}) {
@@ -62,7 +70,8 @@ export async function migrateProject({ projectRoot, stackPackages = [] } = {}) {
62
70
  { projectVersion: version, supportedVersion: CURRENT_PROJECT_FORMAT_VERSION },
63
71
  );
64
72
  }
65
- await migration({ projectRoot: root, stackPackages });
73
+ const result = await migration({ projectRoot: root, stackPackages });
74
+ migratedFiles.push(...(result?.changedFiles || []));
66
75
  version += 1;
67
76
  await writeProjectFormatVersion(root, version);
68
77
  migratedFiles.push(PROJECT_VERSION_PATH);
@@ -2,9 +2,9 @@ import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
4
  import { gitContext } from './git.js';
5
+ import { OPENCODE_PLUGIN_PATH } from './paths.js';
5
6
  import { writeFileAtomic } from './utils.js';
6
7
 
7
- const OPENCODE_PLUGIN_PATH = '.opencode/plugins/genesis-project-guidance.js';
8
8
  const OPENCODE_PLUGIN_SOURCE = new URL('../../plugins/opencode/project-guidance.js', import.meta.url);
9
9
 
10
10
  async function existingSource(location) {
@@ -4,6 +4,7 @@ export const STACK_PATH = 'genesis/stack.md';
4
4
  export const PROGRAM_ROOT = 'genesis/program';
5
5
  export const PROJECT_VERSION_PATH = 'genesis/version';
6
6
  export const VERIFICATION_PATH = '.genesis/verification.json';
7
+ export const OPENCODE_PLUGIN_PATH = '.opencode/plugins/genesis-project-guidance.js';
7
8
 
8
9
  export function isProjectContentPath(file) {
9
10
  const first = file.split('/')[0];
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
 
3
+ import { isProjectContentPath, OPENCODE_PLUGIN_PATH } from './paths.js';
3
4
  import { runGit } from './process.js';
4
5
  import { pathState } from './utils.js';
5
6
 
@@ -22,3 +23,14 @@ export async function gitVisibleFileStates(projectRoot, { includePath = () => tr
22
23
  }
23
24
  return states;
24
25
  }
26
+
27
+ /** Classifies opening behavior from cheap Git-visible paths and selected Stack state. */
28
+ export async function classifyProjectKind({ projectRoot, stackComponents = [] } = {}) {
29
+ if (stackComponents.length > 0) return 'existing';
30
+ const existing = (await visiblePaths(projectRoot)).some((file) => (
31
+ isProjectContentPath(file)
32
+ && file !== OPENCODE_PLUGIN_PATH
33
+ && !file.split('/').includes('node_modules')
34
+ ));
35
+ return existing ? 'existing' : 'new';
36
+ }
@@ -12,7 +12,7 @@ import {
12
12
  } from './paths.js';
13
13
  import { normalizeSource, pathState, writeFileAtomic } from './utils.js';
14
14
 
15
- export const CURRENT_PROJECT_FORMAT_VERSION = 1;
15
+ export const CURRENT_PROJECT_FORMAT_VERSION = 2;
16
16
  export const CURRENT_PROJECT_FORMAT_SOURCE = `${CURRENT_PROJECT_FORMAT_VERSION}\n`;
17
17
 
18
18
  const VERSION_PATTERN = /^(0|[1-9][0-9]*)\n?$/u;
@@ -17,8 +17,7 @@ import { inspectVerification } from './project-state.js';
17
17
  import { missingStackResources } from './stack-preflight.js';
18
18
  import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
19
19
  import { stableJson } from './utils.js';
20
- import { gitVisibleFileStates } from './project-files.js';
21
- import { isProjectContentPath } from './paths.js';
20
+ import { classifyProjectKind } from './project-files.js';
22
21
 
23
22
  const TASKS = new Set(['start', 'adopt', 'work', 'deslop', 'program', 'blueprint', 'describe', 'review']);
24
23
  const DEFAULT_REQUEST = {
@@ -83,6 +82,26 @@ function programContext(program) {
83
82
  };
84
83
  }
85
84
 
85
+ function startProgramContext(program) {
86
+ return {
87
+ status: program.status,
88
+ subsystems: program.subsystems,
89
+ moduleCount: program.modules.length,
90
+ ...(program.diagnostic ? { diagnostic: program.diagnostic } : {}),
91
+ };
92
+ }
93
+
94
+ function startStackContext(stack) {
95
+ return {
96
+ components: stack.components.map(({ id, description }) => ({ id, description })),
97
+ };
98
+ }
99
+
100
+ async function startInstructions(instructions, projectKind) {
101
+ const specific = await readInstalledAsset(`start-${projectKind}`);
102
+ return `${instructions.trim()}\n\n${specific.trim()}`;
103
+ }
104
+
86
105
  function codeIndexContext(index) {
87
106
  return {
88
107
  machineCity: {
@@ -231,21 +250,17 @@ async function generateStartPrompt({
231
250
  root,
232
251
  stackPackages,
233
252
  }) {
234
- const availableEngineeringProfiles = (await listEngineeringProfileCatalog())
235
- .map(({ id, name, description }) => ({ id, name, description }));
236
253
  let blueprint;
237
254
  try {
238
255
  blueprint = await readBlueprint(root, { required: true });
239
256
  } catch (error) {
240
257
  if (error?.code !== 'BLUEPRINT_REQUIRED') throw error;
241
- const files = await gitVisibleFileStates(root, { includePath: isProjectContentPath });
242
- const existing = [...files.values()].some((state) => state.exists);
243
- if (!existing) throw error;
258
+ if (await classifyProjectKind({ projectRoot: root }) === 'new') throw error;
244
259
  return {
245
260
  status: 'ready',
246
261
  task: 'start',
247
262
  prompt: renderPrompt({
248
- instructions,
263
+ instructions: await startInstructions(instructions, 'existing-uninitialized'),
249
264
  request,
250
265
  context: {
251
266
  task: 'start',
@@ -253,8 +268,6 @@ async function generateStartPrompt({
253
268
  projectKind: 'existing-uninitialized',
254
269
  genesis: { initialized: false },
255
270
  engineering: engineeringPromptContext(engineering),
256
- availableEngineeringProfiles,
257
- program: programContext(program),
258
271
  },
259
272
  engineeringGuidance: engineering.guidance,
260
273
  }),
@@ -263,42 +276,51 @@ async function generateStartPrompt({
263
276
  };
264
277
  }
265
278
  const stack = await readStack(root, { stackPackages });
279
+ const projectKind = await classifyProjectKind({
280
+ projectRoot: root,
281
+ stackComponents: stack.components,
282
+ });
266
283
  const availableStackPackages = [...new Set([...stack.stackPackages, ...stackPackages])];
267
- const [index, catalog] = await Promise.all([
268
- buildProjectIndex({ projectRoot: root, stackPackages, write: false }),
269
- listStackCatalogPieces({ projectRoot: root, stackPackages: availableStackPackages }),
284
+ const [catalog, availableEngineeringProfiles, projectSkills, renderedInstructions] = await Promise.all([
285
+ projectKind === 'new'
286
+ ? listStackCatalogPieces({ projectRoot: root, stackPackages: availableStackPackages })
287
+ : Promise.resolve([]),
288
+ projectKind === 'new'
289
+ ? listEngineeringProfileCatalog()
290
+ .then((profiles) => profiles.map(({ id, name, description }) => ({ id, name, description })))
291
+ : Promise.resolve([]),
292
+ inspectProjectSkills({ projectRoot: root, stack }),
293
+ startInstructions(instructions, projectKind),
270
294
  ]);
271
- const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
272
- const newProject = stack.components.length === 0 && index.fileCount === 0;
273
295
  return {
274
296
  status: 'ready',
275
297
  task: 'start',
276
298
  prompt: renderPrompt({
277
- instructions,
299
+ instructions: renderedInstructions,
278
300
  request,
279
301
  context: {
280
302
  task: 'start',
281
303
  projectRoot: root,
282
- projectKind: newProject ? 'new' : 'existing',
304
+ projectKind,
283
305
  blueprint: {
284
306
  path: blueprint.path,
285
307
  description: blueprint.description,
286
308
  },
287
309
  engineering: engineeringPromptContext(engineering),
288
- availableEngineeringProfiles,
289
- stack: stackPromptContext(stack),
290
- availableStackPieces: stackCatalogContext(catalog, hiddenStackPieces),
291
- program: programContext(program),
292
- codeIndex: codeIndexContext(index),
310
+ stack: startStackContext(stack),
311
+ ...(projectKind === 'new'
312
+ ? {
313
+ availableEngineeringProfiles,
314
+ availableStackPieces: stackCatalogContext(catalog, hiddenStackPieces),
315
+ }
316
+ : { program: startProgramContext(program) }),
293
317
  },
294
318
  engineeringGuidance: engineering.guidance,
295
319
  guidance: stack.guidance,
296
- skills: renderAgentSkillCatalog(projectSkills.skills),
297
320
  }),
298
321
  warnings: [
299
322
  ...(program.diagnostic ? [program.diagnostic] : []),
300
323
  ...projectSkills.diagnostics,
301
- ...index.diagnostics,
302
324
  ],
303
325
  verificationCommands: [],
304
326
  };
@@ -1,6 +1,7 @@
1
1
  import { readEngineering, readEngineeringBaseline } from './engineering.js';
2
2
  import { gitContext } from './git.js';
3
3
  import { readStack } from './stack.js';
4
+ import { listStackCatalogPieces } from './stack-catalog.js';
4
5
 
5
6
  async function optionalStack(projectRoot, stackPackages) {
6
7
  try { return await readStack(projectRoot, { stackPackages }); } catch { return null; }
@@ -23,12 +24,23 @@ async function optionalEngineering(projectRoot) {
23
24
  }
24
25
  }
25
26
 
27
+ async function optionalStackComponentIds(projectRoot, stack, stackPackages) {
28
+ try {
29
+ const packages = [...new Set([...(stack?.stackPackages || []), ...stackPackages])];
30
+ return (await listStackCatalogPieces({ projectRoot, stackPackages: packages }))
31
+ .map(({ id }) => id);
32
+ } catch {
33
+ return [];
34
+ }
35
+ }
36
+
26
37
  export async function projectSessionContext({ projectRoot, stackPackages = [] } = {}) {
27
38
  const root = (await gitContext(projectRoot)).repositoryRoot;
28
39
  const [stack, engineering] = await Promise.all([
29
40
  optionalStack(root, stackPackages),
30
41
  optionalEngineering(root),
31
42
  ]);
43
+ const availableComponents = await optionalStackComponentIds(root, stack, stackPackages);
32
44
  const selected = stack?.components.map(({ id }) => id) || [];
33
45
  const stackStatus = stack
34
46
  ? (selected.length > 0 ? selected.join(', ') : 'none')
@@ -38,10 +50,16 @@ export async function projectSessionContext({ projectRoot, stackPackages = [] }
38
50
  output: [
39
51
  'This is a Genesis-enriched project.',
40
52
  '- Read `genesis/blueprint.md`, `genesis/engineering.md`, and `genesis/stack.md` for product intent, engineering approach, and selected technology.',
53
+ '- For a new project whose Blueprint does not yet establish product direction, do not research technology or create source until the user has made clear what is being built, who or what will use or invoke it, and the first observable useful outcome. Ask only unresolved high-impact questions. A reply confirms only what it explicitly answers; Stack confirmation is not product intent.',
54
+ '- Once product direction is clear, choose one smallest implementation path through selected technology guidance, or authoritative technology documentation when the catalog has no match, and read only what that path requires. Do not survey alternatives, clone whole technology repositories, inspect unrelated package internals, or delegate research unless one concrete failure requires one exact investigation.',
55
+ `- Available Stack components: ${availableComponents.length > 0 ? availableComponents.map((id) => `\`${id}\``).join(', ') : 'none'}.`,
56
+ '- When the user names an unselected technology that exactly matches this catalog, run `genesis stack list`, ask whether to add and prepare it, and wait for confirmation. If confirmed, run `genesis stack add <piece...>` and follow its returned preparation prompt in the same task; then use `genesis context` and any installed technology skill. Never infer dependency commands from a component id. If declined or unmatched, continue through authoritative technology documentation without inventing a component.',
57
+ '- Project-owned operation sections in `genesis/stack.md` are the durable application contract. Make the implementation satisfy them or update a complete section to match evidenced reality; Genesis executes only Verification.',
41
58
  '- Use the relevant project Agent Skills below `.agents/skills/`.',
42
59
  '- 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.',
43
60
  '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
44
61
  '- 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.',
62
+ '- 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.',
45
63
  '- Deslop only when explicitly requested. Genesis defines its behavior-preserving committed scope; selected Stack components may add technology-specific cleanup guidance.',
46
64
  '- This guidance is loaded for a new session and refreshed after compaction. Continue the active request without restarting completed work.',
47
65
  `Engineering profile: ${engineering.profile?.id || 'invalid; run the Genesis `check` operation'}.`,
@@ -10,6 +10,7 @@ import {
10
10
 
11
11
  const require = createRequire(import.meta.url);
12
12
  const PACKAGE_NAME = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/u;
13
+ const FIRST_PARTY_STACK_PACKAGE = 'genesis-stack';
13
14
 
14
15
  export function normalizeStackPackageName(value) {
15
16
  const name = String(value ?? '').trim();
@@ -50,6 +51,16 @@ async function packageDirectory(packageName, projectRoot) {
50
51
  );
51
52
  }
52
53
 
54
+ export async function installedFirstPartyStackPackages({ projectRoot } = {}) {
55
+ try {
56
+ await packageDirectory(FIRST_PARTY_STACK_PACKAGE, projectRoot);
57
+ return [FIRST_PARTY_STACK_PACKAGE];
58
+ } catch (error) {
59
+ if (error?.code === 'STACK_PACKAGE_UNAVAILABLE') return [];
60
+ throw error;
61
+ }
62
+ }
63
+
53
64
  async function readPieceDirectory(
54
65
  directory,
55
66
  sourcePrefix,
@@ -0,0 +1,205 @@
1
+ import { GenesisError } from './errors.js';
2
+ import { composeStackEnvironmentDefaults } from './stack-environment-defaults.js';
3
+ import { composeStackEnvironmentFiles } from './stack-environment-files.js';
4
+ import {
5
+ composeStackSections,
6
+ opaqueStackSections,
7
+ trimStackSectionLines,
8
+ } from './stack-section.js';
9
+
10
+ const PROJECT_CONTRACT_NAMES = [
11
+ 'Resources',
12
+ 'Environment defaults',
13
+ 'Environment files',
14
+ 'Verification',
15
+ ];
16
+
17
+ function distinctVerificationCommands(components) {
18
+ const seen = new Set();
19
+ return components.flatMap(({ verificationCommands }) => verificationCommands)
20
+ .filter(({ label, argv }) => {
21
+ const key = JSON.stringify([label, argv]);
22
+ if (seen.has(key)) return false;
23
+ seen.add(key);
24
+ return true;
25
+ });
26
+ }
27
+
28
+ function projectResource(resource) {
29
+ return {
30
+ id: resource.id,
31
+ kind: resource.kind,
32
+ environmentAlternatives: resource.environmentAlternatives.map((alternative) => ({
33
+ bindings: alternative.bindings,
34
+ ...(alternative.allowEmpty.length > 0 ? { allowEmpty: alternative.allowEmpty } : {}),
35
+ ...(alternative.preferred ? { preferred: true } : {}),
36
+ })),
37
+ ...(Object.keys(resource.optionalBindings).length > 0
38
+ ? { optionalBindings: resource.optionalBindings }
39
+ : {}),
40
+ };
41
+ }
42
+
43
+ function resourceLines(components) {
44
+ const resources = components.flatMap(({ resources }) => resources);
45
+ if (resources.length === 0) return null;
46
+ return resources.flatMap((resource, index) => [
47
+ ...(index > 0 ? [''] : []),
48
+ '```json genesis-resource',
49
+ JSON.stringify(projectResource(resource), null, 2),
50
+ '```',
51
+ ]);
52
+ }
53
+
54
+ function environmentDefaultLines(components) {
55
+ const defaults = composeStackEnvironmentDefaults(components);
56
+ if (defaults.length === 0) return null;
57
+ return defaults.map(({ name, value }) => `- Default \`${name}\`: \`${value}\``);
58
+ }
59
+
60
+ function environmentFileLines(components) {
61
+ const files = composeStackEnvironmentFiles(components, null);
62
+ if (files.length === 0) return null;
63
+ return files.map(({ path }) => `- Dotenv \`${path}\``);
64
+ }
65
+
66
+ function verificationLines(components) {
67
+ const commands = distinctVerificationCommands(components);
68
+ if (commands.length === 0) return null;
69
+ return commands.map(({ label, argv }) => (
70
+ `- Verify \`${label}\`: ${argv.map((value) => `\`${value}\``).join(' ')}`
71
+ ));
72
+ }
73
+
74
+ function componentGenesisContracts(components, sections) {
75
+ return new Map([
76
+ ['Resources', sections.has('Resources') ? null : resourceLines(components)],
77
+ ['Environment defaults', sections.has('Environment defaults')
78
+ ? null
79
+ : environmentDefaultLines(components)],
80
+ ['Environment files', sections.has('Environment files')
81
+ ? null
82
+ : environmentFileLines(components)],
83
+ ['Verification', sections.has('Verification') ? null : verificationLines(components)],
84
+ ].filter(([, lines]) => lines !== null));
85
+ }
86
+
87
+ export function stackPieceContractNames(piece) {
88
+ return [
89
+ ...(piece.resources.length > 0 ? ['Resources'] : []),
90
+ ...(piece.environmentDefaults.length > 0 ? ['Environment defaults'] : []),
91
+ ...(piece.environmentFiles.length > 0 ? ['Environment files'] : []),
92
+ ...(piece.verificationCommands.length > 0 ? ['Verification'] : []),
93
+ ...piece.extensions.map(({ name }) => name),
94
+ ];
95
+ }
96
+
97
+ function contractSections({ components, knownSectionNames, sections }) {
98
+ const genesisContracts = componentGenesisContracts(components, sections);
99
+ const materializedSections = [];
100
+ const projectLines = new Map();
101
+
102
+ for (const name of PROJECT_CONTRACT_NAMES) {
103
+ if (sections.has(name)) {
104
+ projectLines.set(name, trimStackSectionLines(sections.get(name)));
105
+ } else if (genesisContracts.has(name)) {
106
+ projectLines.set(name, genesisContracts.get(name));
107
+ materializedSections.push(name);
108
+ }
109
+ }
110
+
111
+ const extensions = composeStackSections(
112
+ components,
113
+ opaqueStackSections(sections, knownSectionNames),
114
+ );
115
+ for (const extension of extensions) {
116
+ const [diagnostic] = extension.diagnostics;
117
+ if (diagnostic) {
118
+ throw new GenesisError(diagnostic.code, diagnostic.message, diagnostic.details);
119
+ }
120
+ projectLines.set(extension.name, trimStackSectionLines(extension.lines));
121
+ if (!sections.has(extension.name)) materializedSections.push(extension.name);
122
+ }
123
+
124
+ return {
125
+ environmentDefaultLines: projectLines.get('Environment defaults') ?? null,
126
+ environmentFileLines: projectLines.get('Environment files') ?? null,
127
+ extensionSections: [...projectLines.entries()]
128
+ .filter(([name]) => !PROJECT_CONTRACT_NAMES.includes(name))
129
+ .map(([name, lines]) => ({ name, lines })),
130
+ materializedSections,
131
+ projectContracts: [...projectLines].map(([name, lines]) => ({ name, lines })),
132
+ resourceLines: projectLines.get('Resources') ?? null,
133
+ verificationLines: projectLines.get('Verification') ?? null,
134
+ };
135
+ }
136
+
137
+ export function materializeStackProjectContracts(options) {
138
+ return contractSections(options);
139
+ }
140
+
141
+ export function requireMaterializedStackProjectContracts(options) {
142
+ const result = contractSections(options);
143
+ if (result.materializedSections.length > 0) {
144
+ const headings = result.materializedSections.map((name) => `\`## ${name}\``);
145
+ throw new GenesisError(
146
+ 'STACK_PROJECT_CONTRACTS_INCOMPLETE',
147
+ `Selected Stack components require project-owned contracts missing from genesis/stack.md: ${headings.join(', ')}. Run \`genesis migrate\` for an older project or re-run the confirmed \`genesis stack add\` operation for a current project.`,
148
+ { sections: result.materializedSections },
149
+ );
150
+ }
151
+ return result;
152
+ }
153
+
154
+ function contractBlocks(projectContracts) {
155
+ if (projectContracts.length === 0) {
156
+ return [
157
+ 'No operation contract was proposed by these components. Inspect the application you are creating and declare only its real setup, verification, output, and other consumer operations before calling it ready.',
158
+ ];
159
+ }
160
+ return projectContracts.flatMap(({ name, lines }) => [
161
+ `### \`## ${name}\``,
162
+ '',
163
+ '~~~markdown',
164
+ ...lines,
165
+ '~~~',
166
+ '',
167
+ ]);
168
+ }
169
+
170
+ export function renderStackPreparationPrompt({
171
+ components,
172
+ materializedSections = [],
173
+ newlySelected = [],
174
+ projectContracts = [],
175
+ reviewSections = [],
176
+ } = {}) {
177
+ if (newlySelected.length === 0 && materializedSections.length === 0) return '';
178
+ const selected = components.map(({ id }) => `\`${id}\``).join(', ');
179
+ return [
180
+ '# Prepare the selected Stack',
181
+ '',
182
+ `Genesis selected ${selected} and made its concrete operation proposals project-owned in \`genesis/stack.md\`. That file is now the durable contract for this application; future catalog releases must not silently change it.`,
183
+ ...(materializedSections.length > 0 ? [
184
+ '',
185
+ `Materialized contract headings: ${materializedSections.map((name) => `\`## ${name}\``).join(', ')}.`,
186
+ ] : []),
187
+ ...(reviewSections.length > 0 ? [
188
+ '',
189
+ `These existing project contracts remained authoritative while the new components were added: ${reviewSections.map((name) => `\`## ${name}\``).join(', ')}. Reconcile them against the newly selected technology using source evidence; Genesis deliberately did not overwrite them.`,
190
+ ] : []),
191
+ '',
192
+ 'Continue this same task now:',
193
+ '',
194
+ '1. Read `genesis/blueprint.md`, `genesis/engineering.md`, and the complete `genesis/stack.md`, then run `genesis context .` (or the relevant source paths).',
195
+ '2. Load every applicable installed Agent Skill and follow the selected technology’s authoritative guidance before creating dependencies or source.',
196
+ '3. For a new project, implement the source and commands so every project contract below is true. For existing source, inspect reality first and change a complete project section when the inherited proposal is not already true; do not add compatibility code merely to imitate a proposal.',
197
+ '4. Keep each consumer-owned section opaque to Genesis. Validate it with the consumer or framework that owns its grammar and behavior.',
198
+ '5. Establish or update the non-technical Blueprint and affected Program explanations as the product becomes concrete.',
199
+ '6. Run `genesis check`, resolve structural or incomplete-contract diagnostics, and run `genesis verify` only after the declared verification commands exist. Passing verification is evidence for those commands, not proof of every consumer operation.',
200
+ '',
201
+ '## Project contracts to satisfy',
202
+ '',
203
+ ...contractBlocks(projectContracts),
204
+ ].join('\n').trimEnd();
205
+ }
@@ -31,8 +31,8 @@ export function opaqueStackSections(sections, knownNames) {
31
31
 
32
32
  /**
33
33
  * Compose consumer-owned Stack sections without interpreting their contents.
34
- * A project declaration replaces component defaults; otherwise exactly one
35
- * selected component may provide a section with a given name.
34
+ * An existing project declaration wins; otherwise exactly one selected
35
+ * component may propose a section for materialization under a given name.
36
36
  */
37
37
  export function composeStackSections(components = [], projectSections = []) {
38
38
  const projectByName = new Map(projectSections.map((section) => [section.name, section]));