genesis-compiler 1.2.28 → 1.3.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.
@@ -2,6 +2,7 @@ import { readInstalledAsset } from './assets.js';
2
2
  import { inspectProjectSkills, renderAgentSkillCatalog } from './agent-skills.js';
3
3
  import { buildProjectIndex, MACHINE_CITY_PATH, PROGRAM_CITY_PATH } from './code-index.js';
4
4
  import { BLUEPRINT_SKELETON_SOURCE, readBlueprint } from './blueprint.js';
5
+ import { defaultCollaboration, readCollaboration } from './collaboration.js';
5
6
  import {
6
7
  engineeringPromptContext,
7
8
  listEngineeringProfileCatalog,
@@ -17,8 +18,8 @@ import { inspectVerification } from './project-state.js';
17
18
  import { missingStackResources } from './stack-preflight.js';
18
19
  import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
19
20
  import { stableJson } from './utils.js';
20
- import { gitVisibleFileStates } from './project-files.js';
21
- import { isProjectContentPath } from './paths.js';
21
+ import { classifyProjectKind } from './project-files.js';
22
+ import { inspectProjectFormatAtRoot } from './project-format.js';
22
23
 
23
24
  const TASKS = new Set(['start', 'adopt', 'work', 'deslop', 'program', 'blueprint', 'describe', 'review']);
24
25
  const DEFAULT_REQUEST = {
@@ -83,6 +84,26 @@ function programContext(program) {
83
84
  };
84
85
  }
85
86
 
87
+ function startProgramContext(program) {
88
+ return {
89
+ status: program.status,
90
+ subsystems: program.subsystems,
91
+ moduleCount: program.modules.length,
92
+ ...(program.diagnostic ? { diagnostic: program.diagnostic } : {}),
93
+ };
94
+ }
95
+
96
+ function startStackContext(stack) {
97
+ return {
98
+ components: stack.components.map(({ id, description }) => ({ id, description })),
99
+ };
100
+ }
101
+
102
+ async function startInstructions(instructions, projectKind) {
103
+ const specific = await readInstalledAsset(`start-${projectKind}`);
104
+ return `${instructions.trim()}\n\n${specific.trim()}`;
105
+ }
106
+
86
107
  function codeIndexContext(index) {
87
108
  return {
88
109
  machineCity: {
@@ -109,6 +130,7 @@ function renderPrompt({
109
130
  cleanup = '',
110
131
  postChange = '',
111
132
  adoption = '',
133
+ collaborationGuidance = '',
112
134
  engineeringGuidance = '',
113
135
  }) {
114
136
  return [
@@ -124,6 +146,7 @@ function renderPrompt({
124
146
  stableJson(context).trimEnd(),
125
147
  '```',
126
148
  ...(engineeringGuidance ? ['', 'ENGINEERING APPROACH', '', engineeringGuidance] : []),
149
+ ...(collaborationGuidance ? ['', 'COLLABORATION APPROACH', '', collaborationGuidance] : []),
127
150
  ...(guidance ? ['', 'SELECTED STACK GUIDANCE', '', guidance] : []),
128
151
  ...(adoption ? ['', 'SELECTED STACK ADOPTION GUIDANCE', '', adoption] : []),
129
152
  ...(skills ? ['', 'AVAILABLE AGENT SKILLS', '', skills] : []),
@@ -133,7 +156,43 @@ function renderPrompt({
133
156
  ].join('\n');
134
157
  }
135
158
 
136
- async function generateAdoptionPrompt({ engineering, environment, instructions, program, request, root, stackPackages }) {
159
+ function collaborationPromptContext(collaboration) {
160
+ return {
161
+ path: collaboration.path,
162
+ status: collaboration.status,
163
+ tone: collaboration.tone,
164
+ responseLength: collaboration.responseLength,
165
+ experience: collaboration.experience,
166
+ explanationStyle: collaboration.explanationStyle,
167
+ requirements: collaboration.requirements || 'Nothing.',
168
+ };
169
+ }
170
+
171
+ function stableSessionPromptParts({ collaboration, engineering, installed }) {
172
+ return installed
173
+ ? { context: {}, collaborationGuidance: '', engineeringGuidance: '' }
174
+ : {
175
+ context: {
176
+ collaboration: collaborationPromptContext(collaboration),
177
+ engineering: engineeringPromptContext(engineering),
178
+ },
179
+ collaborationGuidance: collaboration.guidance,
180
+ engineeringGuidance: engineering.guidance,
181
+ };
182
+ }
183
+
184
+ async function collaborationForPrompt(projectRoot) {
185
+ try {
186
+ return await readCollaboration(projectRoot);
187
+ } catch (error) {
188
+ if (error?.code !== 'COLLABORATION_REQUIRED') throw error;
189
+ const format = await inspectProjectFormatAtRoot(projectRoot);
190
+ if (format.status !== 'uninitialized') throw error;
191
+ return defaultCollaboration();
192
+ }
193
+ }
194
+
195
+ async function generateAdoptionPrompt({ environment, instructions, program, request, root, sessionPrompt, stackPackages }) {
137
196
  const blueprint = await readBlueprint(root, { required: true });
138
197
  const stack = await readStack(root, { stackPackages });
139
198
  const availableStackPackages = [...new Set([...stack.stackPackages, ...stackPackages])];
@@ -157,7 +216,7 @@ async function generateAdoptionPrompt({ engineering, environment, instructions,
157
216
  projectRoot: root,
158
217
  projectKind: 'existing',
159
218
  blueprint: { path: blueprint.path, source: blueprint.source },
160
- engineering: engineeringPromptContext(engineering),
219
+ ...sessionPrompt.context,
161
220
  stack: stackPromptContext(stack),
162
221
  availableStackPieces: stackCatalogContext(catalog),
163
222
  program: programContext(program),
@@ -166,7 +225,8 @@ async function generateAdoptionPrompt({ engineering, environment, instructions,
166
225
  : { status: 'present' },
167
226
  codeIndex: codeIndexContext(index),
168
227
  },
169
- engineeringGuidance: engineering.guidance,
228
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
229
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
170
230
  guidance: stack.guidance,
171
231
  adoption: stack.adoption,
172
232
  skills: renderAgentSkillCatalog(projectSkills.skills),
@@ -181,7 +241,7 @@ async function generateAdoptionPrompt({ engineering, environment, instructions,
181
241
  };
182
242
  }
183
243
 
184
- async function generateExplanationPrompt({ engineering, instructions, program, request, root, stackPackages, task }) {
244
+ async function generateExplanationPrompt({ instructions, program, request, root, sessionPrompt, stackPackages, task }) {
185
245
  const blueprint = await readBlueprint(root, {
186
246
  required: true,
187
247
  requireDescription: task === 'program',
@@ -204,12 +264,13 @@ async function generateExplanationPrompt({ engineering, instructions, program, r
204
264
  task,
205
265
  projectRoot: root,
206
266
  blueprint: blueprintContext,
207
- engineering: engineeringPromptContext(engineering),
267
+ ...sessionPrompt.context,
208
268
  stack: stackPromptContext(stack),
209
269
  program: programContext(program),
210
270
  codeIndex: codeIndexContext(index),
211
271
  },
212
- engineeringGuidance: engineering.guidance,
272
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
273
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
213
274
  guidance: stack.guidance,
214
275
  skills: renderAgentSkillCatalog(projectSkills.skills),
215
276
  }),
@@ -224,81 +285,86 @@ async function generateExplanationPrompt({ engineering, instructions, program, r
224
285
 
225
286
  async function generateStartPrompt({
226
287
  hiddenStackPieces,
227
- engineering,
228
288
  instructions,
229
289
  program,
230
290
  request,
231
291
  root,
292
+ sessionPrompt,
232
293
  stackPackages,
233
294
  }) {
234
- const availableEngineeringProfiles = (await listEngineeringProfileCatalog())
235
- .map(({ id, name, description }) => ({ id, name, description }));
236
295
  let blueprint;
237
296
  try {
238
297
  blueprint = await readBlueprint(root, { required: true });
239
298
  } catch (error) {
240
299
  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;
300
+ if (await classifyProjectKind({ projectRoot: root }) === 'new') throw error;
244
301
  return {
245
302
  status: 'ready',
246
303
  task: 'start',
247
304
  prompt: renderPrompt({
248
- instructions,
305
+ instructions: await startInstructions(instructions, 'existing-uninitialized'),
249
306
  request,
250
307
  context: {
251
308
  task: 'start',
252
309
  projectRoot: root,
253
310
  projectKind: 'existing-uninitialized',
254
311
  genesis: { initialized: false },
255
- engineering: engineeringPromptContext(engineering),
256
- availableEngineeringProfiles,
257
- program: programContext(program),
312
+ ...sessionPrompt.context,
258
313
  },
259
- engineeringGuidance: engineering.guidance,
314
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
315
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
260
316
  }),
261
317
  warnings: program.diagnostic ? [program.diagnostic] : [],
262
318
  verificationCommands: [],
263
319
  };
264
320
  }
265
321
  const stack = await readStack(root, { stackPackages });
322
+ const projectKind = await classifyProjectKind({
323
+ projectRoot: root,
324
+ stackComponents: stack.components,
325
+ });
266
326
  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 }),
327
+ const [catalog, availableEngineeringProfiles, projectSkills, renderedInstructions] = await Promise.all([
328
+ projectKind === 'new'
329
+ ? listStackCatalogPieces({ projectRoot: root, stackPackages: availableStackPackages })
330
+ : Promise.resolve([]),
331
+ projectKind === 'new'
332
+ ? listEngineeringProfileCatalog()
333
+ .then((profiles) => profiles.map(({ id, name, description }) => ({ id, name, description })))
334
+ : Promise.resolve([]),
335
+ inspectProjectSkills({ projectRoot: root, stack }),
336
+ startInstructions(instructions, projectKind),
270
337
  ]);
271
- const projectSkills = await inspectProjectSkills({ projectRoot: root, stack });
272
- const newProject = stack.components.length === 0 && index.fileCount === 0;
273
338
  return {
274
339
  status: 'ready',
275
340
  task: 'start',
276
341
  prompt: renderPrompt({
277
- instructions,
342
+ instructions: renderedInstructions,
278
343
  request,
279
344
  context: {
280
345
  task: 'start',
281
346
  projectRoot: root,
282
- projectKind: newProject ? 'new' : 'existing',
347
+ projectKind,
283
348
  blueprint: {
284
349
  path: blueprint.path,
285
350
  description: blueprint.description,
286
351
  },
287
- engineering: engineeringPromptContext(engineering),
288
- availableEngineeringProfiles,
289
- stack: stackPromptContext(stack),
290
- availableStackPieces: stackCatalogContext(catalog, hiddenStackPieces),
291
- program: programContext(program),
292
- codeIndex: codeIndexContext(index),
352
+ ...sessionPrompt.context,
353
+ stack: startStackContext(stack),
354
+ ...(projectKind === 'new'
355
+ ? {
356
+ availableEngineeringProfiles,
357
+ availableStackPieces: stackCatalogContext(catalog, hiddenStackPieces),
358
+ }
359
+ : { program: startProgramContext(program) }),
293
360
  },
294
- engineeringGuidance: engineering.guidance,
361
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
362
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
295
363
  guidance: stack.guidance,
296
- skills: renderAgentSkillCatalog(projectSkills.skills),
297
364
  }),
298
365
  warnings: [
299
366
  ...(program.diagnostic ? [program.diagnostic] : []),
300
367
  ...projectSkills.diagnostics,
301
- ...index.diagnostics,
302
368
  ],
303
369
  verificationCommands: [],
304
370
  };
@@ -309,6 +375,7 @@ export async function generateProjectPrompt({
309
375
  hiddenStackPieces = [],
310
376
  projectRoot,
311
377
  request = '',
378
+ sessionContextInstalled = false,
312
379
  stackPackages = [],
313
380
  task = 'work',
314
381
  } = {}) {
@@ -317,10 +384,16 @@ export async function generateProjectPrompt({
317
384
  }
318
385
  const root = (await gitContext(projectRoot)).repositoryRoot;
319
386
  const userRequest = requestText(request, task);
320
- const [instructions, engineering] = await Promise.all([
387
+ const [instructions, collaboration, engineering] = await Promise.all([
321
388
  readInstalledAsset(task),
389
+ collaborationForPrompt(root),
322
390
  readEngineering(root),
323
391
  ]);
392
+ const sessionPrompt = stableSessionPromptParts({
393
+ collaboration,
394
+ engineering,
395
+ installed: sessionContextInstalled === true,
396
+ });
324
397
 
325
398
  if (task === 'blueprint') {
326
399
  const blueprint = await readBlueprint(root);
@@ -335,9 +408,10 @@ export async function generateProjectPrompt({
335
408
  path: 'genesis/blueprint.md',
336
409
  source: blueprint?.source || BLUEPRINT_SKELETON_SOURCE,
337
410
  },
338
- engineering: engineeringPromptContext(engineering),
411
+ ...sessionPrompt.context,
339
412
  },
340
- engineeringGuidance: engineering.guidance,
413
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
414
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
341
415
  skills: renderAgentSkillCatalog(projectSkills.skills),
342
416
  });
343
417
  return {
@@ -354,21 +428,21 @@ export async function generateProjectPrompt({
354
428
  return generateStartPrompt({
355
429
  instructions,
356
430
  hiddenStackPieces,
357
- engineering,
358
431
  program,
359
432
  request: userRequest,
360
433
  root,
434
+ sessionPrompt,
361
435
  stackPackages,
362
436
  });
363
437
  }
364
438
  if (task === 'adopt') {
365
439
  return generateAdoptionPrompt({
366
440
  environment,
367
- engineering,
368
441
  instructions,
369
442
  program,
370
443
  request: userRequest,
371
444
  root,
445
+ sessionPrompt,
372
446
  stackPackages,
373
447
  });
374
448
  }
@@ -381,10 +455,10 @@ export async function generateProjectPrompt({
381
455
  return generateStartPrompt({
382
456
  instructions: await readInstalledAsset('start'),
383
457
  hiddenStackPieces,
384
- engineering,
385
458
  program,
386
459
  request: userRequest,
387
460
  root,
461
+ sessionPrompt,
388
462
  stackPackages,
389
463
  });
390
464
  }
@@ -392,10 +466,10 @@ export async function generateProjectPrompt({
392
466
  return generateStartPrompt({
393
467
  instructions: await readInstalledAsset('start'),
394
468
  hiddenStackPieces,
395
- engineering,
396
469
  program,
397
470
  request: userRequest,
398
471
  root,
472
+ sessionPrompt,
399
473
  stackPackages,
400
474
  });
401
475
  }
@@ -403,10 +477,10 @@ export async function generateProjectPrompt({
403
477
  if (['describe', 'program'].includes(task)) {
404
478
  return generateExplanationPrompt({
405
479
  instructions,
406
- engineering,
407
480
  program,
408
481
  request: userRequest,
409
482
  root,
483
+ sessionPrompt,
410
484
  stackPackages,
411
485
  task,
412
486
  });
@@ -432,7 +506,7 @@ export async function generateProjectPrompt({
432
506
  task,
433
507
  projectRoot: root,
434
508
  blueprint: { path: blueprint.path, description: blueprint.description },
435
- engineering: engineeringPromptContext(engineering),
509
+ ...sessionPrompt.context,
436
510
  stack: stackPromptContext(stack),
437
511
  program: programContext(program),
438
512
  resourceInputs: missing.length > 0
@@ -450,7 +524,8 @@ export async function generateProjectPrompt({
450
524
  instructions,
451
525
  request: userRequest,
452
526
  context,
453
- engineeringGuidance: engineering.guidance,
527
+ collaborationGuidance: sessionPrompt.collaborationGuidance,
528
+ engineeringGuidance: sessionPrompt.engineeringGuidance,
454
529
  guidance: stack.guidance,
455
530
  skills: renderAgentSkillCatalog(projectSkills.skills),
456
531
  postChange: task === 'work' ? stack.postChange : '',
@@ -1,7 +1,16 @@
1
+ import { readCollaboration } from './collaboration.js';
2
+ import { GENESIS_CONTRACTS } from './contracts.js';
1
3
  import { readEngineering, readEngineeringBaseline } from './engineering.js';
4
+ import { GenesisError } from './errors.js';
2
5
  import { gitContext } from './git.js';
3
6
  import { readStack } from './stack.js';
4
7
  import { listStackCatalogPieces } from './stack-catalog.js';
8
+ import { normalizeSource, sha256, stableJson } from './utils.js';
9
+
10
+ const HOST_CONTEXT_MAX_BYTES = Object.freeze({
11
+ session: 16 * 1024,
12
+ turn: 512,
13
+ });
5
14
 
6
15
  async function optionalStack(projectRoot, stackPackages) {
7
16
  try { return await readStack(projectRoot, { stackPackages }); } catch { return null; }
@@ -24,6 +33,65 @@ async function optionalEngineering(projectRoot) {
24
33
  }
25
34
  }
26
35
 
36
+ async function optionalCollaboration(projectRoot) {
37
+ try {
38
+ return await readCollaboration(projectRoot);
39
+ } catch {
40
+ return {
41
+ guidance: [
42
+ 'The project collaboration approach is invalid. Do not infer replacement preferences; run the Genesis `check` operation.',
43
+ ].join('\n'),
44
+ };
45
+ }
46
+ }
47
+
48
+ async function hostContextContribution({ hostDriver, hostDriverInput, scope }) {
49
+ if (hostDriver === undefined || hostDriver === null) {
50
+ if (hostDriverInput !== undefined) {
51
+ throw new GenesisError(
52
+ 'HOST_CONTEXT_INVALID',
53
+ 'Host context input requires an explicitly registered host driver.',
54
+ );
55
+ }
56
+ return '';
57
+ }
58
+ if (typeof hostDriver !== 'function') {
59
+ throw new GenesisError('HOST_CONTEXT_INVALID', 'The registered host driver must be a function.');
60
+ }
61
+ if (
62
+ !hostDriverInput
63
+ || typeof hostDriverInput !== 'object'
64
+ || Array.isArray(hostDriverInput)
65
+ || hostDriverInput.scope !== scope
66
+ ) {
67
+ throw new GenesisError(
68
+ 'HOST_CONTEXT_INVALID',
69
+ `The ${scope} context lane requires host input with scope \`${scope}\`.`,
70
+ );
71
+ }
72
+ const value = await hostDriver(hostDriverInput);
73
+ if (typeof value !== 'string') {
74
+ throw new GenesisError('HOST_CONTEXT_INVALID', 'The registered host driver must return plain text.');
75
+ }
76
+ const output = normalizeSource(value).trim();
77
+ if (Buffer.byteLength(output, 'utf8') > HOST_CONTEXT_MAX_BYTES[scope]) {
78
+ throw new GenesisError(
79
+ 'HOST_CONTEXT_INVALID',
80
+ `The registered host driver's ${scope} contribution exceeds ${HOST_CONTEXT_MAX_BYTES[scope]} bytes.`,
81
+ );
82
+ }
83
+ return output;
84
+ }
85
+
86
+ function composedContext(contract, output) {
87
+ return {
88
+ contract,
89
+ status: 'ready',
90
+ output,
91
+ identity: sha256(stableJson({ contract, output })),
92
+ };
93
+ }
94
+
27
95
  async function optionalStackComponentIds(projectRoot, stack, stackPackages) {
28
96
  try {
29
97
  const packages = [...new Set([...(stack?.stackPackages || []), ...stackPackages])];
@@ -34,40 +102,62 @@ async function optionalStackComponentIds(projectRoot, stack, stackPackages) {
34
102
  }
35
103
  }
36
104
 
37
- export async function projectSessionContext({ projectRoot, stackPackages = [] } = {}) {
105
+ export async function projectSessionContext({
106
+ hostDriver,
107
+ hostDriverInput,
108
+ projectRoot,
109
+ stackPackages = [],
110
+ } = {}) {
38
111
  const root = (await gitContext(projectRoot)).repositoryRoot;
39
- const [stack, engineering] = await Promise.all([
112
+ const [stack, collaboration, engineering, hostContext] = await Promise.all([
40
113
  optionalStack(root, stackPackages),
114
+ optionalCollaboration(root),
41
115
  optionalEngineering(root),
116
+ hostContextContribution({ hostDriver, hostDriverInput, scope: 'session' }),
42
117
  ]);
43
118
  const availableComponents = await optionalStackComponentIds(root, stack, stackPackages);
44
119
  const selected = stack?.components.map(({ id }) => id) || [];
45
120
  const stackStatus = stack
46
121
  ? (selected.length > 0 ? selected.join(', ') : 'none')
47
122
  : 'unavailable; run the Genesis `check` operation';
123
+ const output = [
124
+ 'This is a Genesis-enriched project.',
125
+ '- Read `genesis/blueprint.md`, `genesis/collaboration.md`, `genesis/engineering.md`, and `genesis/stack.md` for product intent, collaboration approach, engineering approach, and selected technology.',
126
+ '- 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.',
127
+ '- 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.',
128
+ `- Available Stack components: ${availableComponents.length > 0 ? availableComponents.map((id) => `\`${id}\``).join(', ') : 'none'}.`,
129
+ '- 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.',
130
+ '- 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.',
131
+ '- Use the relevant project Agent Skills below `.agents/skills/`.',
132
+ '- 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.',
133
+ '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
134
+ '- 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.',
135
+ '- 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.',
136
+ '- Deslop only when explicitly requested. Genesis defines its behavior-preserving committed scope; selected Stack components may add technology-specific cleanup guidance.',
137
+ '- This guidance is loaded for a new session and refreshed after compaction. Continue the active request without restarting completed work.',
138
+ `Engineering profile: ${engineering.profile?.id || 'invalid; run the Genesis `check` operation'}.`,
139
+ `Selected Stack components: ${stackStatus}.`,
140
+ '',
141
+ 'ENGINEERING APPROACH',
142
+ '',
143
+ engineering.guidance,
144
+ '',
145
+ 'COLLABORATION APPROACH',
146
+ '',
147
+ collaboration.guidance,
148
+ ...(hostContext ? ['', 'HOST CONTEXT', '', hostContext] : []),
149
+ ].join('\n');
150
+ return composedContext(GENESIS_CONTRACTS.sessionContext, output);
151
+ }
152
+
153
+ export async function projectTurnContext({ hostDriver, hostDriverInput } = {}) {
154
+ const output = await hostContextContribution({
155
+ hostDriver,
156
+ hostDriverInput,
157
+ scope: 'turn',
158
+ });
48
159
  return {
49
- status: 'ready',
50
- output: [
51
- 'This is a Genesis-enriched project.',
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.',
58
- '- Use the relevant project Agent Skills below `.agents/skills/`.',
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.',
60
- '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
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.',
63
- '- Deslop only when explicitly requested. Genesis defines its behavior-preserving committed scope; selected Stack components may add technology-specific cleanup guidance.',
64
- '- This guidance is loaded for a new session and refreshed after compaction. Continue the active request without restarting completed work.',
65
- `Engineering profile: ${engineering.profile?.id || 'invalid; run the Genesis `check` operation'}.`,
66
- `Selected Stack components: ${stackStatus}.`,
67
- '',
68
- 'ENGINEERING APPROACH',
69
- '',
70
- engineering.guidance,
71
- ].join('\n'),
160
+ ...composedContext(GENESIS_CONTRACTS.turnContext, output),
161
+ trust: 'untrusted',
72
162
  };
73
163
  }
@@ -271,8 +271,8 @@ export async function materializeProjectStackContracts({ projectRoot, stackPacka
271
271
 
272
272
  function composedProse(components, field) {
273
273
  return components
274
- .map((piece) => piece[field] || '')
275
- .filter(Boolean)
274
+ .filter((piece) => piece[field])
275
+ .map((piece) => `### \`${piece.id}\`\n\n${piece[field]}`)
276
276
  .join('\n\n');
277
277
  }
278
278
 
package/src/index.js CHANGED
@@ -2,6 +2,10 @@ import path from 'node:path';
2
2
 
3
3
  import { checkProject } from './index/check.js';
4
4
  import { buildProjectIndex, GENESIS_DERIVED_ARTIFACTS } from './index/code-index.js';
5
+ import {
6
+ inspectProjectCollaboration,
7
+ setProjectCollaboration,
8
+ } from './index/collaboration.js';
5
9
  import { GENESIS_CONTRACTS } from './index/contracts.js';
6
10
  import { contextForProjectPaths } from './index/context.js';
7
11
  import { generateProjectPrompt } from './index/prompt.js';
@@ -19,8 +23,21 @@ import { listStackCatalogPieces } from './index/stack-catalog.js';
19
23
  import { addStackPieces, readStack } from './index/stack.js';
20
24
  import { uniqueSorted } from './index/utils.js';
21
25
  import { verifyProject } from './index/verification.js';
22
-
23
- export { GENESIS_CONTRACTS };
26
+ import { projectSessionContext, projectTurnContext } from './index/session-context.js';
27
+ import {
28
+ HOST_CONTEXT_RESOLVER_DATA_ENV,
29
+ HOST_CONTEXT_RESOLVER_ENV,
30
+ SESSION_CONTEXT_INSTALLED_ENV,
31
+ } from './index/host-context-resolver.js';
32
+
33
+ export {
34
+ GENESIS_CONTRACTS,
35
+ HOST_CONTEXT_RESOLVER_DATA_ENV,
36
+ HOST_CONTEXT_RESOLVER_ENV,
37
+ SESSION_CONTEXT_INSTALLED_ENV,
38
+ projectSessionContext,
39
+ projectTurnContext,
40
+ };
24
41
 
25
42
  function withIndexResult(result, index) {
26
43
  const changedFiles = uniqueSorted([...result.changedFiles, ...index.changedFiles]);
@@ -115,6 +132,10 @@ export function inspectEnvironment(options) {
115
132
  return inspectProjectEnvironment(options);
116
133
  }
117
134
 
135
+ export function inspectCollaboration(options) {
136
+ return inspectProjectCollaboration(options);
137
+ }
138
+
118
139
  export function inspectStackSection(options) {
119
140
  return inspectProjectStackSection(options);
120
141
  }
@@ -131,6 +152,10 @@ export function setEngineeringProfile(options) {
131
152
  return selectEngineeringProfile(options);
132
153
  }
133
154
 
155
+ export function setCollaboration(options) {
156
+ return setProjectCollaboration(options);
157
+ }
158
+
134
159
  export function generatePrompt(options) {
135
160
  return generateProjectPrompt(options);
136
161
  }