genesis-compiler 1.10.21 → 1.10.22

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.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: genesis-review
3
+ description: Review the work against what the software should do through an explicitly requested review over the whole app, the changes since its last completed run, a named section or the current work, judging the tests (each changed behavior has one that would fail without it; none weakened or skipped), the gaps between intent, code and explanation, and costs that grow with the data, writing only tests and reporting everything else. Use when asked for a review, a review pass, or to check the tests and the work against the Blueprint.
4
+ ---
5
+
6
+ # Genesis review
7
+
8
+ Judge the work against what the software should do, and the tests against the
9
+ work, and say plainly what was and was not verified. A review is explicit:
10
+ never run it merely because implementation finished.
11
+
12
+ Resolve the invocation before running the first Genesis operation. Do not use a
13
+ Genesis operation or `genesis --version` as an availability probe.
14
+
15
+ 1. Read the repository-root `package.json`. Treat Genesis as project-pinned only
16
+ when that manifest's `name` is `genesis-compiler`, or its `dependencies`,
17
+ `devDependencies`, or `optionalDependencies` contain the exact
18
+ `genesis-compiler` key.
19
+ 2. For a project-pinned repository, invoke every Genesis CLI operation with
20
+ `npm exec --no -- genesis <arguments>`. This runs without fetching another
21
+ package. If the declared package is not installed, report that the project's
22
+ dependencies are not prepared; do not fall back to another Genesis version.
23
+ 3. Otherwise, do not try `npm exec`. Run `command -v genesis` (or the current
24
+ shell's equivalent `PATH` lookup). When it resolves an executable, invoke
25
+ `genesis <arguments>` directly.
26
+ 4. When neither source is available, stop and report that Genesis is
27
+ unavailable. Never install or update Genesis merely to satisfy a workflow
28
+ instruction.
29
+
30
+ Successful invocation resolution is routine. Do not narrate whether Genesis is
31
+ project-pinned or on `PATH`, and never describe direct `PATH` use as a fallback.
32
+ Mention how the invocation was selected only when resolution fails or the user
33
+ explicitly asks.
34
+
35
+ Follow the configured Collaboration approach for user-facing updates, including
36
+ its technical depth and response length. Report concrete progress,
37
+ findings, or blockers; do not announce that you are following guidance.
38
+
39
+
40
+ ## Load the effective project instructions once
41
+
42
+ A review request may arrive as a complete Genesis-generated prompt or as a
43
+ direct request such as `Review` or `Review the Orders section`.
44
+
45
+ If the current task already contains `GENESIS CONTEXT` whose task is `review`,
46
+ continue with it directly. Do not generate another prompt.
47
+
48
+ Otherwise, before resolving scope, run the Genesis `prompt --task review`
49
+ operation once using the invocation rule above. For a bare `Review` request,
50
+ pass no request argument. When the user names a section, commits or the
51
+ current work, pass that exact request as one safely quoted argument. Treat the
52
+ printed prompt as the instructions for this same turn; do not dispatch it to
53
+ another agent and do not generate it again.
54
+
55
+ When the user asks for a deep run of this pass, pass its Deep request,
56
+ `Review the whole app deeply.`, exactly;
57
+ Genesis then adds the run's `DEEP RUN` section.
58
+
59
+ ## Resolve the scope
60
+
61
+ Inspect `git status --short` and the staged and unstaged diffs before editing.
62
+ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
63
+ not stage, stash, commit, or discard changes as a prerequisite.
64
+
65
+ When the prompt has a `PASS RUN` section, the request is one of the pass's own
66
+ runs (the whole app, the changes since its last completed run judged against
67
+ the whole app, or a deep run) or a run over the changes since a base commit the
68
+ request names, and its scope is the one that section states, whatever the
69
+ defaults below say. A deep run's prompt also has a `DEEP RUN`
70
+ section; follow it with "Deep run" below.
71
+
72
+ - **The work (default).** The relationship between the Blueprint, the code, the
73
+ Program and the tests as they stand now.
74
+ - **The whole app.** Every behavior the project's own source exposes and the
75
+ tests that prove it.
76
+ - **A section or the changes.** A named area, explicit commits, or the current
77
+ task's changes: the behavior they create or change and its tests.
78
+
79
+ If the scope is ambiguous, ask which section or changes.
80
+
81
+ ## Boundaries
82
+
83
+ - The prompt says what a review judges. It edits only tests: it adds or updates
84
+ the tests changed behavior lacks and changes no product code, Blueprint,
85
+ Program, Stack or other file. Every other finding is reported with the
86
+ smallest sensible correction, left unchanged.
87
+ - Run the tests relevant to the scope; report what actually ran and what was not
88
+ verified, and never report an unrun check as passing.
89
+
90
+ Leave the review as ordinary uncommitted work for a later normal commit. Never
91
+ amend or rewrite selected commits.
92
+
93
+ When the prompt ends with a `PASS RECORD` step, take it once the review has
94
+ finished: the Genesis `passes done review` operation records it in `genesis/passes-added.json`,
95
+ an uncommitted change that lands in the review's own commit. Genesis adds that
96
+ step only to the pass's own runs, whose requests it writes itself, so a review
97
+ of a section, commits, a base or the current work is never recorded as the
98
+ pass; neither is a review that stopped early, was abandoned or still has
99
+ failing checks.
100
+
101
+ ## Deep run
102
+
103
+ When the prompt has a `DEEP RUN` section, follow it: it says how the checklist,
104
+ evidence, chunks, commits and report work for every pass. This pass adds to the
105
+ checklist:
106
+
107
+ - every behavior the source exposes (operation, screen, command or job), each
108
+ with the test that would fail without it, or the missing test written or
109
+ reported;
110
+ - every test file: whether it runs in the whole suite, what it proves, what it
111
+ costs, and any test weakened, skipped or loosened;
112
+ - every list, batch and write path, with its query or call counts at a few rows
113
+ and at a realistic number;
114
+ - the Blueprint, Program and `genesis/subsystems.md` entries against the code;
115
+ - the cross-subsystem item: the same behavior tested in different ways or not
116
+ at all, a table with more than one owner, and work over a whole population
117
+ done inside a request.
118
+
119
+ Review's limits hold in a deep run: it writes only tests, and every other
120
+ finding is reported and left unchanged.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Genesis review"
3
+ short_description: "Review the work and its tests against what the app should do"
4
+ default_prompt: "Use $genesis-review to Review the whole app."
@@ -54,6 +54,10 @@ to another agent and do not generate it again. This step composes the selected
54
54
  Stack's security pass guidance and any project customization with the portable
55
55
  contract below.
56
56
 
57
+ When the user asks for a deep run of this pass, pass its Deep request,
58
+ `Security pass the whole app deeply.`, exactly;
59
+ Genesis then adds the run's `DEEP RUN` section.
60
+
57
61
  ## Resolve the scope
58
62
 
59
63
  Inspect `git status --short` and the staged and unstaged diffs before editing.
@@ -61,9 +65,11 @@ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
61
65
  not stage, stash, commit, or discard changes as a prerequisite.
62
66
 
63
67
  When the prompt has a `PASS RUN` section, the request is one of the pass's own
64
- runs, the whole app or the changes since its last completed run judged against
65
- the whole app, and its scope is the one that section states, whatever the
66
- defaults below say.
68
+ runs (the whole app, the changes since its last completed run judged against
69
+ the whole app, or a deep run) or a run over the changes since a base commit the
70
+ request names, and its scope is the one that section states, whatever the
71
+ defaults below say. A deep run's prompt also has a `DEEP RUN`
72
+ section; follow it with "Deep run" below.
67
73
 
68
74
  - **The whole app (default).** Every way in: each route, page, command,
69
75
  action, message or socket handler, webhook, scheduled or background job,
@@ -277,3 +283,15 @@ step only to the pass's own runs, whose requests it writes itself, so a security
277
283
  of a section, commits or your own changes is never recorded as the pass;
278
284
  neither is a pass that stopped early, was abandoned or still has failing
279
285
  checks.
286
+
287
+ ## Deep run
288
+
289
+ When the prompt has a `DEEP RUN` section, follow it: it says how the checklist,
290
+ evidence, chunks, commits and report work for every pass. This pass adds to the
291
+ checklist:
292
+
293
+ - every way in from the map (each route, page, command, action, handler,
294
+ webhook, job, file read from outside and outbound call), every sign-in,
295
+ session and token flow, and every secret;
296
+ - the cross-subsystem item: one resource guarded differently on different
297
+ ways in, and a protection applied on some ways in but not on others.
@@ -53,6 +53,10 @@ to another agent and do not generate it again. This step composes the selected
53
53
  Stack's speed pass guidance and any project customization with the portable
54
54
  contract below.
55
55
 
56
+ When the user asks for a deep run of this pass, pass its Deep request,
57
+ `Speed pass the whole app deeply.`, exactly;
58
+ Genesis then adds the run's `DEEP RUN` section.
59
+
56
60
  ## Resolve the scope
57
61
 
58
62
  Inspect `git status --short` and the staged and unstaged diffs before editing.
@@ -60,9 +64,11 @@ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
60
64
  not stage, stash, commit, or discard changes as a prerequisite.
61
65
 
62
66
  When the prompt has a `PASS RUN` section, the request is one of the pass's own
63
- runs, the whole app or the changes since its last completed run judged against
64
- the whole app, and its scope is the one that section states, whatever the
65
- defaults below say.
67
+ runs (the whole app, the changes since its last completed run judged against
68
+ the whole app, or a deep run) or a run over the changes since a base commit the
69
+ request names, and its scope is the one that section states, whatever the
70
+ defaults below say. A deep run's prompt also has a `DEEP RUN`
71
+ section; follow it with "Deep run" below.
66
72
 
67
73
  - **The whole app (default).** The test suite and its preparation, every
68
74
  screen, view or command a person opens and the main actions in it, and
@@ -331,3 +337,15 @@ step only to the pass's own runs, whose requests it writes itself, so a speed
331
337
  pass of a section, commits or your own changes is never recorded as the pass;
332
338
  neither is a pass that stopped early, was abandoned or still has failing
333
339
  checks.
340
+
341
+ ## Deep run
342
+
343
+ When the prompt has a `DEEP RUN` section, follow it: it says how the checklist,
344
+ evidence, chunks, commits and report work for every pass. This pass adds to the
345
+ checklist:
346
+
347
+ - every slow test file and phase of the suite, every screen's first load and
348
+ each navigation, every main request, command and job, startup, and work that
349
+ runs while nobody asks for anything, each measured before and after;
350
+ - the cross-subsystem item: the same work done in several places (repeated
351
+ setup, duplicate fetches, the same computation on several paths).
@@ -54,6 +54,10 @@ prompt as the instructions for this same turn; do not dispatch it to another
54
54
  agent and do not generate it again. This step composes the selected Stack's UI
55
55
  pass guidance and any project customization with the portable contract below.
56
56
 
57
+ When the user asks for a deep run of this pass, pass its Deep request,
58
+ `UI pass the whole app deeply.`, exactly;
59
+ Genesis then adds the run's `DEEP RUN` section.
60
+
57
61
  ## Resolve the scope
58
62
 
59
63
  Inspect `git status --short` and the staged and unstaged diffs before editing.
@@ -61,9 +65,11 @@ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
61
65
  not stage, stash, commit, or discard changes as a prerequisite.
62
66
 
63
67
  When the prompt has a `PASS RUN` section, the request is one of the pass's own
64
- runs, the whole app or the changes since its last completed run judged against
65
- the whole app, and its scope is the one that section states, whatever the
66
- defaults below say.
68
+ runs (the whole app, the changes since its last completed run judged against
69
+ the whole app, or a deep run) or a run over the changes since a base commit the
70
+ request names, and its scope is the one that section states, whatever the
71
+ defaults below say. A deep run's prompt also has a `DEEP RUN`
72
+ section; follow it with "Deep run" below.
67
73
 
68
74
  - **The changes (default).** With no stated scope, select only your own changes
69
75
  for the current task, including preceding turns: staged, unstaged, untracked
@@ -127,3 +133,15 @@ step only to the pass's own runs, whose requests it writes itself, so a UI pass
127
133
  of your own changes, a section or commits is never recorded as the pass;
128
134
  neither is a pass that stopped early, was abandoned or still has failing
129
135
  checks.
136
+
137
+ ## Deep run
138
+
139
+ When the prompt has a `DEEP RUN` section, follow it: it says how the checklist,
140
+ evidence, chunks, commits and report work for every pass. This pass adds to the
141
+ checklist:
142
+
143
+ - every screen (page, view, dialog or panel) and every shared interface
144
+ component, each checked against the Stack's design rules, with a screenshot
145
+ after any change;
146
+ - the cross-subsystem item: the same control, layout or pattern built or
147
+ styled differently on different screens.
@@ -54,6 +54,10 @@ turn; do not dispatch it to another agent and do not generate it again. This
54
54
  step composes the selected Stack's user experience pass guidance and any
55
55
  project customization with the portable contract below.
56
56
 
57
+ When the user asks for a deep run of this pass, pass its Deep request,
58
+ `User experience pass the whole app deeply.`, exactly;
59
+ Genesis then adds the run's `DEEP RUN` section.
60
+
57
61
  ## Resolve the scope
58
62
 
59
63
  Inspect `git status --short` and the staged and unstaged diffs before editing.
@@ -61,9 +65,11 @@ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
61
65
  not stage, stash, commit, or discard changes as a prerequisite.
62
66
 
63
67
  When the prompt has a `PASS RUN` section, the request is one of the pass's own
64
- runs, the whole app or the changes since its last completed run judged against
65
- the whole app, and its scope is the one that section states, whatever the
66
- defaults below say.
68
+ runs (the whole app, the changes since its last completed run judged against
69
+ the whole app, or a deep run) or a run over the changes since a base commit the
70
+ request names, and its scope is the one that section states, whatever the
71
+ defaults below say. A deep run's prompt also has a `DEEP RUN`
72
+ section; follow it with "Deep run" below.
67
73
 
68
74
  - **The whole app (default).** Every main task a person comes to do, from
69
75
  where they start to where it is done.
@@ -257,3 +263,14 @@ step only to the pass's own runs, whose requests it writes itself, so a user exp
257
263
  of a section, commits or your own changes is never recorded as the pass;
258
264
  neither is a pass that stopped early, was abandoned or still has failing
259
265
  checks.
266
+
267
+ ## Deep run
268
+
269
+ When the prompt has a `DEEP RUN` section, follow it: it says how the checklist,
270
+ evidence, chunks, commits and report work for every pass. This pass adds to the
271
+ checklist:
272
+
273
+ - every main task, walked as a new and as a returning person, with its steps
274
+ and decisions counted before and after;
275
+ - the cross-subsystem item: similar tasks or screens that work differently,
276
+ and one thing called by different names.
@@ -72,7 +72,9 @@ async function execute({ command, operands, options }, { signal, hostContext = n
72
72
  }
73
73
  if (command === 'migrate') return migrate({ projectRoot, stackPackages });
74
74
  if (command === 'skills') return syncSkills({ projectRoot, stackPackages });
75
- if (command === 'passes') return { action: 'done', ...await recordPassDone({ projectRoot, pass: operands[1] }) };
75
+ if (command === 'passes') {
76
+ return { action: 'done', ...await recordPassDone({ projectRoot, pass: operands[1], passes: operands.slice(2) }) };
77
+ }
76
78
  if (command === 'adopt') {
77
79
  return adoptProject({ projectRoot, request: operands.join(' '), stackPackages });
78
80
  }
package/src/cli.js CHANGED
@@ -27,6 +27,7 @@ const USAGE = `Usage:
27
27
  genesis stack add <piece...>
28
28
  genesis skills sync
29
29
  genesis passes done <pass>
30
+ genesis passes done master-pass <pass...>
30
31
  genesis templates list
31
32
  genesis templates apply <catalogue:technology/variant>
32
33
  genesis context <path...>
@@ -42,7 +43,7 @@ const USAGE = `Usage:
42
43
  genesis inspect passes
43
44
  genesis inspect section <name>
44
45
  genesis prompt [request...]
45
- genesis prompt --task <start|adopt|work|deslop|ui-pass|identity-pass|quality-pass|data-pass|security-pass|ux-pass|speed-pass|program|blueprint|describe|review> [request...]
46
+ genesis prompt --task <start|adopt|work|deslop|ui-pass|identity-pass|quality-pass|data-pass|security-pass|ux-pass|speed-pass|master-pass|program|blueprint|describe|review> [request...]
46
47
  genesis verify
47
48
  genesis check
48
49
 
@@ -179,8 +180,10 @@ function parseCommand(argv) {
179
180
  fail('CLI_SKILLS_ACTION_REQUIRED', 'Command skills requires exactly: sync.');
180
181
  }
181
182
  } else if (command === 'passes') {
182
- if (operands.length !== 2 || operands[0] !== 'done') {
183
- fail('CLI_PASSES_ACTION_REQUIRED', 'Command passes requires exactly: done <pass>.');
183
+ // Only the Master pass records the passes it ran with it.
184
+ const master = operands[1] === 'master-pass';
185
+ if (operands[0] !== 'done' || (master ? operands.length < 3 : operands.length !== 2)) {
186
+ fail('CLI_PASSES_ACTION_REQUIRED', 'Command passes requires exactly: done <pass>, or done master-pass <pass...>.');
184
187
  }
185
188
  } else if (command === 'codex') {
186
189
  if (operands.length !== 1 || operands[0] !== 'install') {
@@ -317,10 +320,15 @@ function writeResult(command, result) {
317
320
  line(process.stdout, ` recommended ${pass.recommend.hint}; Stack guidance: ${pass.stackGuidance ? 'yes' : 'no'}; last run: ${pass.lastRun ?? 'never'}`);
318
321
  if (pass.unavailable && !result.unavailable) line(process.stdout, ` unavailable: ${pass.unavailable}`);
319
322
  }
323
+ const { master } = result;
324
+ line(process.stdout, `${master.id}: ${master.title} — ${master.description}`);
325
+ if (master.unavailable) line(process.stdout, ` unavailable: ${master.unavailable}`);
326
+ else line(process.stdout, ` runs, in order: ${master.passes.join(', ')}; last run: ${master.lastRun ?? 'never'}`);
320
327
  return;
321
328
  }
322
329
  if (command === 'passes') {
323
- line(process.stdout, `${result.pass}: recorded ${result.lastRun} in ${result.path}`);
330
+ const ran = result.passes ? ` with ${result.passes.join(', ')}` : '';
331
+ line(process.stdout, `${result.pass}: recorded ${result.lastRun}${ran} in ${result.path}`);
324
332
  return;
325
333
  }
326
334
  if (command === 'inspect' && result.inspection === 'skills') {
@@ -17,7 +17,7 @@ import { parse as parseYaml } from 'yaml';
17
17
 
18
18
  import { asDiagnostic, GenesisError } from './errors.js';
19
19
  import { claudeSkillLinks } from './claude-hooks.js';
20
- import { PASSES } from './passes.js';
20
+ import { MASTER_PASS, PASSES } from './passes.js';
21
21
  import { sha256, stableJson, writeFileAtomic } from './utils.js';
22
22
 
23
23
  const packageRoot = fileURLToPath(new URL('../../', import.meta.url));
@@ -35,12 +35,13 @@ const SKILL_FIELDS = new Set([
35
35
  'allowed-tools',
36
36
  ]);
37
37
 
38
- // This release's Genesis workflow skills: the project and Program skills, and one for each pass. Its skills
39
- // directory holds exactly these, as every release's does (see projectGenesis).
38
+ // This release's Genesis workflow skills: the project and Program skills, one for each pass and one for the Master
39
+ // pass. Its skills directory holds exactly these, as every release's does (see projectGenesis).
40
40
  export const CORE_SKILL_NAMES = Object.freeze([
41
41
  'genesis-project',
42
42
  'genesis-program',
43
43
  ...PASSES.map(({ task }) => `genesis-${task}`),
44
+ `genesis-${MASTER_PASS.task}`,
44
45
  ]);
45
46
 
46
47
  function invalidSkill(message, details = {}) {
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
 
3
- import { PASSES } from './passes.js';
3
+ import { MASTER_PASS, PASSES } from './passes.js';
4
4
 
5
5
  const root = new URL('../../', import.meta.url);
6
6
  const assets = {
@@ -12,6 +12,9 @@ const assets = {
12
12
  work: new URL('prompts/work.txt', root),
13
13
  describe: new URL('prompts/describe.txt', root),
14
14
  ...Object.fromEntries(PASSES.map(({ task }) => [task, new URL(`prompts/${task}.txt`, root)])),
15
+ [MASTER_PASS.task]: new URL('prompts/master-pass.txt', root),
16
+ 'pass-report': new URL('prompts/pass-report.txt', root),
17
+ 'pass-deep': new URL('prompts/pass-deep.txt', root),
15
18
  program: new URL('prompts/program.txt', root),
16
19
  blueprint: new URL('prompts/blueprint.txt', root),
17
20
  review: new URL('prompts/review.txt', root),
@@ -19,6 +19,7 @@ import { attachEntryFunctions, discoverEntryPoints } from './entry-points.js';
19
19
  import { runGit } from './process.js';
20
20
  import { CITY_SCHEMA_VERSION, MACHINE_CITY_SCHEMA, PROGRAM_CITY_SCHEMA } from './city-documents.js';
21
21
  import { gitVisibleFileStates } from './project-files.js';
22
+ import { stackPackageRoots } from './source-size.js';
22
23
  import { readStack } from './stack.js';
23
24
  import { sha256, stableJson, stableJsonParts } from './utils.js';
24
25
 
@@ -388,7 +389,7 @@ export async function buildProjectIndex({
388
389
  if (observationReader) observations = null;
389
390
  const root = (await gitContext(projectRoot)).repositoryRoot;
390
391
  const stack = await readStack(root, { allowUnresolvedSections, stackPackages });
391
- const packageRoots = stack.packageDiscovery.discoveries.flatMap(discovery => discovery.manifests.map(manifest => path.posix.join(path.posix.dirname(manifest), discovery.packageRoot)));
392
+ const packageRoots = stackPackageRoots(stack);
392
393
  const includePath = filePath => isProjectContentPath(filePath) && !packageRoots.some(directory => filePath === directory || filePath.startsWith(`${directory}/`));
393
394
  const states = await gitVisibleFileStates(root, { includePath });
394
395
  const files = [...states]
@@ -4,6 +4,7 @@ export const GENESIS_CONTRACTS = Object.freeze({
4
4
  promptTasks: 'genesis.prompt-tasks.v1',
5
5
  passes: 'genesis.passes.v1',
6
6
  passRuns: 'genesis.pass-runs.v1',
7
+ deepRuns: 'genesis.deep-runs.v1',
7
8
  templates: 'genesis.templates.v1',
8
9
  templateApplication: 'genesis.template-application.v1',
9
10
  collaboration: 'genesis.collaboration.v1',
@@ -0,0 +1,76 @@
1
+ import { GenesisError } from './errors.js';
2
+ import { ADDED_PASS_RECORD_PATH } from './pass-record.js';
3
+ import { deepRequest, MASTER_PASS } from './passes.js';
4
+
5
+ // The names a request may give a pass: its id or its title, either without "pass", in any case.
6
+ function passNames(pass) {
7
+ return [pass.task, pass.title, pass.task.replace(/-pass$/u, ''), pass.title.replace(/ pass$/iu, '')]
8
+ .map((name) => name.toLowerCase());
9
+ }
10
+
11
+ const NAMED = new RegExp(`^${MASTER_PASS.title} \\(([^()]*)\\)(.*)$`, 'su');
12
+
13
+ /**
14
+ * The run and the passes a Master pass request asks for. The request is one of the runs Genesis writes (`runs`),
15
+ * optionally naming the passes in brackets after "Master pass", such as `Master pass (security, data, deslop) the whole
16
+ * app.`; without names it runs every pass the project offers. The passes run in the Master order, whatever order they
17
+ * were named in. Any other request, or a name the project offers no pass for, is refused, naming what it accepts.
18
+ * @param {string} request the request's last paragraph, after any framing a host put before it
19
+ * @param {{ request: string }[]} runs the Master pass's runs
20
+ * @param {{ task: string, title: string }[]} offered the passes the project offers, in the Master order
21
+ */
22
+ export function masterSelection(request, runs, offered) {
23
+ const named = NAMED.exec(request);
24
+ const run = runs.find((entry) => entry.request === (named ? `${MASTER_PASS.title}${named[2]}` : request));
25
+ if (!run) {
26
+ throw new GenesisError('PASS_REQUEST_INVALID', `A ${MASTER_PASS.title} request is ${runs.map(({ request: text }) => `"${text}"`).join(' or ')}, `
27
+ + `naming the passes to run, if not all, in brackets after "${MASTER_PASS.title}", such as "${MASTER_PASS.title} (security, data, deslop) the whole app."`);
28
+ }
29
+ if (!named) return { run, passes: offered };
30
+ const chosen = new Set();
31
+ for (const name of named[1].split(/,|\s+and\s+/u).map((part) => part.trim()).filter(Boolean)) {
32
+ const pass = offered.find((entry) => passNames(entry).includes(name.toLowerCase()));
33
+ if (!pass) {
34
+ throw new GenesisError('PASS_UNKNOWN', `The ${MASTER_PASS.title} cannot run "${name}". Name any of: `
35
+ + `${offered.map(({ task, title }) => `${title} (${task})`).join(', ')}.`, { pass: name });
36
+ }
37
+ chosen.add(pass);
38
+ }
39
+ if (chosen.size === 0) {
40
+ throw new GenesisError('PASS_REQUEST_INVALID', `The ${MASTER_PASS.title} request names no pass in its brackets; leave them out to run every pass.`);
41
+ }
42
+ return { run, passes: offered.filter((pass) => chosen.has(pass)) };
43
+ }
44
+
45
+ /**
46
+ * The prompt's MASTER PASS section: the run's scope, the passes to run in order, each with the request of its own
47
+ * Deep run, and where the Master ledger lives. The Master pass adds only this sequence: each pass's Deep run says how
48
+ * that pass works, from that pass's own prompt, and is never copied here.
49
+ */
50
+ export function masterPassSection({ run, passes, ledger }) {
51
+ const scope = run.commit
52
+ ? `over the changes since commit ${run.commit}, the commit of its last completed run, judged against the whole app`
53
+ : 'over the whole app';
54
+ return [
55
+ `This request is a ${MASTER_PASS.title} ${scope}. It runs these passes deeply, one after another, in this`,
56
+ 'order, each as that pass\'s own Deep run: generate its prompt with the Genesis `prompt` operation and the exact',
57
+ `request below, as \`.agents/skills/genesis-${MASTER_PASS.task}/SKILL.md\` says, and follow it in full before the`,
58
+ 'next pass starts.',
59
+ '',
60
+ ...passes.map((pass, index) => `${index + 1}. ${pass.title}: \`prompt --task ${pass.task} "${deepRequest(pass, run.commit)}"\``),
61
+ '',
62
+ `Keep the Master ledger at \`${ledger}\`.`,
63
+ ].join('\n');
64
+ }
65
+
66
+ // A Master pass's prompt ends with recording it, with the passes it ran, as its last commit.
67
+ export function masterRecordStep() {
68
+ return [
69
+ 'Only once every listed pass has finished or been skipped, make your last step the',
70
+ `Genesis \`passes done ${MASTER_PASS.task} <pass id...>\` operation, naming the passes it ran`,
71
+ `and leaving out those it skipped, invoked as \`.agents/skills/genesis-${MASTER_PASS.task}/SKILL.md\``,
72
+ `says. It records the time and those passes in \`${ADDED_PASS_RECORD_PATH}\`; commit that change`,
73
+ `as the ${MASTER_PASS.title}'s last commit. Do not run it for a ${MASTER_PASS.title} that stopped`,
74
+ 'early, was abandoned, or still has failing checks.',
75
+ ].join('\n');
76
+ }