genesis-compiler 1.10.7 → 1.10.9

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
@@ -294,6 +294,10 @@ than in a host-private policy or a repeated user-message prefix:
294
294
 
295
295
  - `concise`
296
296
 
297
+ ## Long checks
298
+
299
+ - `conservative`
300
+
297
301
  ## Project requirements
298
302
 
299
303
  - Nothing.
@@ -303,7 +307,7 @@ Inspect or change it without parsing Markdown yourself:
303
307
 
304
308
  ```bash
305
309
  genesis collaboration show
306
- genesis collaboration set direct balanced expert conclusions "Be candid."
310
+ genesis collaboration set direct balanced expert conclusions normal "Be candid."
307
311
  ```
308
312
 
309
313
  Genesis owns the supported choices and their concise expansions. Authored
@@ -315,6 +319,26 @@ Updates should describe actual progress, findings, decisions, or blockers,
315
319
  rather than announce compliance with guidance or reassure the user about
316
320
  unrelated behavior staying unchanged.
317
321
 
322
+ The Long checks choice sets how agents handle test suites, builds and other long
323
+ checks. `conservative` runs each one as a single unattended, time-limited
324
+ command that writes its output to a local file and ends with one completion
325
+ signal; the agent does not watch it while it runs and passes this on to agents
326
+ it delegates checks to. It saves the most tokens, and results arrive only when
327
+ each check ends. `normal` lets the agent follow a check's progress and stop a
328
+ clearly broken run early, reacting sooner for more tokens. Both keep the
329
+ Engineering baseline's time limit, local output files, exit status and concise
330
+ evidence reading.
331
+
332
+ The choice belongs to the person, so Genesis never makes it for them. `genesis
333
+ init`, migration and `setCollaboration()` calls that do not pass `longChecks`
334
+ leave `## Long checks` out. While it is absent, `longChecks` is `null` and the
335
+ session guidance asks the agent to put both options to the person once, early,
336
+ before the first long check, to record the answer with `genesis collaboration
337
+ set` (the guidance spells out the command with the current choices), and to use
338
+ `conservative` until then. Once the section exists, the guidance carries only
339
+ the chosen option. A present section with any other value is rejected with the
340
+ valid choices listed.
341
+
318
342
  Persistent hosts should install `projectSessionContext()` once when a provider
319
343
  conversation is created or refreshed. They may then generate task prompts with
320
344
  `sessionContextInstalled: true`, preventing the stable Collaboration and
@@ -180,8 +180,8 @@ Genesis keeps each instruction at one useful level:
180
180
  - `genesis/blueprint.md`, `genesis/program/`, and `genesis/stack.md` are the
181
181
  project's own intent, explanation, and selected technical composition.
182
182
  - `genesis/collaboration.md` selects Genesis-owned tone, response-length,
183
- assumed-experience, and explanation-style instructions plus optional authored
184
- project requirements.
183
+ assumed-experience, explanation-style, and long-checks instructions plus
184
+ optional authored project requirements.
185
185
  - `profiles/engineering/*.md` owns the installed versioned engineering
186
186
  profiles. `genesis/engineering.md` selects one profile and records explicit
187
187
  project requirements. A universal complexity gate is composed ahead of the
@@ -223,8 +223,11 @@ the installed Skill do not rewrite what an already-run command proved.
223
223
 
224
224
  `City regions` and `Indexers` control only the derived structural maps. Indexers
225
225
  identify language adapters. City regions can exclude test/spec paths and group
226
- indexed files into deterministic regions and campuses. Both projections are
227
- navigation data, not proof of complete runtime behavior.
226
+ indexed files into deterministic regions and campuses. A project adds its own
227
+ exclusions, such as example fixtures, in a `## City exclusions` section of
228
+ `genesis/stack.md` with the same ``- Ignore `glob` `` lines (or `- Nothing.`);
229
+ they add to the component exclusions. Both projections are navigation data, not
230
+ proof of complete runtime behavior.
228
231
 
229
232
  ## Project customization
230
233
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.7",
3
+ "version": "1.10.9",
4
4
  "type": "module",
5
5
  "description": "An agent-independent prompt, multi-language code-index, cleanup, and verification companion with project agent guidance.",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.7",
3
+ "version": "1.10.9",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -5,5 +5,5 @@
5
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
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.
7
7
  - During implementation, run the relevant checks and batch related cases when they share setup. Follow the project's verification policy for broad or final checks; do not repeat them merely because an item finishes or a session resumes. Preserve required coverage and isolation.
8
- - Keep complete test output in local artifacts and preserve exit status. Read concise counts, timings and actionable failure excerpts first, expanding evidence only when needed. Do not load full passing reports or repeatedly poll unchanged logs into agent context. Retain startup and cleanup failures; retry only after a change or new evidence justifies it.
8
+ - Bound each long check by a time limit so it ends even when it hangs; write its complete output to a local file and preserve its exit status. Afterwards read concise counts, timings and actionable failure excerpts first, expanding evidence only when needed; never load full passing reports into agent context. Retain startup and cleanup failures; retry only after a change or new evidence justifies it.
9
9
  - For browser verification, batch related actions and return only the relevant values or page region. Capture screenshots at meaningful visual checkpoints or failures, not after every action. Preserve required viewport, identity and security coverage; prefer existing automated cases for repeated checks.
@@ -151,13 +151,26 @@ technology's workspace substrate exists. Finishing an item or resuming a
151
151
  session does not itself require a broad verification run. Keep full output in
152
152
  local artifacts, preserve exit status, and read summaries and relevant failure
153
153
  excerpts before expanding logs. Do not weaken assertions or omit required
154
- coverage to save time or tokens. An unconfigured result
155
- means the declared workspace or checks do not exist yet; it is not a failing
156
- check. Before reporting completion, compare the requested observable behavior,
157
- required inputs and resources, declared project operations, and focused evidence
158
- with what actually exists. Report files changed, checks actually run,
159
- anything not proven, and anything still requiring attention. Never claim that
160
- an unrun check passed or that passing checks prove the whole product.
154
+ coverage to save time or tokens.
155
+
156
+ A change to user-visible behavior ships with the browser specs that cover it
157
+ updated and run in the same change, before pushing. When an existing spec
158
+ fails, first classify it as outdated (the behavior changed on purpose) or as a
159
+ regression. Fix regressions in the product, and change a spec only for an
160
+ intended change, naming the change that made it outdated. Test resources a
161
+ managed host supplies, such as a login state, base URL, browser, or ports, are
162
+ the host's contract. If they do not work (for example, tests run signed out
163
+ despite a supplied login), report the host defect with evidence and stop; never
164
+ compensate in the project by rewriting cookies or adding its own login or
165
+ browser.
166
+
167
+ An unconfigured result means the declared workspace or checks do not exist
168
+ yet; it is not a failing check. Before reporting completion, compare the
169
+ requested observable behavior, required inputs and resources, declared project
170
+ operations, and focused evidence with what actually exists. Report files
171
+ changed, checks actually run, anything not proven, and anything still requiring
172
+ attention. Never claim that an unrun check passed or that passing checks prove
173
+ the whole product.
161
174
 
162
175
  ## Subsystem maintenance
163
176
 
package/src/cli.js CHANGED
@@ -55,7 +55,7 @@ const USAGE = `Usage:
55
55
  genesis adopt [product guidance...]
56
56
  genesis codex install
57
57
  genesis collaboration show
58
- genesis collaboration set <tone> <response-length> <experience> <explanation-style> [project requirements...]
58
+ genesis collaboration set <tone> <response-length> <experience> <explanation-style> <long-checks> [project requirements...]
59
59
  genesis engineering list
60
60
  genesis engineering show [profile]
61
61
  genesis engineering set <profile>
@@ -178,10 +178,10 @@ function parseCommand(argv) {
178
178
  if (action === 'show' && operands.length !== 1) {
179
179
  fail('CLI_EXTRA_ARGUMENT', 'Command collaboration show accepts no extra arguments.');
180
180
  }
181
- if (action === 'set' && operands.length < 5) {
181
+ if (action === 'set' && operands.length < 6) {
182
182
  fail(
183
183
  'CLI_COLLABORATION_VALUES_REQUIRED',
184
- 'Command collaboration set requires tone, response length, experience, and explanation style.',
184
+ 'Command collaboration set requires tone, response length, experience, explanation style, and long checks.',
185
185
  );
186
186
  }
187
187
  } else if (command === 'stack') {
@@ -279,6 +279,7 @@ function writeCollaboration(result) {
279
279
  line(process.stdout, `Response length: ${result.responseLength}`);
280
280
  line(process.stdout, `Assumed experience: ${result.experience}`);
281
281
  line(process.stdout, `Explanation style: ${result.explanationStyle}`);
282
+ line(process.stdout, `Long checks: ${result.longChecks || 'unset (conservative until chosen)'}`);
282
283
  if (result.requirements) {
283
284
  line(process.stdout, 'Project requirements:');
284
285
  line(process.stdout, result.requirements);
@@ -516,7 +517,8 @@ async function execute({ command, operands, options }, { signal, hostContext = n
516
517
  responseLength: operands[2],
517
518
  experience: operands[3],
518
519
  explanationStyle: operands[4],
519
- ...(operands.length > 5 ? { requirements: operands.slice(5).join(' ') } : {}),
520
+ longChecks: operands[5],
521
+ ...(operands.length > 6 ? { requirements: operands.slice(6).join(' ') } : {}),
520
522
  }),
521
523
  };
522
524
  }
@@ -74,6 +74,18 @@ const DIMENSIONS = {
74
74
  guidance: 'Explain the practical reasoning behind decisions in a teaching-oriented way.',
75
75
  },
76
76
  },
77
+ longChecks: {
78
+ conservative: {
79
+ name: 'Conservative',
80
+ description: 'Saves the most tokens; results arrive only when each check ends.',
81
+ guidance: 'Run each long check as one unattended command, bounded by a time limit, that writes its complete output to a local file and exits with its status, so it ends with a single completion signal. Do not watch it while it runs: no progress monitors, log tails, repeated status checks or interim reports. Pass this on to any agent you delegate checks to.',
82
+ },
83
+ normal: {
84
+ name: 'Normal',
85
+ description: 'Reacts sooner, uses more tokens.',
86
+ guidance: 'You may follow a long check\'s progress while it runs and stop a clearly broken run early. Still keep its full output in files and read only what you need.',
87
+ },
88
+ },
77
89
  };
78
90
 
79
91
  const SECTION_FIELDS = Object.freeze({
@@ -81,8 +93,13 @@ const SECTION_FIELDS = Object.freeze({
81
93
  'Response length': 'responseLength',
82
94
  'Assumed experience': 'experience',
83
95
  'Explanation style': 'explanationStyle',
96
+ 'Long checks': 'longChecks',
84
97
  });
85
98
 
99
+ // A missing optional section is an unmade choice, not a default.
100
+ const OPTIONAL_SECTIONS = new Set(['Long checks']);
101
+ const LONG_CHECKS_UNTIL_CHOSEN = 'conservative';
102
+
86
103
  export const DEFAULT_COLLABORATION = Object.freeze({
87
104
  tone: 'encouraging',
88
105
  responseLength: 'concise',
@@ -196,6 +213,15 @@ function collaborationCatalog() {
196
213
  ]));
197
214
  }
198
215
 
216
+ function unsetLongChecksGuidance(value) {
217
+ const choices = DIMENSIONS.longChecks;
218
+ return [
219
+ 'The project has not chosen how agents handle long checks such as test suites and builds. Ask the person once, early, before running the first long check, which option they want:',
220
+ ...Object.entries(choices).map(([id, choice]) => `- \`${id}\`: ${choice.description}`),
221
+ `Record the answer with \`genesis collaboration set ${value.tone} ${value.responseLength} ${value.experience} ${value.explanationStyle} <${Object.keys(choices).join('|')}>\`. Until the person answers, use \`${LONG_CHECKS_UNTIL_CHOSEN}\`: ${choices[LONG_CHECKS_UNTIL_CHOSEN].guidance}`,
222
+ ].join('\n');
223
+ }
224
+
199
225
  function collaborationGuidance(value) {
200
226
  return [
201
227
  'Apply the configured tone, response length, assumed experience, and explanation style to every user-facing message, including progress updates and final responses.',
@@ -217,6 +243,10 @@ function collaborationGuidance(value) {
217
243
  '## Explanation style',
218
244
  '',
219
245
  DIMENSIONS.explanationStyle[value.explanationStyle].guidance,
246
+ '',
247
+ '## Long checks',
248
+ '',
249
+ value.longChecks ? DIMENSIONS.longChecks[value.longChecks].guidance : unsetLongChecksGuidance(value),
220
250
  ...(value.requirements ? [
221
251
  '',
222
252
  '## Project requirements',
@@ -231,17 +261,17 @@ export function parseCollaborationSource(source) {
231
261
  const parsed = sections(source);
232
262
  const expected = new Set([...Object.keys(SECTION_FIELDS), 'Project requirements']);
233
263
  const unknown = [...parsed.sections.keys()].filter((name) => !expected.has(name));
234
- const missing = [...expected].filter((name) => !parsed.sections.has(name));
264
+ const missing = [...expected].filter((name) => !parsed.sections.has(name) && !OPTIONAL_SECTIONS.has(name));
235
265
  if (unknown.length > 0 || missing.length > 0) {
236
266
  throw new GenesisError(
237
267
  'COLLABORATION_INVALID',
238
- `${COLLABORATION_PATH} needs only Tone, Response length, Assumed experience, Explanation style, and Project requirements sections.`,
268
+ `${COLLABORATION_PATH} needs only Tone, Response length, Assumed experience, Explanation style, optional Long checks, and Project requirements sections.`,
239
269
  { path: COLLABORATION_PATH },
240
270
  );
241
271
  }
242
272
  const values = Object.fromEntries(Object.entries(SECTION_FIELDS).map(([sectionName, field]) => [
243
273
  field,
244
- dimensionValue(parsed.sections, sectionName, field),
274
+ parsed.sections.has(sectionName) ? dimensionValue(parsed.sections, sectionName, field) : null,
245
275
  ]));
246
276
  const requirementsSource = sectionText(parsed.sections, 'Project requirements');
247
277
  if (!requirementsSource) {
@@ -296,6 +326,7 @@ function renderCollaboration(value) {
296
326
  '',
297
327
  `- \`${value.explanationStyle}\``,
298
328
  '',
329
+ ...(value.longChecks === null ? [] : ['## Long checks', '', `- \`${value.longChecks}\``, '']),
299
330
  '## Project requirements',
300
331
  '',
301
332
  value.requirements || '- Nothing.',
@@ -340,6 +371,7 @@ export async function inspectProjectCollaboration({ projectRoot } = {}) {
340
371
  export async function setProjectCollaboration({
341
372
  experience,
342
373
  explanationStyle,
374
+ longChecks,
343
375
  projectRoot,
344
376
  requirements,
345
377
  responseLength,
@@ -352,6 +384,7 @@ export async function setProjectCollaboration({
352
384
  responseLength: responseLength ?? current.responseLength,
353
385
  experience: experience ?? current.experience,
354
386
  explanationStyle: explanationStyle ?? current.explanationStyle,
387
+ longChecks: longChecks ?? current.longChecks,
355
388
  requirements: requirements ?? current.requirements,
356
389
  }));
357
390
  const rendered = renderCollaboration(next);
@@ -367,6 +400,7 @@ export async function setProjectCollaboration({
367
400
  responseLength: next.responseLength,
368
401
  experience: next.experience,
369
402
  explanationStyle: next.explanationStyle,
403
+ longChecks: next.longChecks,
370
404
  requirements: next.requirements,
371
405
  choices: collaborationCatalog(),
372
406
  guidance: collaborationGuidance(next),
@@ -167,6 +167,7 @@ function collaborationPromptContext(collaboration) {
167
167
  responseLength: collaboration.responseLength,
168
168
  experience: collaboration.experience,
169
169
  explanationStyle: collaboration.explanationStyle,
170
+ longChecks: collaboration.longChecks || 'unset',
170
171
  requirements: collaboration.requirements || 'Nothing.',
171
172
  };
172
173
  }
@@ -7,8 +7,8 @@ const FALLBACK_LINE = /^- Fallback `([^`]+)` as `([^`]+)`[ \t]*$/u;
7
7
  const REGEXP_SPECIAL = /[.+^${}()|[\]\\]/u;
8
8
  const compiledGlobPatterns = new Map();
9
9
 
10
- function invalid(piecePath, message) {
11
- throw new GenesisError('STACK_PIECE_INVALID', message, { path: piecePath });
10
+ function invalid(piecePath, message, code = 'STACK_PIECE_INVALID') {
11
+ throw new GenesisError(code, message, { path: piecePath });
12
12
  }
13
13
 
14
14
  function normalizedPrefix(value, piecePath) {
@@ -25,7 +25,7 @@ function normalizedPrefix(value, piecePath) {
25
25
  return prefix;
26
26
  }
27
27
 
28
- function normalizedGlob(value, piecePath) {
28
+ function normalizedGlob(value, piecePath, { code, section = 'City regions' } = {}) {
29
29
  const pattern = String(value || '').trim();
30
30
  if (
31
31
  !pattern
@@ -37,7 +37,8 @@ function normalizedGlob(value, piecePath) {
37
37
  ) {
38
38
  invalid(
39
39
  piecePath,
40
- '## City regions ignore patterns must be canonical project-relative globs using only * and ** wildcards.',
40
+ `## ${section} ignore patterns must be canonical project-relative globs using only * and ** wildcards.`,
41
+ code,
41
42
  );
42
43
  }
43
44
  return pattern;
@@ -156,6 +157,27 @@ export function parseStackCityPresentationLines(lines, { path = 'stack piece' }
156
157
  return { exclusions, regions };
157
158
  }
158
159
 
160
+ /**
161
+ * A project's own `## City exclusions` section removes paths no selected
162
+ * component can know about, such as example fixtures. It uses the City regions
163
+ * Ignore grammar and adds to the component exclusions.
164
+ */
165
+ export function parseProjectCityExclusionLines(lines, { path }) {
166
+ if (lines === undefined) return [];
167
+ const content = lines.filter((line) => line.trim());
168
+ if (content.length === 1 && content[0].trim() === '- Nothing.') return [];
169
+ const options = { code: 'STACK_INVALID', section: 'City exclusions' };
170
+ const exclusions = content.map((line) => {
171
+ const ignore = line.match(IGNORE_LINE);
172
+ if (!ignore) invalid(path, '## City exclusions entries must each be one Ignore with a glob.', options.code);
173
+ return normalizedGlob(ignore[1], path, options);
174
+ });
175
+ if (exclusions.length === 0 || new Set(exclusions).size !== exclusions.length) {
176
+ invalid(path, '## City exclusions needs distinct Ignore entries or `- Nothing.`.', options.code);
177
+ }
178
+ return exclusions;
179
+ }
180
+
159
181
  export function composeStackCityPresentation(components = []) {
160
182
  const owners = components.filter((component) => (
161
183
  component.cityRegions.length > 0 || component.cityExclusions.length > 0
@@ -7,7 +7,10 @@ import {
7
7
  normalizeStackPackageName,
8
8
  readStackCatalog,
9
9
  } from './stack-catalog.js';
10
- import { composeStackCityPresentation } from './stack-city-presentation.js';
10
+ import {
11
+ composeStackCityPresentation,
12
+ parseProjectCityExclusionLines,
13
+ } from './stack-city-presentation.js';
11
14
  import { parseUiDiscovery } from './stack-ui-discovery.js';
12
15
  import { parsePackageDiscovery } from './stack-package-discovery.js';
13
16
  import { resolveStackPieces } from './stack-composition.js';
@@ -371,6 +374,13 @@ async function readStackWithContractMode(projectRoot, {
371
374
  source: 'project',
372
375
  diagnostics: [],
373
376
  }));
377
+ const cityExclusions = [...new Set([
378
+ ...cityPresentation.exclusions,
379
+ ...parseProjectCityExclusionLines(
380
+ extensions.find(entry => entry.name === 'City exclusions')?.lines,
381
+ { path: STACK_PATH },
382
+ ),
383
+ ])];
374
384
  const uiDiscovery = parseUiDiscovery(extensions.find(entry => entry.name === 'UI discovery')?.lines);
375
385
  const packageDiscovery = parsePackageDiscovery(extensions.find(entry => entry.name === 'Package discovery')?.lines);
376
386
  return {
@@ -378,7 +388,7 @@ async function readStackWithContractMode(projectRoot, {
378
388
  identityHash: sha256(stableJson({
379
389
  stackPackages: declaredPackages,
380
390
  components: components.map(({ id }) => id),
381
- cityExclusions: cityPresentation.exclusions,
391
+ cityExclusions,
382
392
  cityRegions: cityPresentation.regions,
383
393
  verificationCommands: verificationCommands.map(({ label, argv }) => ({ label, argv })),
384
394
  environmentDefaults,
@@ -388,7 +398,7 @@ async function readStackWithContractMode(projectRoot, {
388
398
  })),
389
399
  components,
390
400
  stackPackages: declaredPackages,
391
- cityExclusions: cityPresentation.exclusions,
401
+ cityExclusions,
392
402
  cityRegions: cityPresentation.regions,
393
403
  uiDiscovery,
394
404
  packageDiscovery,