genesis-compiler 1.2.16 → 1.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@ interaction.
13
13
  ```text
14
14
  genesis/
15
15
  blueprint.md non-technical product intent
16
+ engineering.md selected engineering profile and project-specific requirements
16
17
  stack.md selected technology guidance, verification, declarations, and opaque extensions
17
18
  stack/ optional per-component Description, Guidance, Adoption, Post-change, and Deslop customization
18
19
  program/ concise explanations grouped into conceptual subsystems
@@ -104,8 +105,8 @@ assistant runs `genesis adopt`; the person need not know Genesis terminology or
104
105
  type a command. It never runs adoption without approval and does nothing in an
105
106
  empty or already-adopted project.
106
107
 
107
- `genesis init` creates an empty Blueprint, an empty optional Stack, three
108
- project Agent Skills, and project-local Codex hooks. The skills are ordinary
108
+ `genesis init` creates an empty Blueprint, a focused engineering approach, an
109
+ empty optional Stack, three project Agent Skills, and project-local Codex hooks. The skills are ordinary
109
110
  [Agent Skills](https://agentskills.io): `genesis-project`, `genesis-program`,
110
111
  and `genesis-deslop`, each with standard `SKILL.md` and `agents/openai.yaml`
111
112
  metadata. Open `/hooks` once in Codex to review and trust the hooks. After
@@ -136,6 +137,54 @@ Genesis never overwrites an unmanaged skill and preserves locally modified
136
137
  managed skills. Run `genesis init` after manually editing `genesis/stack.md`,
137
138
  or use `genesis stack add`, to synchronize selected skills.
138
139
 
140
+ ## Engineering approach
141
+
142
+ Every profile inherits one universal rule: implementation must remain easy to
143
+ reason about and be the smallest targeted change that satisfies current
144
+ requirements. Speculative abstractions, infrastructure, compatibility paths,
145
+ distributed patterns, cryptography, and similarly advanced machinery are not
146
+ allowed. When a concrete requirement makes material complexity necessary, the
147
+ agent must explain why a direct design is insufficient and ask before writing
148
+ that complexity.
149
+
150
+ The installed, versioned profile descriptions live in
151
+ `profiles/engineering/*.md`. Straight Genesis users can inspect and select them
152
+ without editing Markdown grammar:
153
+
154
+ ```bash
155
+ genesis engineering list
156
+ genesis engineering show focused.v1
157
+ genesis engineering set durable.v1
158
+ ```
159
+
160
+ `focused.v1` is the default for small direct product work. `durable.v1` adds
161
+ explicit compatibility and operational care for long-lived products.
162
+ `high-assurance.v1` adds risk-driven security and reliability discipline. None
163
+ of them mandates complexity without a concrete need.
164
+
165
+ The selection and any explicit project overrides are portable source:
166
+
167
+ ```markdown
168
+ # Engineering approach
169
+
170
+ ## Profile
171
+
172
+ - `focused.v1`
173
+
174
+ ## Project requirements
175
+
176
+ - Nothing.
177
+ ```
178
+
179
+ People may edit `## Project requirements` to name obligations the presets do
180
+ not capture, including a specific organizational, compliance, cryptographic,
181
+ or scale constraint. `genesis engineering set` changes only the profile and
182
+ preserves those requirements. Generated prompts, path-focused context, Codex
183
+ session guidance, Post-change work, and Deslop all receive the effective
184
+ approach. A project upgraded from an older Genesis release safely uses
185
+ `focused.v1` until `genesis init` or `genesis engineering set focused.v1`
186
+ records the file.
187
+
139
188
  Hosts that manage project environments can call `inspectEnvironment()`.
140
189
  Concrete Stack integrations may declare public, non-secret Environment
141
190
  defaults such as the database driver selected by that integration. Explicit
@@ -323,7 +372,8 @@ The eight task types are deliberately explicit:
323
372
  contracts into Genesis metadata. It composes technology-specific Adoption
324
373
  requirements from the selected Stack and does not change application code.
325
374
  - `work` implements the Blueprint and optional request using current code,
326
- Program, selected Stack context, and progressively loaded Agent Skills.
375
+ Program, the selected engineering approach, selected Stack context, and
376
+ progressively loaded Agent Skills.
327
377
  - `deslop` requests cleanup explicitly. Codex projects also receive a bounded
328
378
  automatic Deslop turn after their separate explanatory reconciliation turn.
329
379
  - `program` edits only `genesis/program/` to explain the code that exists, with
@@ -422,7 +472,7 @@ genesis check
422
472
 
423
473
  `check` is read-only. It reports only structural or recorded facts:
424
474
 
425
- - Blueprint and Stack validity;
475
+ - Blueprint, engineering approach, and Stack validity;
426
476
  - selected Agent Skill presence and structural validity;
427
477
  - Program presence and structural validity;
428
478
  - environment declarations and consumer-owned Stack extensions as `ready`,
@@ -451,10 +501,13 @@ import {
451
501
  getContext,
452
502
  indexCodebase,
453
503
  initialize,
504
+ inspectEngineering,
454
505
  inspectEnvironment,
455
506
  inspectStackSection,
456
507
  installCodex,
508
+ listEngineeringProfiles,
457
509
  listStackPieces,
510
+ setEngineeringProfile,
458
511
  verify,
459
512
  } from 'genesis-compiler';
460
513
  ```
@@ -463,6 +516,9 @@ import {
463
516
  Stack skills, and local Codex hooks.
464
517
  `adoptProject()` also returns the initial `adopt` prompt for an existing
465
518
  codebase. `installCodex()` installs the optional global discovery plugin.
519
+ `listEngineeringProfiles()`, `inspectEngineering()`, and
520
+ `setEngineeringProfile()` expose the same built-in catalog and portable
521
+ project selection used by the CLI and hosts.
466
522
  `getContext()` resolves source paths to the Program modules that cite them,
467
523
  the functions already declared there, selected Stack guidance, available Agent
468
524
  Skills, and verification commands. `indexCodebase()` regenerates or returns the
@@ -474,7 +530,8 @@ projection paths without returning any supplied environment value.
474
530
  interpreting or executing its contents.
475
531
 
476
532
  Normalized results identify their stable public contract in the `contract`
477
- field: `genesis.environment.v1`, `genesis.stack-section.v1`, or
533
+ field: `genesis.engineering.v1`, `genesis.environment.v2`,
534
+ `genesis.stack-section.v1`, or
478
535
  `genesis.verification.v1`. A consumer defines any schema embedded inside the
479
536
  opaque section body.
480
537
 
@@ -2,7 +2,9 @@
2
2
 
3
3
  Genesis reports only facts it can establish mechanically:
4
4
 
5
- 1. Blueprint, Stack, and Program files satisfy their small structural formats.
5
+ 1. Blueprint, Engineering approach, Stack, and Program files satisfy their
6
+ small structural formats, and the selected engineering profile exists in
7
+ the installed versioned catalog.
6
8
  2. Program operations live in conceptual subsystem directories and cite real
7
9
  source files. Optional helper maps are not mechanically certified.
8
10
  3. Stack-declared environment inputs are present and non-placeholder. This does
@@ -17,8 +19,8 @@ Genesis reports only facts it can establish mechanically:
17
19
  City projects the Program modules and source citations that exist. Neither
18
20
  proves semantic equivalence, complete dynamic dispatch, or true duplication.
19
21
 
20
- Prompt text, Stack Description/Guidance/Adoption/Post-change/Deslop prose, Program prose, Agent Skill
21
- content, and agent output are not assurance evidence. Genesis does not claim
22
+ Prompt text, engineering guidance, Stack Description/Guidance/Adoption/Post-change/Deslop prose,
23
+ Program prose, Agent Skill content, and agent output are not assurance evidence. Genesis does not claim
22
24
  that product intent and code have converged, that Program is complete or
23
25
  semantically current, or that passing checks prove the whole product correct.
24
26
 
@@ -40,6 +40,10 @@ Genesis keeps each instruction at one useful level:
40
40
  commands, opaque consumer sections, and an optional authoritative Agent Skill source.
41
41
  - `genesis/blueprint.md`, `genesis/program/`, and `genesis/stack.md` are the
42
42
  project's own intent, explanation, and selected technical composition.
43
+ - `profiles/engineering/*.md` owns the installed versioned engineering
44
+ profiles. `genesis/engineering.md` selects one profile and records explicit
45
+ project requirements. A universal complexity gate is composed ahead of the
46
+ selected profile in every implementation and cleanup path.
43
47
  - The generated JSON block contains current structural facts and warnings.
44
48
 
45
49
  Detailed technology manuals belong in the technology skill's `references/`
@@ -89,7 +93,8 @@ then refreshes them again after Deslop in case cleanup changed the
89
93
  implementation map.
90
94
 
91
95
  Once Codex identifies relevant source, `genesis context <path...>` returns only
92
- Program modules citing those paths plus concise selected Stack context, the
96
+ Program modules citing those paths plus the effective engineering approach and
97
+ concise selected Stack context, the
93
98
  functions structurally indexed in those paths, available Agent Skill catalog,
94
99
  and verification commands. `genesis index <name-or-path>` searches the complete
95
100
  current function inventory and refreshes `.genesis/machine-city.json` and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.2.16",
3
+ "version": "1.2.18",
4
4
  "type": "module",
5
5
  "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with optional Codex hooks.",
6
6
  "repository": {
@@ -38,6 +38,7 @@
38
38
  "prompts/start.txt",
39
39
  "prompts/work.txt",
40
40
  "plugins/genesis",
41
+ "profiles",
41
42
  "skills",
42
43
  "src"
43
44
  ],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.2.16",
3
+ "version": "1.2.18",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -0,0 +1,6 @@
1
+ # Engineering baseline
2
+
3
+ - Every implementation must be easy to reason about and be the smallest targeted change that fully satisfies the known requirements.
4
+ - Prefer direct control flow, existing ownership boundaries, and established project or technology seams.
5
+ - Do not overengineer or overcomplicate the solution. Do not add speculative abstractions, layers, dependencies, infrastructure, compatibility paths, distributed patterns, cryptography, enterprise patterns, or other advanced machinery unless an explicit requirement or a concrete constraint makes it necessary.
6
+ - If good software requires material complexity beyond these rules, stop before writing it. Explain the requirement, why the direct design is insufficient, and the smallest added complexity proposed, then ask the user to approve or clarify the tradeoff.
@@ -0,0 +1,11 @@
1
+ # Durable product
2
+
3
+ ## Description
4
+
5
+ Long-lived product work with explicit compatibility and operational care.
6
+
7
+ ## Guidance
8
+
9
+ - Treat affected public contracts, stored data, migrations, recovery, and operability as first-class requirements.
10
+ - Make ownership and failure handling explicit enough for multiple contributors to maintain safely.
11
+ - Add boundaries or supporting machinery only where a current longevity or operational requirement needs them; keep unaffected work as direct as the focused profile.
@@ -0,0 +1,11 @@
1
+ # Focused
2
+
3
+ ## Description
4
+
5
+ Small, direct changes for ordinary product work.
6
+
7
+ ## Guidance
8
+
9
+ - Optimize for the clearest complete implementation with the fewest moving parts.
10
+ - Keep single-use logic close to its caller and introduce a shared abstraction only when one real shared operation already exists.
11
+ - Do not pre-build extensibility, scale, migration, or organizational machinery for hypothetical future needs.
@@ -0,0 +1,11 @@
1
+ # High assurance
2
+
3
+ ## Description
4
+
5
+ Security- or reliability-critical work backed by explicit risks and evidence.
6
+
7
+ ## Guidance
8
+
9
+ - Establish the relevant threat model, trust boundaries, data sensitivity, availability, compliance, and recovery requirements before making a design they could materially change.
10
+ - Use established, audited primitives; make critical failure paths and ownership explicit; verify the properties on which safety depends.
11
+ - Cryptography, distributed coordination, redundant controls, and similarly advanced machinery still require a concrete risk or obligation. Ask for missing constraints instead of inventing them.
package/prompts/start.txt CHANGED
@@ -13,6 +13,14 @@ the non-technical Blueprint from the user's answers before implementation.
13
13
  If the product direction becomes clear while Stack is still empty, do not end
14
14
  the turn after only writing or summarizing the Blueprint: present the relevant
15
15
  Stack choices in that same turn and ask the user to select one explicitly.
16
+ The default `focused.v1` engineering profile is already suitable for ordinary
17
+ work, so do not force a separate profile question. When the user asks for a
18
+ different engineering approach, or the product clearly has long-lived
19
+ compatibility, operational, security, reliability, or compliance constraints,
20
+ use `availableEngineeringProfiles` to explain only the relevant choices in
21
+ product language. After an explicit choice, run `genesis engineering set
22
+ <profile>`; do not invent or duplicate a built-in profile. Record additional
23
+ explicit obligations in `genesis/engineering.md` under Project requirements.
16
24
  This opening turn owns product direction and Stack selection, not final
17
25
  verification. Do not run `genesis verify` before implementation exists. If
18
26
  structural validation is useful, run `genesis check`; consumer-owned operations
@@ -9,6 +9,11 @@ Review and simplify the current local codebase without changing product
9
9
  behavior. Start with the requested scope, or otherwise the ordinary Git diff
10
10
  and the code immediately around it.
11
11
 
12
+ Apply the engineering approach supplied with the task. If this skill is invoked
13
+ directly, read `genesis/engineering.md` as well as the relevant project context.
14
+ Cleanup must reduce or preserve complexity, never introduce unapproved
15
+ machinery that the selected profile or a concrete requirement does not need.
16
+
12
17
  Consult `.genesis/machine-city.json` or run `genesis index <name-or-path>` to
13
18
  find existing public and internal functions before introducing or consolidating
14
19
  an abstraction. Confirm every apparent duplicate in source and its call sites.
@@ -11,23 +11,26 @@ codebase, tests, Git review, or the coding agent.
11
11
  ## Establish context
12
12
 
13
13
  1. Read `genesis/blueprint.md` for non-technical product intent.
14
- 2. Read `genesis/stack.md` for the selected technical composition, resources,
14
+ 2. Read `genesis/engineering.md` for the selected engineering profile and any
15
+ project-specific requirements.
16
+ 3. Read `genesis/stack.md` for the selected technical composition, resources,
15
17
  and verification commands.
16
- 3. Locate the source involved in the request.
17
- 4. Run `genesis index <name-or-path>` before adding a helper or public operation.
18
+ 4. Locate the source involved in the request.
19
+ 5. Run `genesis index <name-or-path>` before adding a helper or public operation.
18
20
  Reuse an existing function when it already owns the behavior.
19
- 5. Run `genesis context <path...>` for the relevant Program explanations and
21
+ 6. Run `genesis context <path...>` for the relevant Program explanations and
20
22
  selected Stack context.
21
- 6. Load applicable technology skills from `.agents/skills/` or from the
23
+ 7. Load applicable technology skills from `.agents/skills/` or from the
22
24
  agent's own installed skill catalog.
23
25
 
24
26
  For an existing application's first Stack selection, inspect its real setup,
25
- build, and launch commands before relying on component defaults. A selected
27
+ build, and output commands before relying on component defaults. A selected
26
28
  component describes its current foundation; it does not silently port older
27
29
  source. When the existing commands differ, keep the implementation unchanged
28
- and declare exact project-owned `## Workspace setup` and `## Launch` overrides.
29
- Do not claim the inherited component recipe is usable until it matches the
30
- source.
30
+ and declare exact project-owned consumer-operation overrides (for Vibe64,
31
+ `## Workspace setup` and `## Outputs`). Genesis composes those sections as
32
+ opaque text; the named consumer alone owns their meaning and execution. Do not
33
+ claim the inherited component recipe is usable until it matches the source.
31
34
 
32
35
  Program is concise, fallible explanation. Its Sources and optional
33
36
  Implementation maps aid navigation but never substitute for reading code,
@@ -39,14 +42,25 @@ Neither is authority or proof; both may be regenerated with `genesis index`.
39
42
 
40
43
  ## Implement ordinary local work
41
44
 
45
+ - Apply the effective engineering approach supplied by Genesis. Every change
46
+ must be easy to reason about and be the smallest targeted implementation that
47
+ fully meets known requirements. Do not overengineer or overcomplicate it, and
48
+ do not add speculative abstractions, layers,
49
+ dependencies, infrastructure, compatibility paths, distributed patterns,
50
+ cryptography, or other advanced machinery.
51
+ - When a concrete requirement makes material complexity necessary, stop before
52
+ writing it. Explain why the direct design is insufficient and the smallest
53
+ added complexity proposed, then ask the user to approve or clarify the
54
+ tradeoff. Never silently override the engineering approach.
42
55
  - Work directly in the current Git tree and leave useful edits visible in the
43
56
  ordinary diff.
44
57
  - Do not edit the Blueprint, Program, or `.genesis/` during implementation; the
45
- separate reconciliation turn owns explanatory updates. Keep an exact Stack
46
- `## Launch` declaration aligned when the implementation establishes or
47
- intentionally changes how the project is started or how a preview host may request
48
- an application preview identity. Record only capability metadata and
49
- environment variable names there, never environment values or secrets.
58
+ separate reconciliation turn owns explanatory updates. Keep exact
59
+ consumer-owned operation declarations, such as Vibe64 `## Outputs`, aligned
60
+ when implementation intentionally changes the corresponding commands or
61
+ capabilities. Record only capability metadata and environment-variable names
62
+ there, never values or secrets; do not infer or execute the section as
63
+ Genesis behavior.
50
64
  - Follow established project and technology seams instead of creating parallel
51
65
  frameworks, persistence layers, transports, validators, or UI systems.
52
66
  - Never invent unavailable external-resource values or pass literal
package/src/cli.js CHANGED
@@ -9,10 +9,13 @@ import {
9
9
  getContext,
10
10
  indexCodebase,
11
11
  initialize,
12
+ inspectEngineering,
12
13
  inspectEnvironment,
13
14
  inspectStackSection,
14
15
  installCodex,
16
+ listEngineeringProfiles,
15
17
  listStackPieces,
18
+ setEngineeringProfile,
16
19
  verify,
17
20
  } from './index.js';
18
21
  import {
@@ -22,12 +25,16 @@ import {
22
25
  discardCodexTurn,
23
26
  recordCodexTurn,
24
27
  } from './index/codex-hooks.js';
28
+ import { engineeringProfile, readEngineeringBaseline } from './index/engineering.js';
25
29
  import { asDiagnostic, fail } from './index/errors.js';
26
30
 
27
31
  const USAGE = `Usage:
28
32
  genesis init
29
33
  genesis adopt [product guidance...]
30
34
  genesis codex install
35
+ genesis engineering list
36
+ genesis engineering show [profile]
37
+ genesis engineering set <profile>
31
38
  genesis stack list
32
39
  genesis stack add <piece...>
33
40
  genesis context <path...>
@@ -51,7 +58,7 @@ prompt to the agent you already use. Review all edits through the ordinary Git
51
58
  diff, then run genesis verify for the Stack's concrete checks.
52
59
  `;
53
60
 
54
- const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'hook', 'index', 'init', 'inspect', 'prompt', 'stack', 'verify']);
61
+ const COMMANDS = new Set(['adopt', 'check', 'codex', 'context', 'engineering', 'hook', 'index', 'init', 'inspect', 'prompt', 'stack', 'verify']);
55
62
 
56
63
  function parseCommand(argv) {
57
64
  if (argv.length === 0 || argv.includes('--help') || argv.includes('-h') || argv[0] === 'help') {
@@ -95,6 +102,20 @@ function parseCommand(argv) {
95
102
  if (operands[0] === 'list' && operands.length !== 1) {
96
103
  fail('CLI_EXTRA_ARGUMENT', 'Command stack list accepts no extra arguments.');
97
104
  }
105
+ } else if (command === 'engineering') {
106
+ const [action] = operands;
107
+ if (!['list', 'show', 'set'].includes(action)) {
108
+ fail('CLI_ENGINEERING_ACTION_REQUIRED', 'Command engineering requires list, show, or set.');
109
+ }
110
+ if (action === 'list' && operands.length !== 1) {
111
+ fail('CLI_EXTRA_ARGUMENT', 'Command engineering list accepts no extra arguments.');
112
+ }
113
+ if (action === 'show' && operands.length > 2) {
114
+ fail('CLI_EXTRA_ARGUMENT', 'Command engineering show accepts at most one profile.');
115
+ }
116
+ if (action === 'set' && operands.length !== 2) {
117
+ fail('CLI_ENGINEERING_PROFILE_REQUIRED', 'Command engineering set requires exactly one profile.');
118
+ }
98
119
  } else if (command === 'codex') {
99
120
  if (operands.length !== 1 || operands[0] !== 'install') {
100
121
  fail('CLI_CODEX_ACTION_REQUIRED', 'Command codex requires exactly: install.');
@@ -130,6 +151,7 @@ function namedItems(label, values) {
130
151
 
131
152
  function writeCheck(result) {
132
153
  line(process.stdout, `Blueprint: ${result.blueprint}`);
154
+ line(process.stdout, `Engineering approach: ${result.engineering}`);
133
155
  line(process.stdout, `Stack: ${result.stack}`);
134
156
  line(process.stdout, `Agent Skills: ${result.skills}`);
135
157
  line(process.stdout, `Program: ${result.program}`);
@@ -144,6 +166,29 @@ function writeCheck(result) {
144
166
  line(process.stdout, `Check: ${result.status}`);
145
167
  }
146
168
 
169
+ function writeEngineering(result) {
170
+ if (result.action === 'list') {
171
+ for (const profile of result.profiles) {
172
+ line(process.stdout, `${profile.id}: ${profile.name} — ${profile.description}`);
173
+ }
174
+ return;
175
+ }
176
+ if (result.profile) {
177
+ line(process.stdout, `${result.profile.name} (${result.profile.id})`);
178
+ line(process.stdout, result.profile.description);
179
+ }
180
+ if (result.guidance) {
181
+ line(process.stdout, '');
182
+ line(process.stdout, result.guidance);
183
+ }
184
+ if (result.action === 'set') {
185
+ namedItems('Changed files', result.changedFiles);
186
+ line(process.stdout, `engineering: ${result.status}`);
187
+ } else if (result.status) {
188
+ line(process.stdout, `Engineering approach: ${result.status}`);
189
+ }
190
+ }
191
+
147
192
  function writeInspection(result) {
148
193
  const title = result.inspection[0].toUpperCase() + result.inspection.slice(1);
149
194
  line(process.stdout, `${title}: ${result.status}`);
@@ -179,6 +224,10 @@ function writeResult(command, result) {
179
224
  writeCheck(result);
180
225
  return;
181
226
  }
227
+ if (command === 'engineering') {
228
+ writeEngineering(result);
229
+ return;
230
+ }
182
231
  if (command === 'context') {
183
232
  process.stdout.write(result.context.endsWith('\n') ? result.context : `${result.context}\n`);
184
233
  return;
@@ -254,6 +303,36 @@ async function execute({ command, operands, options }, { signal } = {}) {
254
303
  return adoptProject({ projectRoot, request: operands.join(' '), stackPackages });
255
304
  }
256
305
  if (command === 'codex') return installCodex();
306
+ if (command === 'engineering') {
307
+ if (operands[0] === 'list') {
308
+ return { action: 'list', status: 'ok', profiles: await listEngineeringProfiles() };
309
+ }
310
+ if (operands[0] === 'show') {
311
+ if (!operands[1]) return { action: 'show', ...await inspectEngineering({ projectRoot }) };
312
+ const [baseline, profile] = await Promise.all([
313
+ readEngineeringBaseline(),
314
+ engineeringProfile(operands[1]),
315
+ ]);
316
+ return {
317
+ action: 'show',
318
+ status: 'ready',
319
+ profile,
320
+ guidance: [
321
+ '## Universal complexity gate',
322
+ '',
323
+ baseline,
324
+ '',
325
+ '## Profile guidance',
326
+ '',
327
+ profile.guidance,
328
+ ].join('\n'),
329
+ };
330
+ }
331
+ return {
332
+ action: 'set',
333
+ ...await setEngineeringProfile({ profile: operands[1], projectRoot }),
334
+ };
335
+ }
257
336
  if (command === 'stack') {
258
337
  if (operands[0] === 'list') {
259
338
  return { status: 'ok', pieces: await listStackPieces({ projectRoot, stackPackages }) };
@@ -8,11 +8,13 @@ import { inspectVerification } from './project-state.js';
8
8
  import { missingStackResources } from './stack-preflight.js';
9
9
  import { withStackEnvironmentDefaults } from './stack-environment-defaults.js';
10
10
  import { inspectProjectEnvironment } from './environment-files.js';
11
+ import { readEngineering } from './engineering.js';
11
12
 
12
13
  function invalidResult(area, error) {
13
14
  return {
14
15
  status: 'invalid',
15
16
  blueprint: area === 'blueprint' ? 'invalid' : 'valid',
17
+ engineering: area === 'engineering' ? 'invalid' : 'unknown',
16
18
  stack: area === 'stack' ? 'invalid' : 'unknown',
17
19
  skills: area === 'skills' ? 'invalid' : 'unknown',
18
20
  program: 'unknown',
@@ -40,6 +42,13 @@ export async function checkProject({
40
42
  }
41
43
 
42
44
  let stack;
45
+ let engineering;
46
+ try {
47
+ engineering = await readEngineering(root);
48
+ } catch (error) {
49
+ return invalidResult('engineering', error);
50
+ }
51
+
43
52
  try {
44
53
  stack = await readStack(root, { stackPackages });
45
54
  } catch (error) {
@@ -87,6 +96,7 @@ export async function checkProject({
87
96
 
88
97
  const guidance = [];
89
98
  if (program.status === 'missing') guidance.push('Generate an explanatory Program prompt with genesis prompt --task program.');
99
+ if (engineering.status === 'defaulted') guidance.push('Run genesis init or genesis engineering set focused.v1 to record the default engineering approach.');
90
100
  if (program.status === 'invalid') guidance.push('Repair the Program with genesis prompt --task program.');
91
101
  if (skills.status === 'missing') guidance.push('Run genesis init to install selected project Agent Skills.');
92
102
  if (skills.status === 'invalid') guidance.push('Repair the reported Agent Skill or managed-skill manifest.');
@@ -99,6 +109,7 @@ export async function checkProject({
99
109
  guidance.push('Use genesis prompt --task review for a semantic, evidence-based comparison.');
100
110
 
101
111
  const needsAttention = program.status === 'missing'
112
+ || engineering.status === 'defaulted'
102
113
  || skills.status === 'missing'
103
114
  || missingResources.length > 0
104
115
  || extensionDiagnostics.length > 0
@@ -109,6 +120,7 @@ export async function checkProject({
109
120
  ? 'invalid'
110
121
  : needsAttention ? 'attention' : 'ok',
111
122
  blueprint: 'valid',
123
+ engineering: engineering.status,
112
124
  stack: 'valid',
113
125
  skills: skills.status,
114
126
  program: program.status,
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
 
4
4
  import { readInstalledAsset } from './assets.js';
5
5
  import { buildProjectIndex } from './code-index.js';
6
+ import { readEngineering, readEngineeringBaseline } from './engineering.js';
6
7
  import { GenesisError } from './errors.js';
7
8
  import { gitContext } from './git.js';
8
9
  import { isProjectContentPath } from './paths.js';
@@ -115,9 +116,30 @@ async function optionalStack(projectRoot) {
115
116
  try { return await readStack(projectRoot); } catch { return null; }
116
117
  }
117
118
 
119
+ async function optionalEngineering(projectRoot) {
120
+ try {
121
+ return await readEngineering(projectRoot);
122
+ } catch {
123
+ return {
124
+ guidance: [
125
+ '## Universal complexity gate',
126
+ '',
127
+ await readEngineeringBaseline(),
128
+ '',
129
+ 'The project engineering profile is invalid. Do not infer a replacement; run `genesis check` and ask the user before implementation if the selected approach matters.',
130
+ ].join('\n'),
131
+ profile: null,
132
+ status: 'invalid',
133
+ };
134
+ }
135
+ }
136
+
118
137
  export async function codexSessionContext({ projectRoot } = {}) {
119
138
  const root = (await gitContext(projectRoot)).repositoryRoot;
120
- const stack = await optionalStack(root);
139
+ const [stack, engineering] = await Promise.all([
140
+ optionalStack(root),
141
+ optionalEngineering(root),
142
+ ]);
121
143
  const selected = stack?.components.map(({ id }) => id) || [];
122
144
  const stackStatus = stack
123
145
  ? (selected.length > 0 ? selected.join(', ') : 'none')
@@ -127,6 +149,7 @@ export async function codexSessionContext({ projectRoot } = {}) {
127
149
  output: [
128
150
  'This is a Genesis-enriched project.',
129
151
  '- `genesis/blueprint.md` describes non-technical product intent.',
152
+ '- `genesis/engineering.md` selects the project engineering approach and any project-specific requirements.',
130
153
  '- `genesis/stack.md` selects optional technology guidance and verification.',
131
154
  '- `genesis/program/<subsystem>/` explains public operations and useful internal seams.',
132
155
  '- Project Agent Skills live in `.agents/skills/`; load applicable skills progressively.',
@@ -134,9 +157,14 @@ export async function codexSessionContext({ projectRoot } = {}) {
134
157
  '- Before creating a helper or public operation, run `genesis index <name-or-path>` and reuse an existing function when it already owns the behavior.',
135
158
  '- `.genesis/machine-city.json` is the detailed code/function map; `.genesis/program-city.json` is the simpler subsystem/operation map.',
136
159
  '- Program is fallible explanation; code, tests, and runtime behavior remain evidence.',
137
- '- Keep implementation focused; the Stop hook owns optional Stack Post-change work, Blueprint/Program reconciliation, and Deslop turns.',
160
+ '- Apply the engineering approach below to implementation and cleanup; the Stop hook owns optional Stack Post-change work, Blueprint/Program reconciliation, and Deslop turns.',
138
161
  '- After a code-changing turn, Genesis may request separate post-change, explanation, cleanup, and final-summary turns.',
162
+ `Engineering profile: ${engineering.profile?.id || 'invalid; run \`genesis check\`'}.`,
139
163
  `Selected Stack components: ${stackStatus}.`,
164
+ '',
165
+ 'ENGINEERING APPROACH',
166
+ '',
167
+ engineering.guidance,
140
168
  ].join('\n'),
141
169
  };
142
170
  }
@@ -279,9 +307,10 @@ function changedPathLines(changedPaths) {
279
307
  }
280
308
 
281
309
  async function reconciliationContinuation(projectRoot, changedPaths) {
282
- const [instructions, stack] = await Promise.all([
310
+ const [instructions, stack, engineering] = await Promise.all([
283
311
  readInstalledAsset('reconcile'),
284
312
  optionalStack(projectRoot),
313
+ optionalEngineering(projectRoot),
285
314
  ]);
286
315
  return [
287
316
  'This is the automatic Genesis explanation turn for the preceding implementation turn.',
@@ -292,11 +321,15 @@ async function reconciliationContinuation(projectRoot, changedPaths) {
292
321
  ...changedPathLines(changedPaths),
293
322
  '',
294
323
  instructions.trim(),
324
+ '',
325
+ 'ENGINEERING APPROACH',
326
+ '',
327
+ engineering.guidance,
295
328
  ...(stack?.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
296
329
  ].join('\n');
297
330
  }
298
331
 
299
- function postChangeContinuation(changedPaths, stack) {
332
+ function postChangeContinuation(changedPaths, stack, engineering) {
300
333
  return [
301
334
  'This is the one automatic Stack Post-change turn for the preceding implementation.',
302
335
  'Use the same conversation context and the Git-visible changes below.',
@@ -310,14 +343,19 @@ function postChangeContinuation(changedPaths, stack) {
310
343
  'COMPOSED STACK POST-CHANGE WORK',
311
344
  '',
312
345
  stack.postChange,
346
+ '',
347
+ 'ENGINEERING APPROACH',
348
+ '',
349
+ engineering.guidance,
313
350
  ...(stack.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
314
351
  ].join('\n');
315
352
  }
316
353
 
317
354
  async function deslopContinuation(projectRoot, changedPaths) {
318
- const [instructions, stack] = await Promise.all([
355
+ const [instructions, stack, engineering] = await Promise.all([
319
356
  readInstalledAsset('deslop'),
320
357
  optionalStack(projectRoot),
358
+ optionalEngineering(projectRoot),
321
359
  ]);
322
360
  return [
323
361
  'This is the final automatic Genesis Deslop turn for the preceding implementation.',
@@ -328,6 +366,10 @@ async function deslopContinuation(projectRoot, changedPaths) {
328
366
  ...changedPathLines(changedPaths),
329
367
  '',
330
368
  instructions.trim(),
369
+ '',
370
+ 'ENGINEERING APPROACH',
371
+ '',
372
+ engineering.guidance,
331
373
  ...(stack?.guidance ? ['', 'SELECTED STACK GUIDANCE', '', stack.guidance] : []),
332
374
  ...(stack?.deslop ? ['', 'SELECTED STACK CLEANUP GUIDANCE', '', stack.deslop] : []),
333
375
  '',
@@ -360,7 +402,10 @@ export async function completeCodexTurn({ input, projectRoot } = {}) {
360
402
  await rm(statePath, { force: true });
361
403
  return {};
362
404
  }
363
- const stack = await optionalStack(root);
405
+ const [stack, engineering] = await Promise.all([
406
+ optionalStack(root),
407
+ optionalEngineering(root),
408
+ ]);
364
409
  const postChange = Boolean(stack?.postChange?.trim());
365
410
  await writeTurnState(statePath, {
366
411
  ...state,
@@ -370,7 +415,7 @@ export async function completeCodexTurn({ input, projectRoot } = {}) {
370
415
  return {
371
416
  decision: 'block',
372
417
  reason: postChange
373
- ? postChangeContinuation(changedPaths, stack)
418
+ ? postChangeContinuation(changedPaths, stack, engineering)
374
419
  : await reconciliationContinuation(root, changedPaths),
375
420
  };
376
421
  }
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { inspectProjectSkills, renderAgentSkillCatalog } from './agent-skills.js';
5
5
  import { buildProjectIndex } from './code-index.js';
6
6
  import { asDiagnostic, GenesisError } from './errors.js';
7
+ import { readEngineering } from './engineering.js';
7
8
  import { gitContext } from './git.js';
8
9
  import { inspectProgram } from './program.js';
9
10
  import { readStack } from './stack.js';
@@ -56,13 +57,14 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
56
57
  const location = await gitContext(projectRoot);
57
58
  const root = location.repositoryRoot;
58
59
  const targets = [...new Set(paths.map((value) => projectPath(root, location.workingDirectory, value)))];
59
- const [program, stack] = await Promise.all([
60
+ const [program, stack, engineering] = await Promise.all([
60
61
  inspectProgram(root).catch((error) => ({
61
62
  status: 'invalid',
62
63
  modules: [],
63
64
  diagnostic: asDiagnostic(error),
64
65
  })),
65
66
  readStack(root, { stackPackages }),
67
+ readEngineering(root),
66
68
  ]);
67
69
  const modules = program.modules.filter((module) => targets.some((target) => citesTarget(module, target)));
68
70
  const moduleSources = await Promise.all(modules.map(async (module) => ({
@@ -98,6 +100,12 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
98
100
  '## Relevant Program',
99
101
  '',
100
102
  ...programDetails,
103
+ '## Engineering approach',
104
+ '',
105
+ `Profile: \`${engineering.profile.id}\` — ${engineering.profile.description}`,
106
+ '',
107
+ engineering.guidance,
108
+ '',
101
109
  ...stackSummary(stack),
102
110
  ...stackGuidance(stack),
103
111
  '',
@@ -125,6 +133,7 @@ export async function contextForProjectPaths({ paths, projectRoot, stackPackages
125
133
  paths: targets,
126
134
  modules: moduleSources.map(({ source: _source, ...module }) => module),
127
135
  components: stack.components.map(({ id }) => id),
136
+ engineeringProfile: engineering.profile.id,
128
137
  verificationCommands: stack.verificationCommands.map(({ label, argv }) => ({ label, argv })),
129
138
  warnings: [
130
139
  ...(program.diagnostic ? [program.diagnostic] : []),
@@ -1,5 +1,6 @@
1
1
  export const GENESIS_CONTRACTS = Object.freeze({
2
2
  derivedArtifacts: 'genesis.derived-artifacts.v1',
3
+ engineering: 'genesis.engineering.v1',
3
4
  environment: 'genesis.environment.v2',
4
5
  stackSection: 'genesis.stack-section.v1',
5
6
  verification: 'genesis.verification.v1',
@@ -0,0 +1,274 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { GENESIS_CONTRACTS } from './contracts.js';
5
+ import { GenesisError } from './errors.js';
6
+ import { gitContext } from './git.js';
7
+ import { ENGINEERING_PATH } from './paths.js';
8
+ import { normalizeSource, writeFileAtomic } from './utils.js';
9
+
10
+ const PROFILE_ID_PATTERN = /^[a-z][a-z0-9-]*\.v[1-9][0-9]*$/u;
11
+ const PROFILE_IDS = Object.freeze(['focused.v1', 'durable.v1', 'high-assurance.v1']);
12
+ const PROFILE_ASSETS = new Map(PROFILE_IDS.map((id) => [
13
+ id,
14
+ new URL(`../../profiles/engineering/${id}.md`, import.meta.url),
15
+ ]));
16
+ const BASELINE_ASSET = new URL('../../profiles/engineering/baseline.md', import.meta.url);
17
+
18
+ export const DEFAULT_ENGINEERING_PROFILE_ID = 'focused.v1';
19
+ export const ENGINEERING_SKELETON_SOURCE = `# Engineering approach
20
+
21
+ ## Profile
22
+
23
+ - \`${DEFAULT_ENGINEERING_PROFILE_ID}\`
24
+
25
+ ## Project requirements
26
+
27
+ - Nothing.
28
+ `;
29
+
30
+ function normalizeProfileId(value) {
31
+ const id = String(value ?? '').trim();
32
+ if (!PROFILE_ID_PATTERN.test(id)) {
33
+ throw new GenesisError(
34
+ 'ENGINEERING_PROFILE_INVALID',
35
+ 'Engineering profile ids use a lowercase name followed by a version, such as `focused.v1`.',
36
+ { profile: value },
37
+ );
38
+ }
39
+ return id;
40
+ }
41
+
42
+ function sections(source, { title, sourcePath }) {
43
+ const lines = normalizeSource(source).split('\n');
44
+ if (lines.filter((line) => line.trim() === title).length !== 1) {
45
+ throw new GenesisError('ENGINEERING_INVALID', `${sourcePath} needs exactly one \`${title}\` title.`);
46
+ }
47
+ const result = new Map();
48
+ let current = null;
49
+ for (const line of lines) {
50
+ const heading = line.match(/^##\s+(.+?)\s*$/u);
51
+ if (heading) {
52
+ if (result.has(heading[1])) {
53
+ throw new GenesisError('ENGINEERING_INVALID', `Duplicate engineering section: ${heading[1]}.`, {
54
+ path: sourcePath,
55
+ });
56
+ }
57
+ current = [];
58
+ result.set(heading[1], current);
59
+ } else if (current) {
60
+ current.push(line);
61
+ } else if (line.trim() && line.trim() !== title) {
62
+ throw new GenesisError('ENGINEERING_INVALID', `${sourcePath} contains content outside an engineering section.`);
63
+ }
64
+ }
65
+ return result;
66
+ }
67
+
68
+ function sectionText(all, name) {
69
+ return (all.get(name) || []).join('\n').trim();
70
+ }
71
+
72
+ function parseProfileSource(source, { id }) {
73
+ const normalized = normalizeSource(source);
74
+ const titleMatch = normalized.match(/^# ([^\r\n]+)[ \t]*$/mu);
75
+ if (!titleMatch || normalized.match(/^# [^\r\n]+[ \t]*$/gmu)?.length !== 1) {
76
+ throw new GenesisError('ENGINEERING_PROFILE_INVALID', `Engineering profile ${id} needs one title.`);
77
+ }
78
+ const all = sections(normalized, {
79
+ sourcePath: `profiles/engineering/${id}.md`,
80
+ title: titleMatch[0].trim(),
81
+ });
82
+ const unknown = [...all.keys()].filter((name) => !['Description', 'Guidance'].includes(name));
83
+ const description = sectionText(all, 'Description');
84
+ const guidance = sectionText(all, 'Guidance');
85
+ if (unknown.length > 0 || !description || !guidance) {
86
+ throw new GenesisError(
87
+ 'ENGINEERING_PROFILE_INVALID',
88
+ `Engineering profile ${id} needs only non-empty Description and Guidance sections.`,
89
+ { profile: id },
90
+ );
91
+ }
92
+ return Object.freeze({ id, name: titleMatch[1].trim(), description, guidance });
93
+ }
94
+
95
+ export async function engineeringProfile(value) {
96
+ const id = normalizeProfileId(value);
97
+ const location = PROFILE_ASSETS.get(id);
98
+ if (!location) {
99
+ throw new GenesisError(
100
+ 'ENGINEERING_PROFILE_UNKNOWN',
101
+ `No installed engineering profile exists for ${id}.`,
102
+ { profile: id },
103
+ );
104
+ }
105
+ return parseProfileSource(await readFile(location, 'utf8'), { id });
106
+ }
107
+
108
+ export async function readEngineeringBaseline() {
109
+ const source = normalizeSource(await readFile(BASELINE_ASSET, 'utf8')).trim();
110
+ if (!/^# Engineering baseline[ \t]*$/mu.test(source)) {
111
+ throw new GenesisError('ENGINEERING_PROFILE_INVALID', 'The installed engineering baseline is invalid.');
112
+ }
113
+ return source.replace(/^# Engineering baseline[ \t]*\n?/mu, '').trim();
114
+ }
115
+
116
+ export function parseEngineeringSource(source) {
117
+ const normalized = normalizeSource(source);
118
+ const all = sections(normalized, {
119
+ sourcePath: ENGINEERING_PATH,
120
+ title: '# Engineering approach',
121
+ });
122
+ const unknown = [...all.keys()].filter((name) => !['Profile', 'Project requirements'].includes(name));
123
+ if (unknown.length > 0 || !all.has('Profile') || !all.has('Project requirements')) {
124
+ throw new GenesisError(
125
+ 'ENGINEERING_INVALID',
126
+ `${ENGINEERING_PATH} needs only Profile and Project requirements sections.`,
127
+ { path: ENGINEERING_PATH },
128
+ );
129
+ }
130
+ const profileLines = (all.get('Profile') || []).filter((line) => line.trim());
131
+ const match = profileLines.length === 1
132
+ ? profileLines[0].trim().match(/^- `([^`]+)`$/u)
133
+ : null;
134
+ if (!match) {
135
+ throw new GenesisError(
136
+ 'ENGINEERING_INVALID',
137
+ 'The engineering Profile must be exactly one bullet containing a backticked profile id.',
138
+ { path: ENGINEERING_PATH },
139
+ );
140
+ }
141
+ const requirementsSource = sectionText(all, 'Project requirements');
142
+ if (!requirementsSource) {
143
+ throw new GenesisError(
144
+ 'ENGINEERING_INVALID',
145
+ 'Project requirements must contain requirements or `- Nothing.`.',
146
+ { path: ENGINEERING_PATH },
147
+ );
148
+ }
149
+ if (Buffer.byteLength(requirementsSource, 'utf8') > 64 * 1024) {
150
+ throw new GenesisError('ENGINEERING_INVALID', 'Project engineering requirements exceed 64 KiB.', {
151
+ path: ENGINEERING_PATH,
152
+ });
153
+ }
154
+ return {
155
+ path: ENGINEERING_PATH,
156
+ profileId: normalizeProfileId(match[1]),
157
+ requirements: requirementsSource === '- Nothing.' ? '' : requirementsSource,
158
+ source: normalized,
159
+ };
160
+ }
161
+
162
+ function renderEngineering({ profileId, requirements = '' }) {
163
+ return [
164
+ '# Engineering approach',
165
+ '',
166
+ '## Profile',
167
+ '',
168
+ `- \`${profileId}\``,
169
+ '',
170
+ '## Project requirements',
171
+ '',
172
+ requirements.trim() || '- Nothing.',
173
+ '',
174
+ ].join('\n');
175
+ }
176
+
177
+ function effectiveGuidance({ baseline, profile, requirements }) {
178
+ return [
179
+ '## Universal complexity gate',
180
+ '',
181
+ baseline,
182
+ '',
183
+ `## Selected profile: ${profile.name} (\`${profile.id}\`)`,
184
+ '',
185
+ profile.guidance,
186
+ ...(requirements ? [
187
+ '',
188
+ '## Project requirements',
189
+ '',
190
+ requirements,
191
+ ] : []),
192
+ ].join('\n');
193
+ }
194
+
195
+ export async function readEngineering(projectRoot) {
196
+ const location = path.join(projectRoot, ENGINEERING_PATH);
197
+ let parsed;
198
+ let status = 'configured';
199
+ try {
200
+ parsed = parseEngineeringSource(await readFile(location, 'utf8'));
201
+ } catch (error) {
202
+ if (!['ENOENT', 'ENOTDIR'].includes(error?.code)) throw error;
203
+ parsed = parseEngineeringSource(ENGINEERING_SKELETON_SOURCE);
204
+ status = 'defaulted';
205
+ }
206
+ const [baseline, profile] = await Promise.all([
207
+ readEngineeringBaseline(),
208
+ engineeringProfile(parsed.profileId),
209
+ ]);
210
+ return {
211
+ contract: GENESIS_CONTRACTS.engineering,
212
+ path: ENGINEERING_PATH,
213
+ status,
214
+ profile,
215
+ requirements: parsed.requirements,
216
+ source: status === 'configured' ? parsed.source : null,
217
+ guidance: effectiveGuidance({ baseline, profile, requirements: parsed.requirements }),
218
+ };
219
+ }
220
+
221
+ export function engineeringPromptContext(engineering) {
222
+ return {
223
+ path: engineering.path,
224
+ status: engineering.status,
225
+ profile: {
226
+ id: engineering.profile.id,
227
+ name: engineering.profile.name,
228
+ description: engineering.profile.description,
229
+ },
230
+ requirements: engineering.requirements || 'Nothing.',
231
+ };
232
+ }
233
+
234
+ export function listEngineeringProfileCatalog() {
235
+ return Promise.all(PROFILE_IDS.map(engineeringProfile));
236
+ }
237
+
238
+ export async function inspectProjectEngineering({ projectRoot } = {}) {
239
+ const root = (await gitContext(projectRoot)).repositoryRoot;
240
+ const [engineering, profiles] = await Promise.all([
241
+ readEngineering(root),
242
+ listEngineeringProfileCatalog(),
243
+ ]);
244
+ return {
245
+ contract: engineering.contract,
246
+ path: engineering.path,
247
+ status: engineering.status,
248
+ profile: engineering.profile,
249
+ profiles,
250
+ requirements: engineering.requirements,
251
+ guidance: engineering.guidance,
252
+ };
253
+ }
254
+
255
+ export async function selectEngineeringProfile({ profile, projectRoot } = {}) {
256
+ const root = (await gitContext(projectRoot)).repositoryRoot;
257
+ const selected = await engineeringProfile(profile);
258
+ const current = await readEngineering(root);
259
+ const rendered = renderEngineering({
260
+ profileId: selected.id,
261
+ requirements: current.requirements,
262
+ });
263
+ const changed = current.source !== rendered;
264
+ if (changed) await writeFileAtomic(path.join(root, ENGINEERING_PATH), rendered);
265
+ return {
266
+ contract: GENESIS_CONTRACTS.engineering,
267
+ status: changed ? 'updated' : 'unchanged',
268
+ summary: changed
269
+ ? `Selected engineering profile: ${selected.id}.`
270
+ : `Engineering profile ${selected.id} is already selected.`,
271
+ profile: selected,
272
+ changedFiles: changed ? [ENGINEERING_PATH] : [],
273
+ };
274
+ }
package/src/index/init.js CHANGED
@@ -4,8 +4,9 @@ import path from 'node:path';
4
4
  import { syncProjectSkills } from './agent-skills.js';
5
5
  import { BLUEPRINT_SKELETON_SOURCE } from './blueprint.js';
6
6
  import { installCodexHooks } from './codex-hooks.js';
7
+ import { ENGINEERING_SKELETON_SOURCE } from './engineering.js';
7
8
  import { gitContext } from './git.js';
8
- import { BLUEPRINT_PATH, PROGRAM_ROOT, STACK_PATH } from './paths.js';
9
+ import { BLUEPRINT_PATH, ENGINEERING_PATH, PROGRAM_ROOT, STACK_PATH } from './paths.js';
9
10
  import { EMPTY_STACK_SOURCE, readStack } from './stack.js';
10
11
 
11
12
  async function createIfMissing(projectRoot, relativePath, source) {
@@ -24,6 +25,7 @@ export async function initializeProject({ projectRoot, stackPackages = [] } = {}
24
25
  const root = (await gitContext(projectRoot)).repositoryRoot;
25
26
  const created = (await Promise.all([
26
27
  createIfMissing(root, BLUEPRINT_PATH, BLUEPRINT_SKELETON_SOURCE),
28
+ createIfMissing(root, ENGINEERING_PATH, ENGINEERING_SKELETON_SOURCE),
27
29
  createIfMissing(root, STACK_PATH, EMPTY_STACK_SOURCE),
28
30
  ])).filter(Boolean);
29
31
  await mkdir(path.join(root, PROGRAM_ROOT), { recursive: true });
@@ -42,6 +44,7 @@ export async function initializeProject({ projectRoot, stackPackages = [] } = {}
42
44
  'Open Codex and use /hooks to review and trust the project hooks.',
43
45
  'Genesis workflow skills are available in .agents/skills/.',
44
46
  'Describe product intent in genesis/blueprint.md.',
47
+ 'Choose the project engineering approach with genesis engineering set <profile>.',
45
48
  'Stack components are optional; add them with genesis stack add <piece...>.',
46
49
  ].join(' '),
47
50
  };
@@ -1,4 +1,5 @@
1
1
  export const BLUEPRINT_PATH = 'genesis/blueprint.md';
2
+ export const ENGINEERING_PATH = 'genesis/engineering.md';
2
3
  export const STACK_PATH = 'genesis/stack.md';
3
4
  export const PROGRAM_ROOT = 'genesis/program';
4
5
  export const VERIFICATION_PATH = '.genesis/verification.json';
@@ -2,6 +2,11 @@ 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 {
6
+ engineeringPromptContext,
7
+ listEngineeringProfileCatalog,
8
+ readEngineering,
9
+ } from './engineering.js';
5
10
  import { readStack, stackPromptContext } from './stack.js';
6
11
  import { listStackCatalogPieces } from './stack-catalog.js';
7
12
  import { normalizeStackPieceId } from './stack-piece.js';
@@ -103,6 +108,7 @@ function renderPrompt({
103
108
  guidance = '',
104
109
  cleanup = '',
105
110
  adoption = '',
111
+ engineeringGuidance = '',
106
112
  }) {
107
113
  return [
108
114
  instructions.trim(),
@@ -116,6 +122,7 @@ function renderPrompt({
116
122
  '```json',
117
123
  stableJson(context).trimEnd(),
118
124
  '```',
125
+ ...(engineeringGuidance ? ['', 'ENGINEERING APPROACH', '', engineeringGuidance] : []),
119
126
  ...(guidance ? ['', 'SELECTED STACK GUIDANCE', '', guidance] : []),
120
127
  ...(adoption ? ['', 'SELECTED STACK ADOPTION GUIDANCE', '', adoption] : []),
121
128
  ...(skills ? ['', 'AVAILABLE AGENT SKILLS', '', skills] : []),
@@ -124,7 +131,7 @@ function renderPrompt({
124
131
  ].join('\n');
125
132
  }
126
133
 
127
- async function generateAdoptionPrompt({ environment, instructions, program, request, root, stackPackages }) {
134
+ async function generateAdoptionPrompt({ engineering, environment, instructions, program, request, root, stackPackages }) {
128
135
  const blueprint = await readBlueprint(root, { required: true });
129
136
  const stack = await readStack(root, { stackPackages });
130
137
  const availableStackPackages = [...new Set([...stack.stackPackages, ...stackPackages])];
@@ -148,6 +155,7 @@ async function generateAdoptionPrompt({ environment, instructions, program, requ
148
155
  projectRoot: root,
149
156
  projectKind: 'existing',
150
157
  blueprint: { path: blueprint.path, source: blueprint.source },
158
+ engineering: engineeringPromptContext(engineering),
151
159
  stack: stackPromptContext(stack),
152
160
  availableStackPieces: stackCatalogContext(catalog),
153
161
  program: programContext(program),
@@ -156,6 +164,7 @@ async function generateAdoptionPrompt({ environment, instructions, program, requ
156
164
  : { status: 'present' },
157
165
  codeIndex: codeIndexContext(index),
158
166
  },
167
+ engineeringGuidance: engineering.guidance,
159
168
  guidance: stack.guidance,
160
169
  adoption: stack.adoption,
161
170
  skills: renderAgentSkillCatalog(projectSkills.skills),
@@ -170,7 +179,7 @@ async function generateAdoptionPrompt({ environment, instructions, program, requ
170
179
  };
171
180
  }
172
181
 
173
- async function generateExplanationPrompt({ instructions, program, request, root, stackPackages, task }) {
182
+ async function generateExplanationPrompt({ engineering, instructions, program, request, root, stackPackages, task }) {
174
183
  const blueprint = await readBlueprint(root, {
175
184
  required: true,
176
185
  requireDescription: task === 'program',
@@ -193,10 +202,12 @@ async function generateExplanationPrompt({ instructions, program, request, root,
193
202
  task,
194
203
  projectRoot: root,
195
204
  blueprint: blueprintContext,
205
+ engineering: engineeringPromptContext(engineering),
196
206
  stack: stackPromptContext(stack),
197
207
  program: programContext(program),
198
208
  codeIndex: codeIndexContext(index),
199
209
  },
210
+ engineeringGuidance: engineering.guidance,
200
211
  guidance: stack.guidance,
201
212
  skills: renderAgentSkillCatalog(projectSkills.skills),
202
213
  }),
@@ -211,12 +222,15 @@ async function generateExplanationPrompt({ instructions, program, request, root,
211
222
 
212
223
  async function generateStartPrompt({
213
224
  hiddenStackPieces,
225
+ engineering,
214
226
  instructions,
215
227
  program,
216
228
  request,
217
229
  root,
218
230
  stackPackages,
219
231
  }) {
232
+ const availableEngineeringProfiles = (await listEngineeringProfileCatalog())
233
+ .map(({ id, name, description }) => ({ id, name, description }));
220
234
  let blueprint;
221
235
  try {
222
236
  blueprint = await readBlueprint(root, { required: true });
@@ -236,8 +250,11 @@ async function generateStartPrompt({
236
250
  projectRoot: root,
237
251
  projectKind: 'existing-uninitialized',
238
252
  genesis: { initialized: false },
253
+ engineering: engineeringPromptContext(engineering),
254
+ availableEngineeringProfiles,
239
255
  program: programContext(program),
240
256
  },
257
+ engineeringGuidance: engineering.guidance,
241
258
  }),
242
259
  warnings: program.diagnostic ? [program.diagnostic] : [],
243
260
  verificationCommands: [],
@@ -265,11 +282,14 @@ async function generateStartPrompt({
265
282
  path: blueprint.path,
266
283
  description: blueprint.description,
267
284
  },
285
+ engineering: engineeringPromptContext(engineering),
286
+ availableEngineeringProfiles,
268
287
  stack: stackPromptContext(stack),
269
288
  availableStackPieces: stackCatalogContext(catalog, hiddenStackPieces),
270
289
  program: programContext(program),
271
290
  codeIndex: codeIndexContext(index),
272
291
  },
292
+ engineeringGuidance: engineering.guidance,
273
293
  guidance: stack.guidance,
274
294
  skills: renderAgentSkillCatalog(projectSkills.skills),
275
295
  }),
@@ -295,7 +315,10 @@ export async function generateProjectPrompt({
295
315
  }
296
316
  const root = (await gitContext(projectRoot)).repositoryRoot;
297
317
  const userRequest = requestText(request, task);
298
- const instructions = await readInstalledAsset(task);
318
+ const [instructions, engineering] = await Promise.all([
319
+ readInstalledAsset(task),
320
+ readEngineering(root),
321
+ ]);
299
322
 
300
323
  if (task === 'blueprint') {
301
324
  const blueprint = await readBlueprint(root);
@@ -310,7 +333,9 @@ export async function generateProjectPrompt({
310
333
  path: 'genesis/blueprint.md',
311
334
  source: blueprint?.source || BLUEPRINT_SKELETON_SOURCE,
312
335
  },
336
+ engineering: engineeringPromptContext(engineering),
313
337
  },
338
+ engineeringGuidance: engineering.guidance,
314
339
  skills: renderAgentSkillCatalog(projectSkills.skills),
315
340
  });
316
341
  return {
@@ -327,6 +352,7 @@ export async function generateProjectPrompt({
327
352
  return generateStartPrompt({
328
353
  instructions,
329
354
  hiddenStackPieces,
355
+ engineering,
330
356
  program,
331
357
  request: userRequest,
332
358
  root,
@@ -336,6 +362,7 @@ export async function generateProjectPrompt({
336
362
  if (task === 'adopt') {
337
363
  return generateAdoptionPrompt({
338
364
  environment,
365
+ engineering,
339
366
  instructions,
340
367
  program,
341
368
  request: userRequest,
@@ -352,6 +379,7 @@ export async function generateProjectPrompt({
352
379
  return generateStartPrompt({
353
380
  instructions: await readInstalledAsset('start'),
354
381
  hiddenStackPieces,
382
+ engineering,
355
383
  program,
356
384
  request: userRequest,
357
385
  root,
@@ -362,6 +390,7 @@ export async function generateProjectPrompt({
362
390
  return generateStartPrompt({
363
391
  instructions: await readInstalledAsset('start'),
364
392
  hiddenStackPieces,
393
+ engineering,
365
394
  program,
366
395
  request: userRequest,
367
396
  root,
@@ -372,6 +401,7 @@ export async function generateProjectPrompt({
372
401
  if (['describe', 'program'].includes(task)) {
373
402
  return generateExplanationPrompt({
374
403
  instructions,
404
+ engineering,
375
405
  program,
376
406
  request: userRequest,
377
407
  root,
@@ -400,6 +430,7 @@ export async function generateProjectPrompt({
400
430
  task,
401
431
  projectRoot: root,
402
432
  blueprint: { path: blueprint.path, description: blueprint.description },
433
+ engineering: engineeringPromptContext(engineering),
403
434
  stack: stackPromptContext(stack),
404
435
  program: programContext(program),
405
436
  resourceInputs: missing.length > 0
@@ -417,6 +448,7 @@ export async function generateProjectPrompt({
417
448
  instructions,
418
449
  request: userRequest,
419
450
  context,
451
+ engineeringGuidance: engineering.guidance,
420
452
  guidance: stack.guidance,
421
453
  skills: renderAgentSkillCatalog(projectSkills.skills),
422
454
  cleanup: task === 'deslop' ? stack.deslop : '',
package/src/index.js CHANGED
@@ -5,6 +5,11 @@ import { buildProjectIndex, GENESIS_DERIVED_ARTIFACTS } from './index/code-index
5
5
  import { GENESIS_CONTRACTS } from './index/contracts.js';
6
6
  import { contextForProjectPaths } from './index/context.js';
7
7
  import { generateProjectPrompt } from './index/prompt.js';
8
+ import {
9
+ inspectProjectEngineering,
10
+ listEngineeringProfileCatalog,
11
+ selectEngineeringProfile,
12
+ } from './index/engineering.js';
8
13
  import { initializeProject } from './index/init.js';
9
14
  import { installCodexPlugin } from './index/codex-plugin.js';
10
15
  import { inspectProjectEnvironment } from './index/environment-files.js';
@@ -98,6 +103,18 @@ export function inspectStackSection(options) {
98
103
  return inspectProjectStackSection(options);
99
104
  }
100
105
 
106
+ export function listEngineeringProfiles() {
107
+ return listEngineeringProfileCatalog();
108
+ }
109
+
110
+ export function inspectEngineering(options) {
111
+ return inspectProjectEngineering(options);
112
+ }
113
+
114
+ export function setEngineeringProfile(options) {
115
+ return selectEngineeringProfile(options);
116
+ }
117
+
101
118
  export function generatePrompt(options) {
102
119
  return generateProjectPrompt(options);
103
120
  }