genesis-compiler 1.10.10 → 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
@@ -363,16 +363,34 @@ 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.
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.
376
394
 
377
395
  The installed, versioned profile descriptions live in
378
396
  `profiles/engineering/*.md`. Straight Genesis users can inspect and select them
@@ -973,9 +991,15 @@ ordinary project operations stop until the migration is resolved.
973
991
  ## Development
974
992
 
975
993
  Run `npm ci` in this source checkout, then `npm test`. The test command runs the
976
- original test files with file concurrency fixed at one. The local first-party
977
- catalogue is a development-only dependency so catalogue and starter tests work
978
- 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.
979
1003
 
980
1004
  ## Public API
981
1005
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-compiler",
3
- "version": "1.10.10",
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": {
@@ -48,7 +48,7 @@
48
48
  "docs/parsers.md"
49
49
  ],
50
50
  "scripts": {
51
- "test": "node --test --test-concurrency=1 test/*.test.js"
51
+ "test": "node --test test/*.test.js"
52
52
  },
53
53
  "jskitRelease": {
54
54
  "versionFiles": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis",
3
- "version": "1.10.10",
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,9 +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
- - 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
+ - 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.
9
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.
10
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.
11
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.
12
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,11 +8,18 @@ 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.
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`.
16
23
 
17
24
  Do not edit any file. Return a concise report ordered by impact with exact file
18
25
  references, the evidence inspected, and the smallest sensible correction.
@@ -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,14 +164,17 @@ 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, 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.
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.
175
178
 
176
179
  An unconfigured result means the declared workspace or checks do not exist
177
180
  yet; it is not a failing check. Before reporting completion, compare the
@@ -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.
@@ -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',
@@ -34,6 +34,7 @@ export const GENESIS_BOOTSTRAP_PATHS = Object.freeze([
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.
@@ -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 }); }