genesis-compiler 1.10.8 → 1.10.10

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
@@ -27,7 +27,7 @@ genesis/
27
27
  collaboration.md project-wide communication choices and authored requirements
28
28
  engineering.md selected engineering profile and project-specific requirements
29
29
  stack.md selected components, resources, verification, and project-owned operation contracts
30
- stack/ optional per-component Description, Guidance, Adoption, Post-change, and Deslop customization
30
+ stack/ optional per-component Description, Guidance, Adoption, Post-change, Deslop, and UI pass customization
31
31
  subsystems.md authored responsibilities, Program membership, and data ownership/use
32
32
  program/ concise public-operation explanations
33
33
  .agents/skills/
@@ -36,7 +36,7 @@ genesis/
36
36
  .codex/hooks.json project-local Codex lifecycle integration
37
37
  .claude/settings.json project-local Claude Code lifecycle integration
38
38
  .opencode/plugins/ project-local OpenCode lifecycle integration
39
- .genesis/ local, git-ignored (`genesis init` adds the ignore lines)
39
+ .genesis/ local, git-ignored (`genesis init` adds the ignore lines; `genesis index` reports CITY_FILES_TRACKED while Git tracks a City)
40
40
  machine-city.json derived detailed source and function map
41
41
  program-city.json derived simple subsystem and operation map
42
42
  call-web.json derived function call web (`genesis index --call-web`)
@@ -187,10 +187,10 @@ type a command. It never runs adoption without approval and does nothing in an
187
187
  empty or already-adopted project.
188
188
 
189
189
  `genesis init` creates an empty Blueprint, a focused engineering approach, an
190
- empty optional Stack, the current `genesis/version`, three project Agent Skills,
190
+ empty optional Stack, the current `genesis/version`, four project Agent Skills,
191
191
  and project-local guidance adapters for Codex, Claude Code and OpenCode. The skills are ordinary
192
192
  [Agent Skills](https://agentskills.io): `genesis-project`, `genesis-program`,
193
- and `genesis-deslop`, each with standard `SKILL.md` and `agents/openai.yaml`
193
+ `genesis-deslop` and `genesis-ui-pass`, each with standard `SKILL.md` and `agents/openai.yaml`
194
194
  metadata. Open `/hooks` once in Codex to review and trust the guidance hook;
195
195
  Claude Code loads its project hooks and OpenCode discovers its local project plugin automatically. All three use the same
196
196
  Genesis-owned operating guide for new sessions and after compaction. When the
@@ -363,6 +363,17 @@ allowed. When a concrete requirement makes material complexity necessary, the
363
363
  agent must explain why a direct design is insufficient and ask before writing
364
364
  that complexity.
365
365
 
366
+ The same baseline keeps what the software builds fast as it grows. A request,
367
+ job or test costs in proportion to the rows it handles, never to stored history:
368
+ a list reads each table once, a write decides access once, and every visibility
369
+ check is bounded by the rows it reads. List, batch and write paths have a test
370
+ at realistic row counts that fails above a fixed query or time budget. Test
371
+ databases are prepared once per run from a stored schema and seed snapshot
372
+ checked by a digest, with the full migration history replayed only by one
373
+ separate fresh-schema check. A time limit catches a hang; a slow check is
374
+ measured and its cause fixed, never hidden by a longer limit, retries or
375
+ smaller data.
376
+
366
377
  The installed, versioned profile descriptions live in
367
378
  `profiles/engineering/*.md`. Straight Genesis users can inspect and select them
368
379
  without editing Markdown grammar:
@@ -575,6 +586,28 @@ up; it does not silently select the latest commit. Its result is a normal
575
586
  uncommitted diff for review and a later commit. It never amends selected commits,
576
587
  silently fixes newly discovered defects, or runs as an automatic completion phase.
577
588
 
589
+ ### UI pass your changes, a section, or the whole app
590
+
591
+ A UI pass makes UI exact to the selected Stack's design rules without changing
592
+ behavior. Ask directly, or generate the prompt for a host:
593
+
594
+ ```text
595
+ UI pass
596
+ UI pass the last commit
597
+ UI pass the Suppliers section
598
+ UI pass the whole app
599
+ ```
600
+
601
+ ```bash
602
+ genesis prompt --task ui-pass "UI pass the Suppliers section"
603
+ ```
604
+
605
+ The installed `genesis-ui-pass` skill resolves the scope as Deslop does, adds
606
+ the named-section and whole-app scopes, and follows the effective `UI pass`
607
+ prose of the selected Stack components, which names the technology's design
608
+ rules and method. Defects become findings; screenshots at 390, 768 and 1280
609
+ pixels in light and dark are the evidence. See [docs/ui-pass.md](docs/ui-pass.md).
610
+
578
611
  Projects can customize a selected technology through its matching file, such
579
612
  as `genesis/stack/jskit.md`:
580
613
 
@@ -821,6 +854,7 @@ add or replace complete project sections when its real scripts differ.
821
854
  | `Deployment` build | `npm run build` | Produce the deployable application build. | The deployment consumer. |
822
855
  | `Deployment` serve | `npm start` | Start the built production application. | The deployment consumer. |
823
856
  | `Deployment` readiness | `GET /api/health` → `200` | Prove that the deployed service is ready. | The deployment consumer. |
857
+ | `Deployment` generated secrets | `AUTH_LOCAL_SESSION_SECRET` | Let the deployment create the local-authentication session secret instead of committing or inventing it. | The deployment consumer. |
824
858
 
825
859
  The JSKIT proposal also declares `.env` as its environment projection and says
826
860
  that inactive deployment workspaces may recreate `node_modules`; it deliberately
@@ -834,7 +868,8 @@ database-preparation command. If the application implements a real migration or
834
868
  seed script, the agent records that exact command in the complete project
835
869
  sections whose consumers need it. Local authentication similarly requires the
836
870
  application to declare `AUTH_LOCAL_SESSION_SECRET` without committing its
837
- value.
871
+ value; the JSKIT Deployment proposal lists it as a generated secret for the
872
+ deployment consumer to create.
838
873
 
839
874
  ### How the Stack contract is enforced
840
875
 
@@ -290,6 +290,13 @@ once in the same turn so those effective Stack instructions are present. A
290
290
  prompt whose Genesis context already identifies the Deslop task is followed
291
291
  directly and never generated recursively.
292
292
 
293
+ A UI pass is an explicit prompt task with the same scope rules, plus a named
294
+ section or the whole app. Selected Stack `UI pass` prose supplies the design
295
+ rules and the method for each scope; the installed `genesis-ui-pass` skill
296
+ generates the canonical `ui-pass` prompt once, like Deslop's. A host offers it
297
+ only when `inspectPromptTasks()` reports Stack guidance for `ui-pass`; see
298
+ [UI pass](ui-pass.md).
299
+
293
300
  Once Codex identifies relevant source, `genesis context <path...>` returns only
294
301
  Program modules citing those paths plus the effective engineering approach,
295
302
  concise selected Stack guidance, exact project-owned Stack contracts, the
@@ -95,6 +95,11 @@ Concise technology context.
95
95
 
96
96
  - Technology-specific cleanup guidance.
97
97
 
98
+ ## UI pass
99
+
100
+ - The design rules and per-scope method for a UI pass, usually a pointer to
101
+ the technology's own guidance.
102
+
98
103
  ## Consumer operation
99
104
 
100
105
  ```json example.consumer-operation.v1
@@ -105,7 +110,8 @@ Concise technology context.
105
110
  ```
106
111
  ````
107
112
 
108
- `Description`, `Guidance`, `Adoption`, `Post-change`, and `Deslop` are prose.
113
+ `Description`, `Guidance`, `Adoption`, `Post-change`, `Deslop`, and `UI pass`
114
+ are prose.
109
115
  `Requires` and `Conflicts` define component selection. `Skill` names one
110
116
  complete authoritative Agent Skill directory. `Indexers` selects installed
111
117
  structural adapters.
@@ -223,8 +229,11 @@ the installed Skill do not rewrite what an already-run command proved.
223
229
 
224
230
  `City regions` and `Indexers` control only the derived structural maps. Indexers
225
231
  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.
232
+ indexed files into deterministic regions and campuses. A project adds its own
233
+ exclusions, such as example fixtures, in a `## City exclusions` section of
234
+ `genesis/stack.md` with the same ``- Ignore `glob` `` lines (or `- Nothing.`);
235
+ they add to the component exclusions. Both projections are navigation data, not
236
+ proof of complete runtime behavior.
228
237
 
229
238
  ## Project customization
230
239
 
@@ -252,7 +261,7 @@ A selected component may have one prose-only customization at
252
261
  ```
253
262
 
254
263
  Customizations may add or override only Description, Guidance, Adoption,
255
- Post-change, and Deslop. They cannot mutate selection, resources, environment
264
+ Post-change, Deslop, and UI pass. They cannot mutate selection, resources, environment
256
265
  declarations, Verification, Agent Skill ownership, indexers, or opaque consumer
257
266
  sections.
258
267
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.8",
3
+ "version": "1.10.10",
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": {
@@ -32,6 +32,7 @@
32
32
  "prompts/adopt.txt",
33
33
  "prompts/describe.txt",
34
34
  "prompts/deslop.txt",
35
+ "prompts/ui-pass.txt",
35
36
  "prompts/program.txt",
36
37
  "prompts/review.txt",
37
38
  "prompts/start.txt",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.8",
3
+ "version": "1.10.10",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -4,6 +4,9 @@
4
4
  - Prefer direct control flow, existing ownership boundaries, and established project or technology seams.
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
+ - Keep the cost of each request, job and test proportional to the rows it handles, never to stored history or the whole dataset; this is the direct design, not speculative scale work. A list reads each table once for the whole list (one query with an id-list filter, grouped in memory), never one row or lookup per item. A write decides access once and never pays a read-side visibility check whose cost grows with stored history; every visibility check is bounded by the rows being read.
8
+ - Give each list, batch and write path a test at realistic row counts and history that fails above a fixed query or time budget; a handful of rows hides per-row reads. Tests take the time their work needs, no more: prepare databases and other shared fixtures once per run from a stored schema and seed snapshot checked by a digest of their sources, never by replaying the full migration history inside the run, and keep the full-history migration proof as one separate check. Report preparation and suite timings.
7
9
  - 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
10
  - 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.
11
+ - A time limit catches a hang; it does not fix slowness. When a check, test or request is slower than its work needs, measure where the time goes and fix the cause; never raise a time limit, add retries, repeat a call or shrink realistic data or coverage to make it pass.
9
12
  - 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.
@@ -8,6 +8,12 @@ directions. Identify missing or contradictory behavior, misleading explanation,
8
8
  unnecessarily difficult public shape, and verification gaps. Distinguish direct
9
9
  evidence from inference and state questions that require human intent.
10
10
 
11
+ Report what makes requests or tests slower than their work: a list that reads
12
+ one row or lookup per item, an access check whose cost grows with stored
13
+ history, a list, batch or write path without a realistic-count query or time
14
+ budget, test preparation that replays the migration history, and a time limit
15
+ raised to absorb slowness.
16
+
11
17
  Do not edit any file. Return a concise report ordered by impact with exact file
12
18
  references, the evidence inspected, and the smallest sensible correction.
13
19
 
@@ -0,0 +1,23 @@
1
+ This UI pass task has already been rendered by Genesis. Do not generate another
2
+ UI pass prompt. Read and follow `.agents/skills/genesis-ui-pass/SKILL.md`
3
+ completely.
4
+
5
+ This is an explicitly requested behavior-preserving UI pass. With no stated
6
+ scope, select your own changes for the current task across preceding turns,
7
+ committed or uncommitted. An explicit commit, first-parent count, or range in
8
+ USER REQUEST takes precedence regardless of who authored those changes. A named
9
+ section or "the whole app" in USER REQUEST selects that scope instead.
10
+ Establish ownership from task and edit history; the entire dirty diff is not
11
+ automatically yours. If ownership is unclear, ask for scope. A dirty worktree
12
+ is allowed: preserve unrelated edits and existing staging, and leave the pass
13
+ as ordinary uncommitted work for review.
14
+
15
+ Apply the selected Stack UI pass guidance and applicable technology skills in
16
+ addition to the Genesis UI pass contract. Stack guidance owns the design rules
17
+ and the method for each scope; it cannot broaden the resolved scope or turn
18
+ this into another implementation pass. Navigation rules that the Stack
19
+ guidance marks as must-fix are fixed at every scope.
20
+
21
+ Report the resolved scope, what changed, files changed, the checks and
22
+ screenshots actually taken, and any defect or gap deliberately left for
23
+ follow-up.
@@ -151,13 +151,35 @@ 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
+ Speed is verified like behavior, so tests stay fast as the project grows. A
168
+ change to a list, batch or write path ships with a test at realistic row counts
169
+ and history that fails above a fixed query or time budget. Read the run's
170
+ preparation, suite and slowest-case timings with its counts; when one grows,
171
+ find the cause (a read per row, an access check sized by stored history, test
172
+ preparation that replays migrations) before reporting completion. Never raise a
173
+ time limit, add retries, or shrink fixtures or coverage to make a slow check
174
+ pass.
175
+
176
+ An unconfigured result means the declared workspace or checks do not exist
177
+ yet; it is not a failing check. Before reporting completion, compare the
178
+ requested observable behavior, required inputs and resources, declared project
179
+ operations, and focused evidence with what actually exists. Report files
180
+ changed, checks actually run, anything not proven, and anything still requiring
181
+ attention. Never claim that an unrun check passed or that passing checks prove
182
+ the whole product.
161
183
 
162
184
  ## Subsystem maintenance
163
185
 
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: genesis-ui-pass
3
+ description: Make a project's UI exact to its selected Stack's UI guidance (for example Material 3) through a behavior-preserving UI pass over the agent's own task changes, explicitly selected commits, a named section, or the whole app. Use when asked for a UI pass, to make screens follow the design system exactly, or when a host runs its automatic UI pass after a plan.
4
+ ---
5
+
6
+ # Genesis UI pass
7
+
8
+ Make the selected UI exact to the project's design rules without changing what
9
+ the product does. A UI pass is explicit: never run it merely because
10
+ implementation finished, unless the host's automatic UI pass asked for it.
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
+ ## Load the effective project instructions once
40
+
41
+ A UI pass request may arrive as a complete Genesis-generated prompt or as a
42
+ direct request such as `UI pass` or `UI pass the Suppliers section`.
43
+
44
+ If the current task already contains `GENESIS CONTEXT` whose task is
45
+ `ui-pass`, continue with it directly. Do not generate another prompt.
46
+
47
+ Otherwise, before resolving scope, run the Genesis `prompt --task ui-pass`
48
+ operation once using the invocation rule above. For a bare `UI pass` request,
49
+ pass no request argument. When the user names commits, a section, or the whole
50
+ app, pass that exact request as one safely quoted argument. Treat the printed
51
+ prompt as the instructions for this same turn; do not dispatch it to another
52
+ agent and do not generate it again. This step composes the selected Stack's UI
53
+ pass guidance and any project customization with the portable contract below.
54
+
55
+ ## Resolve the scope
56
+
57
+ Inspect `git status --short` and the staged and unstaged diffs before editing.
58
+ A dirty worktree is allowed. Preserve the existing index and unrelated work; do
59
+ not stage, stash, commit, or discard changes as a prerequisite.
60
+
61
+ - **The changes (default).** With no stated scope, select only your own changes
62
+ for the current task, including preceding turns: staged, unstaged, untracked
63
+ and already-committed task changes, identified from task and edit history and
64
+ confirmed against the current source. Neither the dirty diff, the latest
65
+ commit, nor a Git author proves ownership. An explicit commit, first-parent
66
+ count, or range takes precedence, with the same meaning as for Deslop ("the
67
+ last commit" is `HEAD` against its first parent; "the last N commits" is the
68
+ net change across them). In scope: every screen, component and style those
69
+ changes created or edited, and the screens that render them. If you made no
70
+ UI changes, report that there is nothing to pass.
71
+ - **A section.** A named area (a module, a route subtree, a set of screens, or
72
+ a shared component family): every screen in it, touched or not.
73
+ - **The whole app.** Every screen, the shell and navigation included.
74
+
75
+ If the scope is ambiguous, ask which changes or which section.
76
+
77
+ ## Apply the UI pass contract
78
+
79
+ The SELECTED STACK UI PASS GUIDANCE is the design authority: read every
80
+ document it names completely before editing, and follow its method for the
81
+ resolved scope. Without Stack UI pass guidance, follow the project's own
82
+ established design system and the official documentation of its UI framework;
83
+ do not invent a new one.
84
+
85
+ - **Behavior stays as it is.** Change how the UI looks and is laid out, never
86
+ what it does: no feature, field, action, permission, validation, data or
87
+ copy change beyond what the design rules require. A defect, missing behavior,
88
+ contract mismatch or test gap you notice is a follow-up finding, left
89
+ unchanged. Rules the Stack guidance marks as must-fix (such as navigation
90
+ rules) are fixed at every scope, never only reported.
91
+ - **Fix at the owner.** When the framework itself blocks an exact result,
92
+ report the exact framework change instead of compensating with app CSS,
93
+ `!important`, or a copied framework file.
94
+ - **Prove it.** Run the conformance checks the Stack guidance names on the
95
+ scope until they pass. Take screenshots of every affected screen at 390, 768
96
+ and 1280 pixels wide, in light and dark, through the host's managed browser
97
+ or a temporary browser test: after the pass for the changes, and before and
98
+ after for a section or the whole app. Look at every screenshot. Keep them out
99
+ of the repository's tracked files and delete temporary test files. Close
100
+ every browser you start. Run the focused tests of the affected screens.
101
+ - **Edits.** You may edit UI source, styles and tests whose markup changed;
102
+ every behavior assertion stays. Do not edit `.genesis/`, Git metadata,
103
+ dependency directories, generated build output, retained migration history,
104
+ or external resources. Update an affected Program citation only when a move
105
+ makes it stale.
106
+
107
+ Leave the pass as ordinary uncommitted work for review and a later normal
108
+ commit. Never amend or rewrite the selected commits. Summarize the resolved
109
+ scope and any commit identities, what changed, files changed, the checks and
110
+ screenshots actually taken (with their paths), and follow-up findings left
111
+ unchanged.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Genesis UI pass"
3
+ short_description: "Make your task's UI, a section or the whole app exact"
4
+ default_prompt: "Use $genesis-ui-pass to UI pass your own changes for the current task."
package/src/cli.js CHANGED
@@ -76,7 +76,7 @@ const USAGE = `Usage:
76
76
  genesis inspect skills
77
77
  genesis inspect section <name>
78
78
  genesis prompt [request...]
79
- genesis prompt --task <start|adopt|work|deslop|program|blueprint|describe|review> [request...]
79
+ genesis prompt --task <start|adopt|work|deslop|ui-pass|program|blueprint|describe|review> [request...]
80
80
  genesis verify
81
81
  genesis check
82
82
 
@@ -38,6 +38,7 @@ const CORE_SKILLS = [
38
38
  'genesis-project',
39
39
  'genesis-program',
40
40
  'genesis-deslop',
41
+ 'genesis-ui-pass',
41
42
  ].map((name) => ({
42
43
  component: null,
43
44
  package: null,
@@ -10,6 +10,7 @@ const assets = {
10
10
  work: new URL('prompts/work.txt', root),
11
11
  describe: new URL('prompts/describe.txt', root),
12
12
  deslop: new URL('prompts/deslop.txt', root),
13
+ 'ui-pass': new URL('prompts/ui-pass.txt', root),
13
14
  program: new URL('prompts/program.txt', root),
14
15
  blueprint: new URL('prompts/blueprint.txt', root),
15
16
  review: new URL('prompts/review.txt', root),
@@ -308,6 +308,31 @@ async function callWebIgnored(root) {
308
308
  }
309
309
  }
310
310
 
311
+ // The Cities are rebuilt from the working tree, so a copy Git tracks is stale
312
+ // after every commit. Reports the exact fix: ignore lines for any City the
313
+ // patterns miss (matched without the index, which never ignores tracked
314
+ // files), then untracking the tracked copies.
315
+ async function trackedCityDiagnostics(root) {
316
+ const cities = [MACHINE_CITY_PATH, PROGRAM_CITY_PATH];
317
+ const { stdout } = await runGit(root, ['ls-files', '-z', '--cached', '--', ...cities]);
318
+ const tracked = stdout.toString('utf8').split('\0').filter(Boolean);
319
+ if (tracked.length === 0) return [];
320
+ const unignored = [];
321
+ for (const city of cities) {
322
+ try {
323
+ await runGit(root, ['check-ignore', '--quiet', '--no-index', city]);
324
+ } catch (error) {
325
+ if (error.details?.status !== 1) throw error;
326
+ unignored.push(`\`/${city}\``);
327
+ }
328
+ }
329
+ const untrack = `\`git rm --cached -- ${tracked.join(' ')}\` and commit`;
330
+ const message = unignored.length
331
+ ? `Add ${unignored.join(' and ')} to .gitignore, then run ${untrack}`
332
+ : `Run ${untrack}`;
333
+ return [{ code: 'CITY_FILES_TRACKED', path: tracked[0], message }];
334
+ }
335
+
311
336
  // Call sites are only checked for presence up front. Their values are read
312
337
  // when the call web is built, after the observation reader, so a host's
313
338
  // inspector never runs beside them.
@@ -382,6 +407,7 @@ export async function buildProjectIndex({
382
407
  if (await callWebIgnored(root)) callSites = await prepareCallSites(root, indexers, files);
383
408
  else callWebDiagnostics.push({ code: 'CALL_WEB_NOT_IGNORED', path: CALL_WEB_PATH, message: 'Add `/.genesis/call-web.json` to .gitignore' });
384
409
  }
410
+ const cityDiagnostics = await trackedCityDiagnostics(root);
385
411
  const extractionRoot = await extractionCacheRoot(root, 'extractions');
386
412
  const { contributions, diagnostics, cacheLocations } = await extractCodeIndexes({ indexers, files, projectRoot: root, signal, callSites: callSites?.misses.map(entry => entry.path) || [], cacheRoot: extractionRoot });
387
413
  if (callSites) {
@@ -430,7 +456,7 @@ export async function buildProjectIndex({
430
456
  }
431
457
  const architecture = machine.architecture;
432
458
  const discovered = await discoverEntryPoints({ projectRoot: root, architecture, functions: machine.functions, program });
433
- architecture.diagnostics.push(...discovered.diagnostics, ...callWebDiagnostics);
459
+ architecture.diagnostics.push(...discovered.diagnostics, ...callWebDiagnostics, ...cityDiagnostics);
434
460
  architecture.capabilities.entryPoints = discovered.capability;
435
461
  let callWebDocument = null;
436
462
  let callWebSource = null;
@@ -478,7 +504,7 @@ export async function buildProjectIndex({
478
504
  if (callSites) await pruneExtractions(callSites.cacheRoot, callSites.candidates.map(entry => entry.location));
479
505
  }
480
506
  const allMatches = machine.functions.filter((entry) => functionMatches(entry, queries));
481
- // Entry-point and call-web diagnostics are already part of the architecture's.
507
+ // Entry-point, call-web and tracked-City diagnostics are already part of the architecture's.
482
508
  const allDiagnostics = [...diagnostics, ...program.diagnostics, ...architectureDiagnostics];
483
509
  return {
484
510
  status: allDiagnostics.length > 0 ? 'completed-with-warning' : 'ready',
@@ -1,6 +1,7 @@
1
1
  export const GENESIS_CONTRACTS = Object.freeze({
2
2
  subsystems: 'genesis.subsystems.v0',
3
3
  projectInspection: 'genesis.project-inspection.v1',
4
+ promptTasks: 'genesis.prompt-tasks.v1',
4
5
  templates: 'genesis.templates.v1',
5
6
  templateApplication: 'genesis.template-application.v1',
6
7
  collaboration: 'genesis.collaboration.v1',
@@ -29,7 +29,7 @@ export const GENESIS_BOOTSTRAP_PATHS = Object.freeze([
29
29
  '.claude/settings.json',
30
30
  '.opencode/plugins/genesis-project-guidance.js',
31
31
  '.agents/skills/.genesis-managed.json',
32
- ...['genesis-project', 'genesis-program', 'genesis-deslop'].flatMap((name) => [
32
+ ...['genesis-project', 'genesis-program', 'genesis-deslop', 'genesis-ui-pass'].flatMap((name) => [
33
33
  `.claude/skills/${name}`,
34
34
  `.agents/skills/${name}/SKILL.md`,
35
35
  `.agents/skills/${name}/agents/openai.yaml`,
@@ -11,6 +11,7 @@ import {
11
11
  import { readStack, stackPromptContext } from './stack.js';
12
12
  import { listStackCatalogPieces } from './stack-catalog.js';
13
13
  import { normalizeStackPieceId } from './stack-piece.js';
14
+ import { GENESIS_CONTRACTS } from './contracts.js';
14
15
  import { GenesisError } from './errors.js';
15
16
  import { gitContext } from './git.js';
16
17
  import { inspectProgram } from './program.js';
@@ -22,12 +23,13 @@ import { classifyProjectKind } from './project-files.js';
22
23
  import { inspectProjectFormatAtRoot } from './project-format.js';
23
24
  import { inspectProject } from './project-inspection.js';
24
25
 
25
- const TASKS = new Set(['start', 'adopt', 'work', 'deslop', 'program', 'blueprint', 'describe', 'review']);
26
+ const TASKS = new Set(['start', 'adopt', 'work', 'deslop', 'ui-pass', 'program', 'blueprint', 'describe', 'review']);
26
27
  const DEFAULT_REQUEST = {
27
28
  start: 'Start a conversation about this project.',
28
29
  adopt: 'Import the existing project into a truthful Genesis project contract.',
29
30
  work: 'Implement the product intent expressed by the current Blueprint.',
30
31
  deslop: 'Deslop your own changes for the current task.',
32
+ 'ui-pass': 'UI pass your own changes for the current task.',
31
33
  program: 'Refresh the complete useful Program for the code that exists now.',
32
34
  describe: 'Create or refresh the complete Blueprint and useful Program for the codebase that exists now.',
33
35
  review: 'Review the complete useful relationship between Blueprint, code, Program, and tests.',
@@ -131,6 +133,7 @@ function renderPrompt({
131
133
  skills = '',
132
134
  guidance = '',
133
135
  cleanup = '',
136
+ uiPass = '',
134
137
  postChange = '',
135
138
  adoption = '',
136
139
  collaborationGuidance = '',
@@ -155,6 +158,7 @@ function renderPrompt({
155
158
  ...(skills ? ['', 'AVAILABLE AGENT SKILLS', '', skills] : []),
156
159
  ...(postChange ? ['', 'SELECTED STACK POST-CHANGE GUIDANCE', '', postChange] : []),
157
160
  ...(cleanup ? ['', 'SELECTED STACK CLEANUP GUIDANCE', '', cleanup] : []),
161
+ ...(uiPass ? ['', 'SELECTED STACK UI PASS GUIDANCE', '', uiPass] : []),
158
162
  '',
159
163
  ].join('\n');
160
164
  }
@@ -404,6 +408,21 @@ async function generateStartPrompt({
404
408
  };
405
409
  }
406
410
 
411
+ /**
412
+ * The prompt tasks Genesis renders, each with whether the selected Stack adds its own guidance to that task (Adoption,
413
+ * Post-change, Deslop or UI pass prose). A host offers a Stack-dependent operation, such as an automatic UI pass,
414
+ * from this answer instead of inferring it from component ids.
415
+ */
416
+ export async function inspectProjectPromptTasks({ projectRoot, stackPackages = [] } = {}) {
417
+ const root = (await gitContext(projectRoot)).repositoryRoot;
418
+ const stack = await readStack(root, { stackPackages });
419
+ const lanes = { adopt: stack.adoption, work: stack.postChange, deslop: stack.deslop, 'ui-pass': stack.uiPass };
420
+ return {
421
+ contract: GENESIS_CONTRACTS.promptTasks,
422
+ tasks: [...TASKS].map((task) => ({ task, stackGuidance: Boolean(lanes[task]) })),
423
+ };
424
+ }
425
+
407
426
  export async function generateProjectPrompt({
408
427
  environment = process.env,
409
428
  hiddenStackPieces = [],
@@ -574,6 +593,7 @@ export async function generateProjectPrompt({
574
593
  skills: renderAgentSkillCatalog(projectSkills.skills),
575
594
  postChange: task === 'work' ? stack.postChange : '',
576
595
  cleanup: task === 'deslop' ? stack.deslop : '',
596
+ uiPass: task === 'ui-pass' ? stack.uiPass : '',
577
597
  });
578
598
  return {
579
599
  status: 'ready',
@@ -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
@@ -16,6 +16,7 @@ const STACK_CUSTOMIZATION_FIELDS = new Map([
16
16
  ['Adoption', 'adoption'],
17
17
  ['Post-change', 'postChange'],
18
18
  ['Deslop', 'deslop'],
19
+ ['UI pass', 'uiPass'],
19
20
  ]);
20
21
  const STACK_PIECE_SECTIONS = new Set([
21
22
  'Adoption',
@@ -31,6 +32,7 @@ const STACK_PIECE_SECTIONS = new Set([
31
32
  'Requires',
32
33
  'Resources',
33
34
  'Skill',
35
+ 'UI pass',
34
36
  'Verification',
35
37
  ]);
36
38
 
@@ -115,12 +117,12 @@ function customizationSections(source, piecePath) {
115
117
  if (/^#{1,3}\s+/u.test(line)) invalid(piecePath, `Unknown customization heading: ${line.trim()}.`);
116
118
  if (field) content.push(line);
117
119
  else if (line.trim()) {
118
- invalid(piecePath, 'Customization content must be below Description, Guidance, Adoption, Post-change, or Deslop.');
120
+ invalid(piecePath, 'Customization content must be below Description, Guidance, Adoption, Post-change, Deslop, or UI pass.');
119
121
  }
120
122
  }
121
123
  finishField();
122
124
  if (Object.values(result).every((values) => Object.keys(values).length === 0)) {
123
- invalid(piecePath, 'Stack customization must add or override Description, Guidance, Adoption, Post-change, or Deslop.');
125
+ invalid(piecePath, 'Stack customization must add or override Description, Guidance, Adoption, Post-change, Deslop, or UI pass.');
124
126
  }
125
127
  return result;
126
128
  }
@@ -329,6 +331,7 @@ export function parseStackPieceSource(value, {
329
331
  const adoption = textSection(all, 'Adoption');
330
332
  const postChange = textSection(all, 'Post-change');
331
333
  const deslop = textSection(all, 'Deslop');
334
+ const uiPass = textSection(all, 'UI pass');
332
335
  if (!description) invalid(piecePath, 'Stack piece needs Description.');
333
336
  const verificationCommands = parseStackVerificationLines(
334
337
  all.get('Verification') || [],
@@ -364,6 +367,7 @@ export function parseStackPieceSource(value, {
364
367
  environmentFiles: environmentFiles || [],
365
368
  extensions: opaqueStackSections(all, STACK_PIECE_SECTIONS),
366
369
  deslop,
370
+ uiPass,
367
371
  };
368
372
  }
369
373
 
@@ -412,5 +416,6 @@ export function applyStackPieceCustomization(piece, customization) {
412
416
  customization.add.postChange,
413
417
  ),
414
418
  deslop: customizedText(piece.deslop, customization.override.deslop, customization.add.deslop),
419
+ uiPass: customizedText(piece.uiPass, customization.override.uiPass, customization.add.uiPass),
415
420
  };
416
421
  }
@@ -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,
@@ -404,6 +414,7 @@ async function readStackWithContractMode(projectRoot, {
404
414
  adoption: composedProse(components, 'adoption'),
405
415
  postChange: composedProse(components, 'postChange'),
406
416
  deslop: composedProse(components, 'deslop'),
417
+ uiPass: composedProse(components, 'uiPass'),
407
418
  };
408
419
  }
409
420
 
package/src/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  } from './index/collaboration.js';
13
13
  import { GENESIS_CONTRACTS } from './index/contracts.js';
14
14
  import { contextForProjectPaths } from './index/context.js';
15
- import { generateProjectPrompt } from './index/prompt.js';
15
+ import { generateProjectPrompt, inspectProjectPromptTasks } from './index/prompt.js';
16
16
  import {
17
17
  inspectProjectEngineering,
18
18
  listEngineeringProfileCatalog,
@@ -192,6 +192,10 @@ export function generatePrompt(options) {
192
192
  return generateProjectPrompt(options);
193
193
  }
194
194
 
195
+ export function inspectPromptTasks(options) {
196
+ return inspectProjectPromptTasks(options);
197
+ }
198
+
195
199
  export function getContext({ paths, projectRoot = process.cwd(), stackPackages = [] } = {}) {
196
200
  return contextForProjectPaths({ paths, projectRoot, stackPackages });
197
201
  }