genesis-compiler 1.10.9 → 1.10.11

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,35 @@ 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 and correct as it
367
+ grows, through checks the agent makes and judges rather than numbers it must
368
+ hit. A request, job or test costs in proportion to the rows it handles, never
369
+ to stored history: the agent counts a list's, batch's or write's queries at a
370
+ few rows and at a realistic number and finds what grows with the rows. Tests
371
+ take the time their work needs: the agent reads the suite's time and its
372
+ slowest files and fixes where the time goes, prepares test databases once per
373
+ run (usually from a stored snapshot checked by a digest, with the full
374
+ migration history replayed only by one separate fresh-schema check), and keeps
375
+ each test file independent so files run in parallel and two identical runs
376
+ agree. A refactor lists the behaviors and tests before changing code and walks
377
+ the list again afterwards.
378
+ A failing test is a defect to explain, never noise to rerun, skip or call
379
+ pre-existing, and a final run is one whole run of the pushed tree. Each declared
380
+ constraint and protection is tried with one input that breaks it, access comes
381
+ from the trusted context, and a change others depend on fails loudly where the
382
+ old shape is still used. A short list names what is wrong in any context, such
383
+ as a silently capped read, an edited migration that has run, an edited
384
+ generated file, or tests against a real database. A time limit catches a hang;
385
+ a slow check is measured and its cause fixed, never hidden by a longer limit,
386
+ retries or smaller data. Coarse outer limits, such as a list making hundreds of
387
+ queries or a suite running for hours, mark what is clearly broken; they are not
388
+ targets. The baseline is the short form: the genesis-project skill's sanity
389
+ checks guide (`references/sanity-checks.md`) gives each check's examples and
390
+ outer limits and is read before the agent reports such work done, and each
391
+ Stack piece's "Sanity checks with <stack>" guidance names the stack's own tools
392
+ for them, such as its query counter, parallel test runner and strict loading
393
+ mode.
394
+
366
395
  The installed, versioned profile descriptions live in
367
396
  `profiles/engineering/*.md`. Straight Genesis users can inspect and select them
368
397
  without editing Markdown grammar:
@@ -575,6 +604,28 @@ up; it does not silently select the latest commit. Its result is a normal
575
604
  uncommitted diff for review and a later commit. It never amends selected commits,
576
605
  silently fixes newly discovered defects, or runs as an automatic completion phase.
577
606
 
607
+ ### UI pass your changes, a section, or the whole app
608
+
609
+ A UI pass makes UI exact to the selected Stack's design rules without changing
610
+ behavior. Ask directly, or generate the prompt for a host:
611
+
612
+ ```text
613
+ UI pass
614
+ UI pass the last commit
615
+ UI pass the Suppliers section
616
+ UI pass the whole app
617
+ ```
618
+
619
+ ```bash
620
+ genesis prompt --task ui-pass "UI pass the Suppliers section"
621
+ ```
622
+
623
+ The installed `genesis-ui-pass` skill resolves the scope as Deslop does, adds
624
+ the named-section and whole-app scopes, and follows the effective `UI pass`
625
+ prose of the selected Stack components, which names the technology's design
626
+ rules and method. Defects become findings; screenshots at 390, 768 and 1280
627
+ pixels in light and dark are the evidence. See [docs/ui-pass.md](docs/ui-pass.md).
628
+
578
629
  Projects can customize a selected technology through its matching file, such
579
630
  as `genesis/stack/jskit.md`:
580
631
 
@@ -821,6 +872,7 @@ add or replace complete project sections when its real scripts differ.
821
872
  | `Deployment` build | `npm run build` | Produce the deployable application build. | The deployment consumer. |
822
873
  | `Deployment` serve | `npm start` | Start the built production application. | The deployment consumer. |
823
874
  | `Deployment` readiness | `GET /api/health` → `200` | Prove that the deployed service is ready. | The deployment consumer. |
875
+ | `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
876
 
825
877
  The JSKIT proposal also declares `.env` as its environment projection and says
826
878
  that inactive deployment workspaces may recreate `node_modules`; it deliberately
@@ -834,7 +886,8 @@ database-preparation command. If the application implements a real migration or
834
886
  seed script, the agent records that exact command in the complete project
835
887
  sections whose consumers need it. Local authentication similarly requires the
836
888
  application to declare `AUTH_LOCAL_SESSION_SECRET` without committing its
837
- value.
889
+ value; the JSKIT Deployment proposal lists it as a generated secret for the
890
+ deployment consumer to create.
838
891
 
839
892
  ### How the Stack contract is enforced
840
893
 
@@ -938,9 +991,15 @@ ordinary project operations stop until the migration is resolved.
938
991
  ## Development
939
992
 
940
993
  Run `npm ci` in this source checkout, then `npm test`. The test command runs the
941
- original test files with file concurrency fixed at one. The local first-party
942
- catalogue is a development-only dependency so catalogue and starter tests work
943
- after a clean install; it is not required by the published compiler runtime.
994
+ test files in parallel at Node's default file concurrency; each file creates its
995
+ own temporary projects and directories, so it can run beside any other. The
996
+ suite is kept cheap by what it runs rather than by how many cores share it: a
997
+ test about command logic calls the CLI's own `runCli` in its process, each
998
+ command keeps one real `genesis` run, test projects copy one prepared empty Git
999
+ repository, and the template tests copy one prepared starter instead of
1000
+ rebuilding it. The local first-party catalogue is a development-only dependency
1001
+ so catalogue and starter tests work after a clean install; it is not required by
1002
+ the published compiler runtime.
944
1003
 
945
1004
  ## Public API
946
1005
 
@@ -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.
@@ -255,7 +261,7 @@ A selected component may have one prose-only customization at
255
261
  ```
256
262
 
257
263
  Customizations may add or override only Description, Guidance, Adoption,
258
- Post-change, and Deslop. They cannot mutate selection, resources, environment
264
+ Post-change, Deslop, and UI pass. They cannot mutate selection, resources, environment
259
265
  declarations, Verification, Agent Skill ownership, indexers, or opaque consumer
260
266
  sections.
261
267
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.9",
3
+ "version": "1.10.11",
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",
@@ -47,7 +48,7 @@
47
48
  "docs/parsers.md"
48
49
  ],
49
50
  "scripts": {
50
- "test": "node --test --test-concurrency=1 test/*.test.js"
51
+ "test": "node --test test/*.test.js"
51
52
  },
52
53
  "jskitRelease": {
53
54
  "versionFiles": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.9",
3
+ "version": "1.10.11",
4
4
  "description": "Makes Codex aware of optional Genesis adoption for existing projects.",
5
5
  "author": {
6
6
  "name": "Mobily Enterprises"
@@ -4,6 +4,15 @@
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
+ - A request, job or test costs what the rows it handles cost, never what stored history or the whole dataset costs; this is the direct design, not speculative scale work. Check it instead of assuming it: count a list's, batch's or write's queries or storage calls at a few rows and at a realistic number, and judge whether the count follows the pages read or grows with the rows. A count that grows usually means a read, lookup or access check runs per row, or an access check is sized by stored history; related rows come in batches, one query per related table, and every visibility check is bounded by the rows being read. A list that makes hundreds of queries, or a save that takes tens of seconds, is broken: find the cause first. Such limits mark what is clearly broken; they are not targets, and code is never bent to reach a number.
8
+ - Give list, batch and write paths a test at realistic row counts and history, since a handful of rows hides per-row work; it compares counts at two sizes rather than holding a fixed ceiling tuned until it passes. Tests take the time their work needs, no more: read the suite's total time and its slowest files, and find where a slow file's time goes (database preparation, replayed migrations, fixed sleeps, polling, server or build starts). Prepare databases and shared fixtures once per run and reuse them, usually from a stored schema and seed snapshot checked by a digest of their sources rather than by replaying migrations, and keep the full-history migration proof as one separate check. Each test file owns its data, ports and files, never touches a development or production database, and can run beside any other, so two identical runs give the same result; files run in parallel within the machine's test budget, and a suite whose wall time is close to the sum of its files' times ran them one at a time, so find why. A suite that runs for hours, or test preparation measured in minutes, is broken.
9
+ - A refactor, consolidation or move keeps every behavior, or drops one only with the person's agreement. Before changing code, list what it does (its operations, inputs and their validation, permissions, states and edge cases) with the test covering each, adding one where none does; afterwards walk the list again, with the original tests passing and their assertions unchanged. A change that deletes tests or weakens their assertions is not a refactor.
10
+ - A failing test is a defect to explain, in the product or in the test, never noise or a "pre-existing" baseline: do not rerun it until it passes, skip it or loosen it to finish. A final run is one run of the whole suite on the tree that is pushed; reruns, chunks and partial runs are reported as partial. A skipped test records its reason and the run that does cover it. A new test is seen to fail once, by breaking the behavior or reverting the fix.
11
+ - Every constraint and protection the code declares is enforced: send one input that breaks each (a missing required field, a call past a rate limit, a caller without permission, another tenant's id) and see it refused. Authorization is decided before validation answers, from the trusted context rather than ids the client sends, and a missing value, scope or collaborator refuses instead of allowing.
12
+ - A change that other code depends on (a name, a default, a contract) fails loudly, naming its fix, wherever the old shape is still used, and every dependent project is booted and checked with it.
13
+ - Never, whatever the context: cap a read its callers treat as complete; drop a behavior, rule or access decision silently; declare a constraint or protection that nothing enforces; let a client's ids or a missing value decide access; store a missing value as a real one; run tests against a developer's or production database or configuration; edit a migration that has run anywhere; edit a generated file instead of its source; let the first visitor to a new deployment own it; run production with development settings; release a test's servers, processes or timers only where a timeout skips it. Each one silently loses data, protection or a whole run.
7
14
  - 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
15
  - 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.
16
+ - 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
17
  - 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.
18
+ - These are the short form. Genesis's sanity checks guide (`.agents/skills/genesis-project/references/sanity-checks.md`) gives each check's examples and outer limits: read the section that applies before reporting data access, tests, a refactor, a screen or a release done, and apply it with the tools your Stack pieces name.
@@ -16,5 +16,6 @@ addition to the Genesis Deslop contract. Stack guidance may improve
16
16
  technology-specific cleanup, but it cannot broaden the resolved scope,
17
17
  weaken behavior preservation, or turn this into another implementation pass.
18
18
 
19
- Report the resolved scope, what became simpler, files changed, checks actually
20
- run, and any defect or gap deliberately left for follow-up.
19
+ Report the resolved scope, what became simpler, the behaviors checked before
20
+ and after, files changed, checks actually run, and any defect or gap
21
+ deliberately left for follow-up.
@@ -8,6 +8,19 @@ 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, citing a count or
12
+ timing where one exists and the code path otherwise: a list or batch whose
13
+ query count grows with its rows, an access check whose cost grows with stored
14
+ history, a list, batch or write path never tested at realistic counts, test
15
+ preparation that replays the migration history, tests that share drifting data
16
+ or cannot run in parallel, and a time limit raised to absorb slowness. Report
17
+ behavior a refactor dropped: operations, validation, permissions or edge cases
18
+ the earlier code had and the current code lacks, and tests deleted or weakened
19
+ with it. Report declared constraints and protections that nothing enforces, and
20
+ access decided from ids the client sends rather than the trusted context.
21
+ Judge each against the matching section and outer limit of
22
+ `.agents/skills/genesis-project/references/sanity-checks.md`.
23
+
11
24
  Do not edit any file. Return a concise report ordered by impact with exact file
12
25
  references, the evidence inspected, and the smallest sensible correction.
13
26
 
@@ -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.
@@ -108,6 +108,14 @@ abstractions. Consolidate ownership where one clear module is enough. Do not
108
108
  optimize for tiny files or indirection; optimize for a small, obvious design
109
109
  that a junior programmer can follow.
110
110
 
111
+ Before editing, list the behaviors of the code in scope with the test that
112
+ covers each, as the engineering approach's refactor rule and the "Refactors
113
+ keep behavior" section of
114
+ `.agents/skills/genesis-project/references/sanity-checks.md` describe. After
115
+ cleanup, walk the list again: every item must still hold and still have a test,
116
+ and a test the cleanup changed must assert no less than before. A behavior no
117
+ test covers is a follow-up finding, never permission to drop it.
118
+
111
119
  Cleanup is not a second implementation pass. The selected changes limit where
112
120
  cleanup may occur; sharing a file does not bring unrelated edits into scope or
113
121
  authorize different behavior in that file. Deslop may reorganize behavior
@@ -136,5 +144,5 @@ Leave the cleanup as ordinary uncommitted work for review and a later normal
136
144
  commit. Never amend or rewrite the selected commits. Run focused checks when
137
145
  useful. Final declared checks remain available through the Genesis `verify`
138
146
  operation. Summarize the resolved scope and any selected commit identities,
139
- what became simpler, files changed, checks actually run, and follow-up findings
140
- left unchanged.
147
+ what became simpler, the behaviors checked before and after, files changed,
148
+ checks actually run, and follow-up findings left unchanged.
@@ -164,6 +164,18 @@ despite a supplied login), report the host defect with evidence and stop; never
164
164
  compensate in the project by rewriting cookies or adding its own login or
165
165
  browser.
166
166
 
167
+ Speed is verified like behavior, with the engineering approach's sanity
168
+ checks. A change to a list, batch or write path ships with a test at realistic
169
+ row counts and history that compares its query count at a few rows and at a
170
+ realistic number. Read the run's preparation, suite and slowest-file timings
171
+ with its counts; when one grows with the data, find the cause (a read per row,
172
+ an access check sized by stored history, test preparation that replays
173
+ migrations) before reporting completion. After a refactor, walk the behavior
174
+ list made before it and report any item that no longer holds. Before reporting
175
+ data access, tests, a refactor, a screen or a release done, read the matching
176
+ section of [the sanity checks guide](references/sanity-checks.md) and apply its
177
+ checks with the tools your Stack pieces name.
178
+
167
179
  An unconfigured result means the declared workspace or checks do not exist
168
180
  yet; it is not a failing check. Before reporting completion, compare the
169
181
  requested observable behavior, required inputs and resources, declared project
@@ -0,0 +1,352 @@
1
+ # Sanity checks
2
+
3
+ The Engineering baseline states these checks in short form. This guide gives
4
+ each one's examples and outer limits. Read the section that applies before
5
+ reporting data access, tests, a refactor, a screen or a release done.
6
+
7
+ ## How to use this guide
8
+
9
+ - Each section gives what good looks like, the check you make and judge, real
10
+ failures it catches, and an outer limit.
11
+ - Measure, then judge. Prefer counts (queries, requests, processes, DOM nodes)
12
+ to clock times, which change with the machine.
13
+ - An outer limit marks what is clearly broken, never a target: code just under
14
+ one is not thereby fine, and code is never bent to reach a number.
15
+ - Apply each check with the tools your Stack pieces name under "Sanity checks
16
+ with <stack>": the query counter, the parallel runner, the strict mode.
17
+ - Examples come from real projects; one that depends on a stack names it.
18
+
19
+ ## Never do these
20
+
21
+ Each one silently loses data, protection or a whole run.
22
+
23
+ - Cap a read its callers treat as complete: they act on a silent first page.
24
+ - Drop a behavior, rule or access decision silently in a restructure.
25
+ - Declare a constraint or protection that nothing enforces.
26
+ - Let a client's ids or a missing value decide access.
27
+ - Store a missing value as a real one: a blank later reads as fact.
28
+ - Run tests against a development or production database or configuration.
29
+ - Edit a migration that has run anywhere: databases diverge.
30
+ - Let the first visitor to a new deployment own it.
31
+ - Run production with development settings, fallbacks, hosts or secrets.
32
+ - Release a test's servers, processes or timers only where a timeout skips it.
33
+ - Edit a generated file instead of its source: the next generation erases it.
34
+
35
+ ## Data: loading a dataset
36
+
37
+ **Good:** a list, page or report takes about as many queries for 10 rows as
38
+ for 200. Related rows come in one batch per related table, and each visibility
39
+ check is part of the query, bounded by the rows read.
40
+
41
+ **Check:** seed hundreds of rows and long per-person histories. Count the
42
+ queries at a small and a realistic size and look at what grows. A count that
43
+ grows with the rows means something runs per row: a single-record read, lookup
44
+ or access check inside a loop. A count that grows with a person's history means
45
+ an access check sized by history. Read the captured SQL for:
46
+ - a schema-metadata read on a request path;
47
+ - a row read back just after it was written;
48
+ - the same table and id read twice in one request;
49
+ - one statement repeated with one id at a time;
50
+ - access-policy SQL that runs to kilobytes (print it per role and read its
51
+ plan).
52
+
53
+ Compare a response's size with what the screen shows of it.
54
+
55
+ **Examples:**
56
+ - A page of 100 incidents read each incident's revision, pinned module and
57
+ readers one at a time: 1,801 queries and 5–7 s. Batched per table it took
58
+ 181, still about one a row.
59
+ - A notice policy checked every source a reader had ever been notified about:
60
+ 26–33 s and 13,176 queries per 100 notices.
61
+ - Schema metadata joined across a database server holding hundreds of
62
+ databases cost 25–63 s per call (MySQL `information_schema`).
63
+
64
+ **Outer limit:** a list that makes hundreds of queries, takes over a second on
65
+ a quiet database, runs the same access check more than a few times per
66
+ request, or returns megabytes for one screen is broken. Find the loop first.
67
+
68
+ ## Data: saves, batches and background work
69
+
70
+ **Good:** a batch writes each table in a few statements inside the owner's
71
+ transaction, not one full create per row. Work nobody waits for runs as a
72
+ scheduled or queued job in bounded chunks. A read request takes no lock and
73
+ writes nothing. Two writers on the same records never produce a server error.
74
+
75
+ **Check:** count a batch's statements at 10 items and at 500. Look for due work
76
+ run inside whichever request comes first, and one long transaction around it.
77
+ Find any lock or write a read path takes for one-time setup: read first, lock
78
+ only on the rare unset path. Run two writers on the same records; a retried
79
+ transaction must be safe to repeat.
80
+
81
+ **Examples:**
82
+ - An import created each row through the full single-record create path, with
83
+ its lookups and read-back, one row at a time.
84
+ - Overdue work ran inside the first user request after a quiet period, so that
85
+ request's time depended on how long nobody had used the app.
86
+ - Concurrent saves surfaced database deadlocks as 500s.
87
+
88
+ **Outer limit:** thousands of queries or tens of seconds for one action, or a
89
+ request whose time depends on how long since anyone used the app.
90
+
91
+ ## Work that repeats or lingers
92
+
93
+ **Good:** work whose answer does not change between calls runs once and is
94
+ reused. Everything a feature starts has a named owner that stops it. Each poll
95
+ stands in for a named event, and retries cover network errors, 5xx and 429
96
+ only. Logs say each thing once.
97
+
98
+ **Check:** time a call that should do nothing. List the processes alive after
99
+ the app has been idle. After a run, read the log's size and most repeated line.
100
+
101
+ **Example:** an idempotent "ensure" step rediscovered its whole configuration on
102
+ every call.
103
+
104
+ **Outer limit:** a no-op check over a second, an idle page requesting every
105
+ second, or a log growing by hundreds of MB per run.
106
+
107
+ ## The wait a person sees
108
+
109
+ **Good:** the time from click to result is known, cold and warm, and each part
110
+ of it has a name.
111
+
112
+ **Check:** read the server's own request times while the tests run. Time the
113
+ main actions from click to result, and split the time into its parts.
114
+
115
+ **Outer limit:** an ordinary page taking seconds on a test database, or tens of
116
+ seconds with no feedback.
117
+
118
+ ## Refactors keep behavior
119
+
120
+ **Good:** a refactor, consolidation or move keeps every behavior and access
121
+ decision, or drops one only with the person's agreement.
122
+
123
+ **Check:** before changing code, list its operations, inputs and their
124
+ validation, permissions per role, states and edge cases, with the test covering
125
+ each. Afterwards walk the list again with the original tests passing and their
126
+ assertions unchanged. Compare per-file test counts before and after, and name
127
+ each removal. Search for what a removed caller was the only caller of. After a
128
+ mechanical sweep, compare files changed with files expected and search for the
129
+ old form. A new mechanism replaces the one already doing the job.
130
+
131
+ **Examples:**
132
+ - Restructures dropped validation and access rules along with the tests that
133
+ covered them, so the suite stayed green.
134
+
135
+ **Outer limit:** dozens of dropped items found at review.
136
+
137
+ ## Failing tests are bugs
138
+
139
+ **Good:** a failing test is a defect with an owner, in the product or in the
140
+ test. Red CI on the main branch stops the line. A final run is one whole run on
141
+ the tree that is pushed.
142
+
143
+ **Check:** name each failure's cause; never rerun until green, skip or loosen
144
+ to finish. Compare the tested commit with the pushed one. List every skip with
145
+ its reason and the run that covers it, against the last run's count. See each
146
+ new test fail once. Diagnosis runs may stop early; a final run runs everything
147
+ and reports what did not run. Report a speed win with its absolute number.
148
+
149
+ **Examples:**
150
+ - A preparation script ended with `exit $status` instead of
151
+ `test $status -eq 0`, so it ended the caller and the tests never ran while
152
+ the run reported success (shell).
153
+ - A suite kept a set of "known" failures as a baseline until they hid a new
154
+ regression.
155
+
156
+ **Outer limit:** a red test on main nobody owns, a suite that needs reruns to
157
+ look green, or dozens of skips nobody can place.
158
+
159
+ ## Tests: time
160
+
161
+ **Good:** tests take the time their work needs. Files run in parallel within
162
+ the machine's test budget. Databases and fixtures are prepared once per run
163
+ from a stored schema and seed, checked by a digest of their sources, and the
164
+ full migration history is replayed once, as its own check. Builds and server starts happen once per run.
165
+
166
+ **Check:** after every run, read the suite time, each phase's time
167
+ (preparation, import, sign-in, tests) and the slowest files. A wall time close
168
+ to the sum of the files' times means they ran one at a time: find why. In a
169
+ slow file, find where the time goes: repeated preparation, replayed
170
+ migrations, fixed sleeps, polling, a process per test, repeated builds or
171
+ sign-ins. Split test time into the app's request time and the harness's before
172
+ choosing a fix. Remove exact repeats across CI jobs. Each sleep names the
173
+ condition it waits for; wait for that. Compare each raised time limit with the
174
+ measured need, and check a limit exists. Assert counts, not tight times.
175
+
176
+ **Examples:**
177
+ - 359 tests ran one file at a time, 163 s in all, because the test command
178
+ forced a concurrency of one.
179
+ - Test databases were rebuilt by replaying every migration for each file.
180
+ - Time limits were raised until slow tests passed, and the slowness stayed.
181
+
182
+ **Outer limit:** a suite running for hours (a small app's for an hour),
183
+ preparation in minutes, or a limit ten times the measured need.
184
+
185
+ ## Tests: each owns what it uses
186
+
187
+ **Good:** each test creates and signs in as what it asserts, and each file owns
188
+ its data, system-picked ports, directories and caches. A skip caused by the
189
+ environment is a failure.
190
+
191
+ **Check:** run a file twice, beside another, reversed and shuffled, and two
192
+ suites at once; the result does not change. Run at a fake time near midnight,
193
+ in another time zone and in a clean environment. Each seed names what needs it;
194
+ volume fixtures belong to the volume tests. A disposable test database server
195
+ may relax durability; each run drops what it made.
196
+
197
+ **Examples:**
198
+ - Specs passed only after an earlier spec left a signed-in user behind.
199
+ - Two parallel runs collided on one fixed port and one shared cache directory.
200
+
201
+ **Outer limit:** a test that passes only in one order, or two identical runs
202
+ that differ.
203
+
204
+ ## Tests: nothing hangs or lingers
205
+
206
+ **Good:** everything a test starts is released by teardown that also runs
207
+ after a timeout, and the process exits when its tests end. Waits wait for the
208
+ thing that matters.
209
+
210
+ **Check:** list what keeps a finished file alive. A wait that often ends at its
211
+ cap waits for the wrong thing: an idle network on a page with live connections,
212
+ or an animation that never ends.
213
+
214
+ **Outer limit:** a file that ends only when its limit kills it.
215
+
216
+ ## Tests that prove behavior
217
+
218
+ **Good:** a test drives the real path, passes after a behavior-preserving
219
+ refactor and fails when the behavior breaks. Doubles answer like the real
220
+ collaborator, refusals included. Every spec is linted and listed by the quick
221
+ check.
222
+
223
+ **Check:** ask whether each test you touch pins source text or internals. An
224
+ export, option or branch only tests use goes. Name the committed test for each
225
+ user-visible feature you changed.
226
+
227
+ **Outer limit:** a feature shipped with no test that would fail without it.
228
+
229
+ ## Inputs and access
230
+
231
+ **Good:** every declared constraint and protection is enforced and has one
232
+ test that trips it. Access comes from the authenticated context, is decided
233
+ before validation answers, and refuses when a scope, role or collaborator is
234
+ missing. Refusals keep their status, code and message to the client. Writes go
235
+ through the layer that applies policy, validation and audit.
236
+
237
+ **Check:** send one input that breaks each constraint (a missing required
238
+ field, a call past a rate limit, a caller without permission, another tenant's
239
+ id) and see it refused. A malformed request from someone without permission
240
+ gets a 403 and no field errors. Trace inputs that reach file paths. Ask what
241
+ production does when each optional collaborator is missing: "nothing,
242
+ silently" means it is required. Each write that bypasses the policy layer, and
243
+ each "anyway" override, names its reason. Before removing a guard, prove the
244
+ layer below enforces it.
245
+
246
+ **Examples:**
247
+ - A partial-update mode accepted an empty body and saved nothing, while the
248
+ schema declared the fields required.
249
+ - A framework silently discarded fields the model did not allow, and the save
250
+ reported success (Laravel without strict mode).
251
+
252
+ **Outer limit:** a request missing a required field succeeds, or a user acts in
253
+ another tenant.
254
+
255
+ ## Changes others depend on
256
+
257
+ **Good:** a change to a name, default or contract fails loudly, naming its
258
+ fix, wherever the old shape remains. A fix reaches every copy of the code it
259
+ fixes. A new setup step runs in every deploy path.
260
+
261
+ **Check:** boot and check every dependent with the change. Search every copy
262
+ of fixed code, scaffold copies included. Prove a new setup step runs in the
263
+ deploy path. Search the docs for removed or renamed names. A local workaround
264
+ for a dependency's gap names the owner's fix and is marked for removal.
265
+
266
+ **Outer limit:** a project that runs differently after an update without
267
+ saying so.
268
+
269
+ ## Screens in use
270
+
271
+ **Good:** a screen renders what is visible, loads what its first view needs,
272
+ and feedback never covers the next action. Each inner page opens directly and
273
+ by link, and Up and Back stay inside the app. Screens use the design system's
274
+ tokens and components and show names, not ids.
275
+
276
+ **Check:**
277
+ - Count DOM nodes with realistic data, and the requests one action causes
278
+ across open screens. Follow a big first-screen chunk to its import.
279
+ - After each action, look at what its feedback covers, use the next action and
280
+ press Escape. At phone width, tap each control after the previous field
281
+ blurs.
282
+ - Scroll every screen to its end at about 390 px, 1,000 px and desktop width,
283
+ in light and dark. With a cold cache, look for numbers where names belong.
284
+ - Make each endpoint the first request after a restart; go offline and back;
285
+ sign out in another tab and return; close the tab during a long action.
286
+ - In a production build, change a screen's inputs and see what it shows change,
287
+ with no render loops.
288
+
289
+ **Examples:**
290
+ - A success message appeared over the Save button and took the next tap.
291
+ - A list rendered every row of a large data set into the page.
292
+
293
+ **Outer limit:** tens of thousands of DOM nodes, typing lag, or megabytes or
294
+ hundreds of requests on a first screen.
295
+
296
+ ## State and workflow
297
+
298
+ **Good:** the same state twice gives the same result. Each fact has one
299
+ resolver. Each status names what clears it, and each gate has a case that must
300
+ fail it. Writes are conditional on the version read. A delete removes what it
301
+ created elsewhere. Readiness is checked directly.
302
+
303
+ **Check:** find every place that resolves the same fact. Check that what a
304
+ delete created outside its table is gone. When a rule's result breaks another
305
+ stated rule, stop and ask, and keep the failing case.
306
+
307
+ **Outer limit:** a step that fails because the one before said ready.
308
+
309
+ ## Production settings
310
+
311
+ **Good:** production fails closed. Development modes, `localhost`, fixed ports
312
+ and fixed secrets exist only in tests. Security headers are sent, email
313
+ ownership is confirmed where sign-up needs it, and tokens and CI jobs have the
314
+ least permission they need.
315
+
316
+ **Check:** read the running production server's mode and flags and a
317
+ production build's response headers. Sign up without confirming email and try
318
+ to sign in. Run the ecosystem's dependency audit; fix it at the dependency
319
+ ranges. Search fixtures for real names, hosts and emails. Name what ends each
320
+ token.
321
+
322
+ **Outer limit:** the first visitor can own the deployment, or it runs as
323
+ development.
324
+
325
+ ## AI features
326
+
327
+ **Good:** the assistant can see and do no more than its user. Volatile state
328
+ sits after the stable prompt prefix, so the model's prompt cache holds.
329
+
330
+ **Check:** sign in as the least-privileged user and list what the assistant can
331
+ read and change. Read cached and uncached input tokens on two turns of one
332
+ conversation.
333
+
334
+ **Outer limit:** most input tokens uncached on a long conversation.
335
+
336
+ ## How you work
337
+
338
+ - Reproduce and measure before changing anything; verify a reviewer's premise.
339
+ - Lint the touched files and confirm the lint reads them. After the gates pass,
340
+ read the code against this guidance and check each suppression's reason.
341
+ - A reviewer can read each changed function, and the commit, in one sitting.
342
+ - Raise an instruction that slows the work or hides failures; do not follow it
343
+ silently.
344
+ - Run the smallest check that answers the question, and do not fill a shared
345
+ machine with full runs. Wait without polling, and read counts and failures,
346
+ not whole logs.
347
+ - Before acting on a shared machine or tree, name the approval that allows it,
348
+ and stay inside the approved scope.
349
+ - At the end of a task, list what is uncommitted, and save or revert it.
350
+
351
+ **Outer limit:** gates green while review finds hundreds of defects, or
352
+ functions thousands of lines long.
@@ -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',
@@ -216,6 +216,7 @@ export async function runGit(cwd, args, options = {}) {
216
216
  ...options.env,
217
217
  }),
218
218
  maxBytes: options.maxBytes,
219
+ input: options.input,
219
220
  timeoutMs: options.timeoutMs,
220
221
  signal: options.signal,
221
222
  code: options.code || 'GIT_EXEC_FAILED',
@@ -29,11 +29,12 @@ 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`,
36
36
  ]),
37
+ '.agents/skills/genesis-project/references/sanity-checks.md',
37
38
  ]);
38
39
  const bootstrapPaths = new Set(GENESIS_BOOTSTRAP_PATHS);
39
40
  // Genesis creates .gitignore only to ignore its Cities; any other content is the project's.
@@ -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',
@@ -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
  }
@@ -414,6 +414,7 @@ async function readStackWithContractMode(projectRoot, {
414
414
  adoption: composedProse(components, 'adoption'),
415
415
  postChange: composedProse(components, 'postChange'),
416
416
  deslop: composedProse(components, 'deslop'),
417
+ uiPass: composedProse(components, 'uiPass'),
417
418
  };
418
419
  }
419
420
 
@@ -17,6 +17,29 @@ function validateRepository(repository) {
17
17
  }
18
18
  }
19
19
 
20
+ /** Read every listed blob through one `git cat-file --batch` process, in listing order. */
21
+ async function readBlobs(root, entries) {
22
+ if (entries.length === 0) return [];
23
+ const { stdout } = await runGit(root, ['cat-file', '--batch'], {
24
+ input: `${entries.map(({ object }) => object).join('\n')}\n`,
25
+ // Each blob is preceded by "<object> blob <size>\n" and followed by "\n".
26
+ maxBytes: entries.reduce((total, { size }) => total + size + 128, 0),
27
+ });
28
+ const blobs = [];
29
+ let offset = 0;
30
+ for (const { object, size } of entries) {
31
+ const headerEnd = stdout.indexOf(0x0a, offset);
32
+ const header = headerEnd === -1 ? '' : stdout.subarray(offset, headerEnd).toString('utf8');
33
+ const start = headerEnd + 1;
34
+ if (header !== `${object} blob ${size}` || stdout.length < start + size + 1) {
35
+ throw new GenesisError('TEMPLATE_SOURCE_INVALID', `Git did not return template blob ${object}.`);
36
+ }
37
+ blobs.push(stdout.subarray(start, start + size));
38
+ offset = start + size + 1;
39
+ }
40
+ return blobs;
41
+ }
42
+
20
43
  /**
21
44
  * Fetch a branch once and read ordinary blobs; never execute or check out remote hooks/filters.
22
45
  * The scratch repository is created below scratchRoot (the system temporary directory by default).
@@ -32,31 +55,43 @@ export async function readGitSnapshot({ repository, branch, scratchRoot = os.tmp
32
55
  await runGit(temporaryRoot, ['check-ref-format', `refs/heads/${branch}`]);
33
56
  await runGit(temporaryRoot, ['fetch', '--quiet', '--depth=1', '--no-tags', '--', repository, `refs/heads/${branch}`], { timeoutMs: 120_000 });
34
57
  const revision = await runGitText(temporaryRoot, ['rev-parse', 'FETCH_HEAD^{commit}']);
35
- const tree = await runGit(temporaryRoot, ['ls-tree', '-rz', '--full-tree', revision]);
36
- const files = [];
58
+ // The long listing carries each blob's size, so the limits hold before any content is read.
59
+ const tree = await runGit(temporaryRoot, ['ls-tree', '-rlz', '--full-tree', revision]);
60
+ const entries = [];
37
61
  let bytes = 0;
62
+ let fileCount = 0;
38
63
  for (const item of tree.stdout.toString('utf8').split('\0').filter(Boolean)) {
39
64
  const separator = item.indexOf('\t');
40
- const [mode, type, object] = item.slice(0, separator).split(' ');
65
+ const [mode, type, object, size] = item.slice(0, separator).split(/ +/u);
41
66
  const name = item.slice(separator + 1);
42
67
  if (!['100644', '100755', '120000'].includes(mode) || type !== 'blob' || path.isAbsolute(name)
43
68
  || name.includes('\\') || name.split('/').some((part) => !part || ['.', '..', '.git'].includes(part.toLowerCase()))) {
44
69
  throw new GenesisError('TEMPLATE_TREE_INVALID', `Template contains an unsupported path or file: ${name}.`);
45
70
  }
46
- const blob = await runGit(temporaryRoot, ['cat-file', 'blob', object]);
71
+ if (mode === '120000') {
72
+ // Only short Claude skill links are accepted; never read a large one.
73
+ if (Number(size) > 1024) throw new GenesisError('TEMPLATE_TREE_INVALID', `Template contains an unsupported link: ${name}.`);
74
+ } else {
75
+ bytes += Number(size);
76
+ fileCount += 1;
77
+ if (bytes > 64 * 1024 * 1024 || fileCount > 10_000) {
78
+ throw new GenesisError('TEMPLATE_TREE_TOO_LARGE', 'Template exceeds the source size limit.');
79
+ }
80
+ }
81
+ entries.push({ mode, object, name, size: Number(size) });
82
+ }
83
+ const blobs = await readBlobs(temporaryRoot, entries);
84
+ const files = [];
85
+ for (const [index, { mode, name }] of entries.entries()) {
47
86
  if (mode === '120000') {
48
87
  const target = claudeSkillLinkTarget(name);
49
- if (!target || !blob.stdout.equals(Buffer.from(target))) {
88
+ if (!target || !blobs[index].equals(Buffer.from(target))) {
50
89
  throw new GenesisError('TEMPLATE_TREE_INVALID', `Template contains an unsupported link: ${name}.`);
51
90
  }
52
91
  // Regenerate these through initialization from the canonical skill files.
53
92
  continue;
54
93
  }
55
- bytes += blob.stdout.length;
56
- if (bytes > 64 * 1024 * 1024 || files.length >= 10_000) {
57
- throw new GenesisError('TEMPLATE_TREE_TOO_LARGE', 'Template exceeds the source size limit.');
58
- }
59
- files.push({ path: name, contents: blob.stdout, mode: mode === '100755' ? 0o777 : 0o666 });
94
+ files.push({ path: name, contents: blobs[index], mode: mode === '100755' ? 0o777 : 0o666 });
60
95
  }
61
96
  return { repository, branch, revision, files };
62
97
  } finally { await rm(temporaryRoot, { recursive: true, force: true }); }
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
  }