enigma-cli 1.32.4 → 1.32.5
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/assets/memory/AGENTS.md +2 -1
- package/assets/memory/CLAUDE.md +2 -1
- package/assets/skills/anti-overengineering-policy/skill.json +1 -1
- package/assets/skills/anti-overengineering-review/skill.json +1 -1
- package/assets/skills/backend-policy/SKILL.md +2 -0
- package/assets/skills/backend-policy/skill.json +4 -4
- package/assets/skills/ciphera-style-policy/skill.json +1 -1
- package/assets/skills/code-review-policy/skill.json +1 -1
- package/assets/skills/core-engineering-policy/skill.json +1 -1
- package/assets/skills/database-expert/SKILL.md +13 -1
- package/assets/skills/database-expert/skill.json +4 -4
- package/assets/skills/debugging-policy/skill.json +1 -1
- package/assets/skills/dependency-policy/skill.json +1 -1
- package/assets/skills/email-policy/skill.json +1 -1
- package/assets/skills/frontend-design/skill.json +1 -1
- package/assets/skills/frontend-policy/SKILL.md +43 -1
- package/assets/skills/frontend-policy/skill.json +5 -5
- package/assets/skills/git-policy/skill.json +1 -1
- package/assets/skills/logo-sourcing-policy/skill.json +1 -1
- package/assets/skills/security-policy/skill.json +2 -2
- package/assets/skills/skill-creator/skill.json +1 -1
- package/assets/skills/task-completion-policy/skill.json +1 -1
- package/assets/skills/technical-writing-policy/skill.json +1 -1
- package/assets/skills/testing-policy/skill.json +1 -1
- package/assets/skills/validation-policy/SKILL.md +33 -2
- package/assets/skills/validation-policy/skill.json +4 -4
- package/bin/checksums.json +4 -4
- package/dist/guardrails.js +116 -0
- package/package.json +1 -1
package/assets/memory/AGENTS.md
CHANGED
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
Non-negotiable, language-agnostic defaults - apply them by default without being asked, using the stack's idiomatic tool. They restate the cores of validation-policy, backend-policy and frontend-policy so they hold even when a skill does not load.
|
|
38
38
|
|
|
39
39
|
- Validate EVERY external input (request body, query, params, event payload, form field, CLI arg, webhook/message) against an explicit schema before use - Zod (TS/JS), Pydantic (Python), the language's equivalent elsewhere. Never consume an unvalidated shape or leave it open-ended. When the input is a tagged/event union, validate the discriminant AND that specific variant's body, with the expected fields typed.
|
|
40
|
-
-
|
|
40
|
+
- Normalize before validating, on the client AND the server, from one shared normalizer: trim every string, lowercase the email, capitalize each word of a person's name, canonicalize a link or handle to one stored form. A check that cannot fail is not validation - never patch the value into validity and then check the patched value.
|
|
41
|
+
- Frontend forms: validate in real time against the same schema, on EVERY field that has a rule and not only the ones with a famous format, and use optimistic UI with rollback on failure for user-facing mutations.
|
|
41
42
|
- Cache reads on the client (localStorage/sessionStorage, or the data layer's cache) with a short TTL (~30s or more) to avoid redundant queries and survive rate limits; invalidate on write.
|
|
42
43
|
- Build reusable, composable components instead of duplicating UI - e.g. a single Input that renders a show/hide toggle when the type is password. Reuse before writing new.
|
|
43
44
|
- Never use the browser's native `alert`/`confirm`/`prompt` - use a dialog/modal component that matches the page design.
|
package/assets/memory/CLAUDE.md
CHANGED
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
Non-negotiable, language-agnostic defaults - apply them by default without being asked, using the stack's idiomatic tool. They restate the cores of validation-policy, backend-policy and frontend-policy so they hold even when a skill does not load.
|
|
38
38
|
|
|
39
39
|
- Validate EVERY external input (request body, query, params, event payload, form field, CLI arg, webhook/message) against an explicit schema before use - Zod (TS/JS), Pydantic (Python), the language's equivalent elsewhere. Never consume an unvalidated shape or leave it open-ended. When the input is a tagged/event union, validate the discriminant AND that specific variant's body, with the expected fields typed.
|
|
40
|
-
-
|
|
40
|
+
- Normalize before validating, on the client AND the server, from one shared normalizer: trim every string, lowercase the email, capitalize each word of a person's name, canonicalize a link or handle to one stored form. A check that cannot fail is not validation - never patch the value into validity and then check the patched value.
|
|
41
|
+
- Frontend forms: validate in real time against the same schema, on EVERY field that has a rule and not only the ones with a famous format, and use optimistic UI with rollback on failure for user-facing mutations.
|
|
41
42
|
- Cache reads on the client (localStorage/sessionStorage, or the data layer's cache) with a short TTL (~30s or more) to avoid redundant queries and survive rate limits; invalidate on write.
|
|
42
43
|
- Build reusable, composable components instead of duplicating UI - e.g. a single Input that renders a show/hide toggle when the type is password. Reuse before writing new.
|
|
43
44
|
- Never use the browser's native `alert`/`confirm`/`prompt` - use a dialog/modal component that matches the page design.
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "On-demand over-engineering review - diff review, whole-repo audit, and enigma: debt-marker ledger (tags delete/stdlib/native/yagni/shrink, line/dep scoring); lists cuts, applies nothing.",
|
|
6
6
|
"updated": "2026-06-16T11:24:30+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "f742a2be3f328b9ea1ff9a35a449177c2cbec35ad16e46f7054b7a873a2ab017"
|
|
9
9
|
}
|
|
@@ -52,6 +52,8 @@ Cache expensive or hot reads on the server to reduce database load, complementin
|
|
|
52
52
|
### When to cache
|
|
53
53
|
|
|
54
54
|
- Cache read-heavy, expensive-to-compute, or frequently requested data.
|
|
55
|
+
- Reach for the client cache first (frontend-policy): it removes the request instead of serving it faster, so it costs this service nothing. A server cache is for what the client cannot hold - an expensive computation shared across users, a payload too large or too sensitive to sit on a device, or a rate-limited upstream you are shielding.
|
|
56
|
+
- Support the client's revalidation instead of making it re-download: answer with an `ETag`/`Last-Modified` and honour `If-None-Match`/`If-Modified-Since` with a `304`, so an unchanged resource costs a header exchange and no body.
|
|
55
57
|
- Do not cache data that must always be strongly consistent unless invalidation is immediate and reliable.
|
|
56
58
|
- Never cache secrets or sensitive data without encryption and strict access control.
|
|
57
59
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-policy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Backend/API architecture: controller-service-repository layering, API and request optimization (batching, avoiding redundant calls, skipping no-op writes), server-side caching (Redis), and Zod boundary validation.",
|
|
6
|
-
"updated": "2026-07-
|
|
7
|
-
"cliVersion": "1.32.
|
|
8
|
-
"sha": "
|
|
6
|
+
"updated": "2026-07-31T16:34:48+02:00",
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
|
+
"sha": "768c2e088948ff97d6b897ce43854e53dfd156738e6a19548679b5327278433d"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Ciphera code style conventions (formatting, naming, imports incl. namespace imports for wide module surfaces, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
|
|
6
6
|
"updated": "2026-07-30T15:48:04+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "05dc812da459071110d96ee41c5201b1ee230471d635352415e10ef8b220c267"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Pre-delivery self-review gate, prioritized review dimensions, and change-quality criteria.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "3d3bbe0602d5bbb4afe37648fe3c2fa39376b1bcbac5d8c441f01fad1e866ed0"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Core engineering execution policy and harness orchestration (highest-authority rules).",
|
|
6
6
|
"updated": "2026-07-30T19:29:19+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "6a881d8589926fa7f48058314fd26d7042fd2ac82f1c87a6c11ffb54d1fda22b"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: database-expert
|
|
3
|
-
description: Senior database architecture - schema design, normalization and anti-duplication, query/index optimization, scalability (partitioning, sharding, replication), and RGPD/GDPR encryption of sensitive data. Use when designing, modifying, migrating, querying, or reviewing any database, schema, SQL, ORM model, or persistence layer.
|
|
3
|
+
description: Senior database architecture - engine selection (PostgreSQL is the default relational engine for anything deployed or multi-writer; SQLite only for local-first, embedded, single-writer stores), schema design, normalization and anti-duplication, query/index optimization, scalability (partitioning, sharding, replication), and RGPD/GDPR encryption of sensitive data. Use when designing, modifying, migrating, querying, or reviewing any database, schema, SQL, ORM model, or persistence layer, and when choosing the datastore for a new project's stack.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Database Expert Policy (Senior Data Architecture Standards)
|
|
@@ -26,6 +26,18 @@ description: Senior database architecture - schema design, normalization and ant
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
+
## Engine Selection (Default: PostgreSQL)
|
|
30
|
+
|
|
31
|
+
- For anything that will be deployed, grow, or be written to by more than one process, the default relational engine is **PostgreSQL**. Pick it without being asked; when something else is chosen, say in one line what constraint forced it.
|
|
32
|
+
- What makes it the default and not a preference: real write concurrency (MVCC, no database-wide writer lock), the types the rest of this policy assumes (native `uuid`, `jsonb`, arrays, enums, ranges, `timestamptz`), partial, expression and GIN indexes, generated columns, materialized views, declarative partitioning, logical replication and read replicas, and extensions that each remove a service from the stack (`pgvector` for embeddings, `pg_trgm` for fuzzy search, PostGIS for geo, `pg_cron` for schedules).
|
|
33
|
+
- SQLite is one file with one writer. It is the right default for a local-first or embedded store - a CLI's own state, a desktop or mobile app, an agent's local cache or index, a test fixture, an offline replica - and the wrong one for a web or API backend, anything running more than one instance, anything a background worker writes to, or anything with a managed-hosting story. Starting there and growing out of it is a migration with downtime, not a config change.
|
|
34
|
+
- MySQL/MariaDB only when the platform, the host or the team requires it. SQL Server or Oracle only where it is already the environment.
|
|
35
|
+
- Do not add a second datastore before PostgreSQL runs out. It handles queues (`SELECT ... FOR UPDATE SKIP LOCKED`), full-text search, vectors, JSON documents and counters well past early scale. Add Redis, a search engine or a vector database when a measured limit demands it, not as part of the initial stack.
|
|
36
|
+
- Serverless and edge runtimes still get PostgreSQL: the problem there is connection count, not the engine, so put a pooler in front (PgBouncer, Prisma Accelerate, the provider's pooled endpoint) instead of switching to a file database.
|
|
37
|
+
- Wire it the same way every time: Prisma as the ORM for TypeScript, migrations committed to version control, the connection string from the environment and never in the repo, and pooling configured before the first load test rather than after the first outage.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
29
41
|
## Identifier Policy (Non-Negotiable)
|
|
30
42
|
|
|
31
43
|
- Every primary key and every externally exposed identifier MUST be a UUID. NEVER use auto-increment, SERIAL, BIGSERIAL, IDENTITY, AUTO_INCREMENT, or any incrementing integer/sequence as an entity ID - no exceptions, in any datastore.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "database-expert",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
|
-
"description": "Senior database architecture policy: query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
|
|
5
|
+
"description": "Senior database architecture policy: engine selection (PostgreSQL by default, SQLite only for local-first/embedded stores), query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
|
|
6
6
|
"updated": "2026-06-03T14:19:50+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
8
|
-
"sha": "
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
|
+
"sha": "b21ae85a015305106f787be2eb0a022661b2e984bc42c6bc66703954256e3c53"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Reproduce-isolate-fix debugging methodology with root-cause discipline and regression verification.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "14b0064c8b33a0dc85e51464b05005cf5801c756b1101789a6924b9548420f6b"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Dependency and supply-chain security: lockfiles and reproducible installs, version pinning, vulnerability auditing, vetting/minimizing packages, vendoring, and SBOM/provenance.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Transactional email: React Email templates instead of hand-written HTML tables, server-side rendering, one send module behind the provider SDK, plain-text alternatives, idempotent background sending, link safety, and deliverability (SPF/DKIM/DMARC, bounce suppression, unsubscribe).",
|
|
6
6
|
"updated": "2026-07-30T19:29:19+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "c9724fdbcdbeab99573be3fd44d4cdd97c2a394d99f3c4395f118f17356b00ed"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one.",
|
|
6
6
|
"updated": "2026-07-29T01:18:36+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "9e30ee7d8a1a1e8c6e7f4e043857cd01841c68a427752e45bc0cad9ec5cfa279"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frontend-policy
|
|
3
|
-
description: Frontend architecture - reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals the current state - form saves, toggles, filters, reorders - not just saves; dirty means the values DIFFER from the loaded snapshot, not that the user touched the field, so a value edited and put back leaves Save disabled), client-side caching (localStorage/sessionStorage to avoid redundant server calls and survive rate limits), instant first paint (render the shell immediately, load data async via the API, show skeletons - never block render on data), perceived performance and responsiveness (instant interaction feedback, prefetch on intent, debounce/throttle, cancel stale requests, avoid request waterfalls, lazy-load heavy widgets), large-list rendering (virtualized infinite scroll vs pagination, skeletons, progressive/parallel loading, short-TTL caching), optimistic UI with rollback, visual restraint (never a card inside a card, borders only where they carry information, spacing and background tone before chrome), icon actions (repeated row/card actions like copy, edit, rename, remove, download, refresh are icon-only buttons carrying aria-label plus title, never a text label), responsive/adaptive layout (fluid units, breakpoints, no overlap or horizontal overflow, viewport meta, touch targets), AI chat/assistant/agent interfaces (use Vercel's AI Elements registry for message threads, streaming, reasoning and tool-call panels, prompt inputs - never hand-roll chat UI in React), and periodic React code-health audits (react-doctor). Use when building or changing UI components, client state, forms/save flows, data fetching/caching, lists that show lots of data, loading states, dashboards/panels, layout/responsiveness, making the UI feel fast, building a chat/AI/agent/LLM interface, or any frontend structure.
|
|
3
|
+
description: Frontend architecture - reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals the current state - form saves, toggles, filters, reorders - not just saves; dirty means the values DIFFER from the loaded snapshot, not that the user touched the field, so a value edited and put back leaves Save disabled), client-side caching (localStorage/sessionStorage to avoid redundant server calls and survive rate limits), instant first paint (render the shell immediately, load data async via the API, show skeletons - never block render on data), perceived performance and responsiveness (instant interaction feedback, prefetch on intent, debounce/throttle, cancel stale requests, avoid request waterfalls, lazy-load heavy widgets), large-list rendering (virtualized infinite scroll vs pagination, skeletons, progressive/parallel loading, short-TTL caching), optimistic UI with rollback, visual restraint (never a card inside a card, borders only where they carry information, spacing and background tone before chrome), icon actions (repeated row/card actions like copy, edit, rename, remove, download, refresh are icon-only buttons carrying aria-label plus title, never a text label), responsive/adaptive layout (fluid units, breakpoints, no overlap or horizontal overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode/spellcheck per field kind, set once in the shared Input, normalized on blur rather than on every keystroke, with an inline error on every field that has a rule), AI chat/assistant/agent interfaces (use Vercel's AI Elements registry for message threads, streaming, reasoning and tool-call panels, prompt inputs - never hand-roll chat UI in React), and periodic React code-health audits (react-doctor). Use when building or changing UI components, client state, forms/save flows, data fetching/caching, lists that show lots of data, loading states, dashboards/panels, layout/responsiveness, making the UI feel fast, building a chat/AI/agent/LLM interface, or any frontend structure.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Frontend Architecture Policy
|
|
@@ -232,6 +232,28 @@ When the user edits a value that must be unique within a set the client already
|
|
|
232
232
|
|
|
233
233
|
---
|
|
234
234
|
|
|
235
|
+
## Every Field Declares Its Keyboard And Its Casing
|
|
236
|
+
|
|
237
|
+
A text input is not generic. What it holds decides the keyboard a phone opens, whether the first letter arrives capitalized, and whether autocorrect rewrites it. Left unset, the mobile defaults produce "juan perez" in a name field and a spell-checked handle in the next one.
|
|
238
|
+
|
|
239
|
+
- Person name (full name, first, last): `autocapitalize="words"`, the matching `autocomplete` token (`name`, `given-name`, `family-name`), `spellcheck="false"`, `autocorrect="off"`. Phone keyboards capitalize SENTENCES by default, which capitalizes only the first word of the name.
|
|
240
|
+
- Email: `type="email"`, `autocomplete="email"`, `inputmode="email"`, `autocapitalize="none"`, `spellcheck="false"`.
|
|
241
|
+
- Username, handle, slug, coupon, licence key: `autocapitalize="none"`, `autocorrect="off"`, `spellcheck="false"`.
|
|
242
|
+
- URL or profile link: `inputmode="url"`, `autocapitalize="none"`, `autocorrect="off"`.
|
|
243
|
+
- Phone: `type="tel"`, `inputmode="tel"`, `autocomplete="tel"`. Numeric code: `inputmode="numeric"`, `autocomplete="one-time-code"`.
|
|
244
|
+
- Free prose (bio, message, description): `autocapitalize="sentences"` with spellcheck left on.
|
|
245
|
+
- Set `enterkeyhint` where the key does something specific (`search`, `send`, `done`), and mark the search field `type="search"`.
|
|
246
|
+
|
|
247
|
+
Where this lives and when it runs:
|
|
248
|
+
|
|
249
|
+
- Put the whole matrix in the shared Input/TextField component, chosen by one prop (`kind="name" | "email" | "handle" | ...`), so a new form gets it by construction. A form that spells out attributes per field will forget them; that is how the defect ships.
|
|
250
|
+
- The attribute only shapes what the keyboard offers. Normalize the value as well - trim, collapse inner spaces, capitalize each word of a name, lowercase an email - using the shared normalizer from validation-policy, and let the server apply the same one.
|
|
251
|
+
- Normalize on BLUR and before save, never on every keystroke: rewriting the value under the caret moves the cursor and breaks IME composition mid-word. While the field has focus, show what the user typed; on blur, show what will be stored.
|
|
252
|
+
- Validate the normalized value, so a trailing space the user has not finished typing never renders as an error.
|
|
253
|
+
- Show the error inline as the user types, from the first blur onward, on EVERY field that has a rule, and keep Save blocked while any field is invalid. Validating the email and leaving the link, the phone and the handle next to it silent is the usual half-built form.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
235
257
|
## Sign-In, Sign-Up and Recovery Screens
|
|
236
258
|
|
|
237
259
|
Auth is the first screen a user meets and the one most often shipped half-built. Treat the four screens as one flow: sign in, sign up, forgot password, set a new password. The server-side rules (token lifetime, rate limits, what an answer may reveal) are owned by security-policy; what follows is the UI half.
|
|
@@ -278,6 +300,26 @@ Cache on the client to avoid redundant server round-trips and to keep the app us
|
|
|
278
300
|
- Prefer stale-while-revalidate for non-critical data: serve cached, refresh in the background.
|
|
279
301
|
- Never serve stale data for security-, money-, or correctness-critical reads.
|
|
280
302
|
|
|
303
|
+
### Show the last snapshot, then patch only what changed
|
|
304
|
+
|
|
305
|
+
The strongest version of stale-while-revalidate: persist the last response and render it on entry, so a returning user sees the screen already populated while the real request is still in flight. It is what makes a view backed by something slow (a NAS, a device on the LAN, a third-party API, anything you do not control) feel loaded instantly, because between two visits almost nothing usually changed.
|
|
306
|
+
|
|
307
|
+
- On mount, render from the persisted snapshot and fire the request at the same time. No spinner over content you can already show; a small "refreshing" marker is enough. Skeletons are for the first ever load, when there is no snapshot.
|
|
308
|
+
- When the response lands, RECONCILE, do not replace. Diff against what is on screen and apply only the differences: rows added, rows removed, fields whose value actually changed. Wholesale replacement is what produces the flash, the scroll jump, and the lost selection, and it is exactly what the snapshot was meant to avoid.
|
|
309
|
+
- Keep identity stable. Key rows by their real id (never the array index), and reuse the existing object for an unchanged row instead of a fresh one, so the framework re-renders the rows that changed and nothing else. Preserve scroll position, selection, expanded rows, in-progress edits, and focus across the refresh.
|
|
310
|
+
- If the whole response is equal to the snapshot, do nothing at all: no state write, no re-render. This is No-Op Detection applied to reads, and it is the common case.
|
|
311
|
+
- Detect "nothing changed" as cheaply as the backend allows: an `ETag` with `If-None-Match` (a `304` costs you a header exchange and no body), a `Last-Modified`/`updatedAt` cursor, or a content hash of the payload. Falling back to comparing the parsed objects is fine for small payloads, and hashing is fine for large ones - just do not deep-compare a huge tree on every poll.
|
|
312
|
+
- Stamp the snapshot with the time it was taken and show it ("updated 2 minutes ago"). A stale number no one can date is worse than a spinner.
|
|
313
|
+
- Never let the snapshot outlive its usefulness: version the stored shape (drop it when the app's schema changes), give it a TTL, cap what you store, and clear it on sign-out. localStorage is small, synchronous, and shared with every script on the page.
|
|
314
|
+
- Sensitive or fast-moving data does not get this treatment: money, permissions, live status, anything that would mislead if it were a minute old. Showing an old value is a correctness decision, not just a UX one.
|
|
315
|
+
|
|
316
|
+
### Caching is not free
|
|
317
|
+
|
|
318
|
+
- Every cache is a second copy of the truth, and the cost is invalidation, staleness bugs, and the memory or storage it occupies. Add one when there is a measured round-trip to save, not by default.
|
|
319
|
+
- Prefer the client cache: it removes the request entirely, so it costs the server nothing and scales with the number of users rather than against it. A server cache (Redis, see backend-policy) is for what the client cannot hold - expensive shared computations, data too large or too sensitive to sit on a device - and it is one more thing to size, evict, and invalidate.
|
|
320
|
+
- Cache the response, not the render. Storing derived UI state means re-deriving it on every schema change and getting it wrong when the derivation does.
|
|
321
|
+
- Coalesce and throttle rather than cache harder: one in-flight request per resource, no refetch on every focus event, and no polling loop that runs while the tab is hidden.
|
|
322
|
+
|
|
281
323
|
---
|
|
282
324
|
|
|
283
325
|
## Instant First Paint (Shell First, Data Async)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontend-policy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
|
-
"description": "Frontend architecture: reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals current state, not just form saves; dirty means different from the loaded snapshot, not touched), instant first paint (render the shell, load data async, skeletons), perceived performance (prefetch on intent, debounce/throttle, cancel stale requests, avoid waterfalls, lazy widgets), large-list rendering (infinite scroll/pagination, virtualization, skeletons, progressive loading), optimistic UI with rollback, visual restraint (one card level, spacing before borders, one elevation scale), icon actions (repeated row/card actions are icon-only buttons with aria-label plus title, not text labels), responsive/adaptive layout (fluid units, breakpoints, no overlap/overflow, viewport meta, touch targets), variable-length text (min-width:0 in flex/grid, wrap vs truncate, long unbroken strings, worst-case content checks), and AI chat/agent interfaces via Vercel's AI Elements registry instead of hand-rolled message threads.",
|
|
6
|
-
"updated": "2026-07-31T16:
|
|
7
|
-
"cliVersion": "1.32.
|
|
8
|
-
"sha": "
|
|
5
|
+
"description": "Frontend architecture: reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals current state, not just form saves; dirty means different from the loaded snapshot, not touched), instant first paint (render the shell, load data async, skeletons), perceived performance (prefetch on intent, debounce/throttle, cancel stale requests, avoid waterfalls, lazy widgets), large-list rendering (infinite scroll/pagination, virtualization, skeletons, progressive loading), optimistic UI with rollback, visual restraint (one card level, spacing before borders, one elevation scale), icon actions (repeated row/card actions are icon-only buttons with aria-label plus title, not text labels), responsive/adaptive layout (fluid units, breakpoints, no overlap/overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode per field kind, set once in the shared Input, normalized on blur), variable-length text (min-width:0 in flex/grid, wrap vs truncate, long unbroken strings, worst-case content checks), and AI chat/agent interfaces via Vercel's AI Elements registry instead of hand-rolled message threads.",
|
|
6
|
+
"updated": "2026-07-31T16:34:48+02:00",
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
|
+
"sha": "79816cdc63a7fd64336b93296aada3520bab26b38a7f65600f5ca27745de3a65"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Git & contribution policy (senior engineering standards).",
|
|
6
6
|
"updated": "2026-07-16T22:44:02+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "e6dfbc33884000d9d25841bd9c5a84d6558ffd374882cb7b34451eb2cebc2161"
|
|
9
9
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "security-policy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Application and AI-agent security: secrets, authn/authz (least privilege), OWASP Top 10, transport/crypto baseline, secure logging, and agent/MCP/tool-use safety.",
|
|
6
6
|
"updated": "2026-07-31T16:26:45+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "e5e7ac2791919e4b8483115ee8b83800c5ebdf4eebde019650d974b400417c55"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Create new skills, modify and improve existing skills, and measure skill performance with evals and benchmarks.",
|
|
6
6
|
"updated": "2026-07-29T01:18:36+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "13d24c217bdb6fe83fe16835d8f5c3d397a9f3338f16876c61ef96e97f34c90a"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Exhaustive completion discipline for long/multi-item tasks - inventory, coverage ledger, verified done.",
|
|
6
6
|
"updated": "2026-07-30T20:10:24+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "2f82288d83dca3676d6bc49955c89477cc298f77fa56b4e414cd95ff858fa261"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Concise, realistic technical copy - UI microcopy, descriptions, hints, empty/error states, and README/doc prose that informs without over-explaining or restating the obvious, and never uses a typographic dash.",
|
|
6
6
|
"updated": "2026-07-28T20:30:23+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "a4b792103eb1f9dad93b9d70ea79dc18fe9cbbc318facf5adb47ae5907d842f9"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Test strategy, coverage gates, deterministic tests, mocking discipline, regression-first bug fixing, and test-suite organization (layout by type/domain, mirrored paths, file naming, fixture/helper placement).",
|
|
6
6
|
"updated": "2026-06-16T17:11:49+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
8
|
"sha": "3bdf591057b760f674fb2b1425f63acb426cda2c4f042e1a74c5a5d3807df664"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: validation-policy
|
|
3
|
-
description: Strict frontend + backend schema validation (Zod or equivalent), schema consistency between client and server, and safe client-facing error handling. Use when handling any external input - forms, API request bodies, query params, CLI args, file parsing, or third-party payloads.
|
|
3
|
+
description: Strict frontend + backend schema validation (Zod or equivalent), normalization before validation (one shared normalizer on both sides - trim, lowercase the email, capitalize each word of a name, canonicalize a profile link or handle) and the rule that a check which cannot fail is not validation, schema consistency between client and server, and safe client-facing error handling. Use when handling any external input - forms, API request bodies, query params, CLI args, file parsing, or third-party payloads.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Validation & Error Handling Policy
|
|
@@ -33,6 +33,7 @@ description: Strict frontend + backend schema validation (Zod or equivalent), sc
|
|
|
33
33
|
- Use schema-driven validation (e.g. Zod or equivalent).
|
|
34
34
|
- Validation must prevent invalid state before submission.
|
|
35
35
|
- UI must reflect validation state immediately and clearly.
|
|
36
|
+
- **Every field, not only the famous formats.** Email and password get validated because their rules are obvious; the fields next to them (a profile link, a phone, a job title, a website) are the ones shipped open. Give each field a rule and show its error inline: a format check where there is a format, a max length everywhere, and an "empty after trimming" check where the value is required. A field whose only rule is `z.string()` is an unvalidated field.
|
|
36
37
|
- Validate cross-record constraints (uniqueness, availability, "already in use") in real time too, not just per-field type/format. When the client already holds the relevant set (the list of accounts, profiles, names, slugs it just rendered), check the input against that loaded data on every change and block submission on a conflict - do not defer the duplicate check to the server round-trip (this gives instant feedback and spares a redundant request and its DB query). The server still re-validates as the authority (client checks can be stale), but the user must see the conflict as they type. Mirror the server's exact rule (same pattern, case-folding, reserved values, and scope - e.g. unique per parent vs. globally) so the two never disagree; exclude the record's own current value when editing so renaming to the same name is not flagged.
|
|
37
38
|
|
|
38
39
|
### Backend / API Validation (Mandatory)
|
|
@@ -54,10 +55,40 @@ description: Strict frontend + backend schema validation (Zod or equivalent), sc
|
|
|
54
55
|
|
|
55
56
|
---
|
|
56
57
|
|
|
58
|
+
## Normalize Before You Validate (Client And Server)
|
|
59
|
+
|
|
60
|
+
Input arrives shaped by whoever typed it: a leading space from a paste, a name in lowercase from a phone keyboard, an address in mixed case, a profile link carrying tracking parameters. Normalize first, validate the normalized value, store that same value. Both sides do it - the client so the user sees what will be stored, the server because a request does not have to come from your form.
|
|
61
|
+
|
|
62
|
+
- Keep the normalizers in ONE module that the client and the server both import, next to the schema they belong to. Two copies drift, and the day they disagree the server rejects what the form accepted.
|
|
63
|
+
- Normalize INSIDE the schema wherever the validator supports it, so no caller can forget. Zod: `z.string().trim().toLowerCase().pipe(z.email())`. Order matters: `z.email().trim()` validates before trimming and rejects a pasted `" a@b.com"`. Yup: `.trim().lowercase().email()`. Pydantic: a `field_validator(mode="before")`.
|
|
64
|
+
- Default for every string field: trim both ends, collapse runs of inner whitespace, strip control and zero-width characters, and normalize Unicode to NFC so a composed and a decomposed accent are the same value.
|
|
65
|
+
- The server normalizes again, always. A client that skipped it, an API client, a script, and a replayed request all reach the same handler.
|
|
66
|
+
|
|
67
|
+
Per field kind (defaults - override only with a reason):
|
|
68
|
+
|
|
69
|
+
| Field | Normalizes to |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| Email | trim, lowercase, then validate. Store it lowercased so lookups and uniqueness never miss. |
|
|
72
|
+
| Person name (full name, first, last) | trim, collapse inner spaces, uppercase the first letter of every word - and ONLY that letter, so `McDonald`, `O'Brien`, `van der Berg` and `Jean-Luc` survive. Split on spaces, hyphens and apostrophes. |
|
|
73
|
+
| Username, handle, slug | trim, drop a leading `@`, lowercase when the identifier is case-insensitive, then check the allowed character set. |
|
|
74
|
+
| Profile link (LinkedIn, GitHub, X, Instagram) | accept BOTH a full URL and a bare handle, canonicalize to one stored form, check the host is the expected domain, and drop query and tracking parameters. |
|
|
75
|
+
| URL | trim, add the scheme when missing, lowercase the host, drop a trailing slash. |
|
|
76
|
+
| Phone | strip spaces, dots, dashes and parentheses, keep the leading `+`, store E.164. |
|
|
77
|
+
| Number, date, money | parse into the canonical type at the boundary; never store the localized string. |
|
|
78
|
+
|
|
79
|
+
### A check that cannot fail is not validation
|
|
80
|
+
|
|
81
|
+
- Never patch the value into validity and then check the patched value. `z.url().safeParse(v.startsWith("http") ? v : "https://" + v)` accepts `asdf`, `pepe`, and every other single token, because `https://asdf` is a syntactically valid URL. The field looks validated, has an error slot, and rejects nothing.
|
|
82
|
+
- Canonicalizing and validating are two steps, in that order. Canonicalize (add the scheme, strip the `@`), then apply a check the canonical value can still fail: the host contains a dot, the host is the expected domain, the path has the expected shape.
|
|
83
|
+
- Before calling a field done, type three wrong values into it and confirm each is rejected. A validator nobody has watched fail is unverified.
|
|
84
|
+
- The same applies to a permissive fallback: an `.optional()` that swallows `""`, a `catch()` that returns a default, or a `refine` that returns `true` on anything it cannot parse.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
57
88
|
## Validation Standards
|
|
58
89
|
|
|
59
90
|
- Validate type, shape, range, format, and required/optional status.
|
|
60
|
-
- Normalize input (trim, case-fold, canonicalize) before validating equality or storing.
|
|
91
|
+
- Normalize input (trim, case-fold, canonicalize) before validating equality or storing, per the section above.
|
|
61
92
|
- Enforce explicit allowlists over denylists for constrained values.
|
|
62
93
|
- Set explicit limits on size, length, and array cardinality to prevent abuse.
|
|
63
94
|
- Fail closed: unknown or unexpected fields are rejected, not silently ignored, on sensitive endpoints.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "validation-policy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
|
-
"description": "Strict frontend + backend schema validation, schema consistency, and safe client-facing error handling.",
|
|
5
|
+
"description": "Strict frontend + backend schema validation, normalization before validation (shared normalizers: trim, lowercase email, capitalize names, canonicalize links and handles), schema consistency, and safe client-facing error handling.",
|
|
6
6
|
"updated": "2026-07-22T01:41:06+02:00",
|
|
7
|
-
"cliVersion": "1.32.
|
|
8
|
-
"sha": "
|
|
7
|
+
"cliVersion": "1.32.5",
|
|
8
|
+
"sha": "84bb43cc570aa927ab5c0311c17242b4158287961aa04af111cf83eb4eec6ce4"
|
|
9
9
|
}
|
package/bin/checksums.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enigma-darwin-arm64": "
|
|
3
|
-
"enigma-linux-arm64": "
|
|
4
|
-
"enigma-linux-x64": "
|
|
5
|
-
"enigma-win32-x64.exe": "
|
|
2
|
+
"enigma-darwin-arm64": "17d7ee22f86d00a6dc03f48ecb7cd64400f941a54008ee6a058807f70b43bbe1",
|
|
3
|
+
"enigma-linux-arm64": "a6de59589f327143d9180eabf76320685e127092fbc60a89f37d08ee880fe44e",
|
|
4
|
+
"enigma-linux-x64": "bb23f85be6f2567ffaae0ebd852472519313fc607fe4ed7b3a10b23497759a08",
|
|
5
|
+
"enigma-win32-x64.exe": "e43f5cafec4ab27c16e6be74e5f802437809042077d94debfd32d371a5202c57"
|
|
6
6
|
}
|
package/dist/guardrails.js
CHANGED
|
@@ -34,6 +34,28 @@ var BUILTIN_RULES = [
|
|
|
34
34
|
severity: "warn",
|
|
35
35
|
skill: "database-expert"
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
id: "db-sqlite-app-datastore",
|
|
39
|
+
label: "PostgreSQL as the default relational engine",
|
|
40
|
+
// Basename glob, so it covers schema.prisma at any depth (a `**/*.prisma` glob would
|
|
41
|
+
// miss a root-level one - the documented gotcha).
|
|
42
|
+
files: ["*.prisma"],
|
|
43
|
+
scope: "file",
|
|
44
|
+
// A Prisma DATASOURCE on sqlite: the app's own database, declared by the ORM this policy
|
|
45
|
+
// already defaults to. `provider` also appears in generator blocks, but only a datasource
|
|
46
|
+
// ever names sqlite, so the value alone is the discriminator.
|
|
47
|
+
// WHY THIS SHAPE AND NOT A DEPENDENCY CHECK: a Prisma project declares NO sqlite driver in
|
|
48
|
+
// package.json (Prisma bundles its own), so the package.json signature that would look
|
|
49
|
+
// natural here misses the exact stack an agent scaffolds. Measured over the corpus: 14
|
|
50
|
+
// prisma schemas, every datasource already postgresql, and 0 package.json files declaring
|
|
51
|
+
// a sqlite driver at all - so this rule is a scaffolding guard with no legacy backlog to
|
|
52
|
+
// flag, which is also why it is the only slice of the convention worth gating.
|
|
53
|
+
pattern: `provider\\s*=\\s*["']sqlite["']`,
|
|
54
|
+
absent: "enigma:allow-sqlite",
|
|
55
|
+
message: "SQLite as the application datastore. SQLite is one file with one writer: it is right for a local-first or embedded store (a CLI's own state, a desktop or mobile app, a local cache or index, a test fixture) and wrong for anything deployed, replicated, or written to by a background worker - and moving off it later is a migration with downtime, not a config change. Default to PostgreSQL: real write concurrency, native uuid/jsonb/arrays/enums/timestamptz, partial and GIN indexes, partitioning and read replicas, plus pgvector, pg_trgm and PostGIS instead of a second service. On serverless put a pooler in front (PgBouncer, Prisma Accelerate, the provider's pooled endpoint); the constraint there is connection count, not the engine. If this datastore is deliberately local-first or embedded, mark it with an `enigma:allow-sqlite` note (database-expert).",
|
|
56
|
+
severity: "block",
|
|
57
|
+
skill: "database-expert"
|
|
58
|
+
},
|
|
37
59
|
{
|
|
38
60
|
id: "be-validate-input-ts",
|
|
39
61
|
label: "Validate request input (TypeScript)",
|
|
@@ -66,6 +88,53 @@ var BUILTIN_RULES = [
|
|
|
66
88
|
severity: "warn",
|
|
67
89
|
skill: "validation-policy"
|
|
68
90
|
},
|
|
91
|
+
{
|
|
92
|
+
id: "val-email-normalize",
|
|
93
|
+
label: "Email is normalized before it is validated",
|
|
94
|
+
files: ["*.ts", "*.tsx", "*.js", "*.jsx", "*.mts", "*.cts", "*.mjs", "*.vue", "*.svelte"],
|
|
95
|
+
excludeFiles: [
|
|
96
|
+
"*.test.*",
|
|
97
|
+
"*.spec.*",
|
|
98
|
+
"**/tests/**",
|
|
99
|
+
"**/__tests__/**",
|
|
100
|
+
"*.d.ts",
|
|
101
|
+
"*.min.js",
|
|
102
|
+
"**/dist/**",
|
|
103
|
+
"**/build/**",
|
|
104
|
+
"**/_build/**",
|
|
105
|
+
"**/node_modules/**",
|
|
106
|
+
"**/vendor/**",
|
|
107
|
+
"dist/**",
|
|
108
|
+
"build/**",
|
|
109
|
+
"_build/**",
|
|
110
|
+
"node_modules/**",
|
|
111
|
+
"vendor/**"
|
|
112
|
+
],
|
|
113
|
+
scope: "file",
|
|
114
|
+
// An email schema declared with no normalization anywhere in the file. The three forms
|
|
115
|
+
// cover the ecosystem: `.string()...email(` (zod 3, yup, joi), `z.email(` (zod 4) and
|
|
116
|
+
// `v.email(` (valibot). Measured over ~2500 files of real product repos: 10 files declare
|
|
117
|
+
// an email schema, 7 normalize nothing - all 7 genuine (invitation forms, backend request
|
|
118
|
+
// schemas, an auth route), 0 false positives. `absent` keys on CASE-FOLDING only, not on
|
|
119
|
+
// trimming: an email schema that trims but keeps the case still lets "A@x.com" and
|
|
120
|
+
// "a@x.com" become two accounts, which is the defect. It stays file-scoped (the engine has
|
|
121
|
+
// no line-scoped absent), so a file that lowercases anything at all clears - a deliberate
|
|
122
|
+
// false negative, precision over recall.
|
|
123
|
+
pattern: "\\.string\\(\\)[^\\n]*\\.email\\(|\\bz\\.email\\(|\\bv\\.email\\(",
|
|
124
|
+
absent: "toLowerCase|lowercase\\(|normalizeEmail|enigma:allow-raw-email",
|
|
125
|
+
message: 'Email schema with no normalization. An address pasted with a leading space or typed in mixed case must reduce to ONE stored value, or the lookup misses, the uniqueness check passes, and the user ends up with a second account. Normalize inside the schema so no caller can forget it: Zod `z.string().trim().toLowerCase().pipe(z.email())` - the order matters, `z.email().trim()` validates first and rejects a pasted " a@b.com" - Yup `.trim().lowercase().email()`, Pydantic a `field_validator(mode="before")`. Use the same schema on the client and the server. If this address must keep its case, mark it with an `enigma:allow-raw-email` note (validation-policy).',
|
|
126
|
+
severity: "block",
|
|
127
|
+
skill: "validation-policy"
|
|
128
|
+
},
|
|
129
|
+
// NOTE: there is deliberately no "URL check that patches the value first" rule, though that
|
|
130
|
+
// exact shape is what makes a link field accept anything: `z.url().safeParse(v.startsWith("http")
|
|
131
|
+
// ? v : "https://" + v)` passes for `asdf`, because `https://asdf` IS a syntactically valid URL.
|
|
132
|
+
// The signature (a scheme interpolated into the string being parsed) does not survive
|
|
133
|
+
// measurement: 7 hits across the corpus and 5 are legitimate canonicalization for DISPLAY or
|
|
134
|
+
// parsing (build a URL to read its hostname), which is the same code shape with none of the
|
|
135
|
+
// defect. Telling them apart needs to know whether the result is a VERDICT or a value, which
|
|
136
|
+
// is not in the line. It stays in validation-policy ("A check that cannot fail is not
|
|
137
|
+
// validation") together with the URL-or-handle canonicalization rule.
|
|
69
138
|
// NOTE: no Go/Rust input-validation rule. Go's manual validation (`if in.X == ""`) is
|
|
70
139
|
// idiomatic and has no detectable signature, and Rust's serde typed deserialization already
|
|
71
140
|
// enforces shape - a rule for either would false-positive. The generic "validate every input"
|
|
@@ -85,6 +154,47 @@ var BUILTIN_RULES = [
|
|
|
85
154
|
severity: "warn",
|
|
86
155
|
skill: "frontend-policy"
|
|
87
156
|
},
|
|
157
|
+
{
|
|
158
|
+
id: "fe-name-input-capitalize",
|
|
159
|
+
label: "A person-name field capitalizes its words",
|
|
160
|
+
files: ["*.tsx", "*.jsx", "*.vue", "*.svelte", "*.astro", "*.html", "*.htm"],
|
|
161
|
+
excludeFiles: [
|
|
162
|
+
"*.test.*",
|
|
163
|
+
"*.spec.*",
|
|
164
|
+
"**/tests/**",
|
|
165
|
+
"**/__tests__/**",
|
|
166
|
+
"**/stories/**",
|
|
167
|
+
"*.stories.*",
|
|
168
|
+
"*.min.js",
|
|
169
|
+
"**/dist/**",
|
|
170
|
+
"**/build/**",
|
|
171
|
+
"**/_build/**",
|
|
172
|
+
"**/node_modules/**",
|
|
173
|
+
"**/vendor/**",
|
|
174
|
+
"dist/**",
|
|
175
|
+
"build/**",
|
|
176
|
+
"_build/**",
|
|
177
|
+
"node_modules/**",
|
|
178
|
+
"vendor/**"
|
|
179
|
+
],
|
|
180
|
+
scope: "file",
|
|
181
|
+
// A field that holds a PERSON's name, with no autocapitalize anywhere in the file. On a
|
|
182
|
+
// phone the keyboard defaults to sentence capitalization, so the user types "juan perez"
|
|
183
|
+
// and that is what gets stored; `autocapitalize="words"` is the one attribute that fixes
|
|
184
|
+
// it, and it is inert on a desktop keyboard.
|
|
185
|
+
// PRECISION comes from the token set, measured over ~2500 files of real product repos.
|
|
186
|
+
// Only names that can ONLY be a person's are matched: the HTML autofill tokens (which the
|
|
187
|
+
// spec defines as the person's name) and first/last/surname/apellido field names. `name`,
|
|
188
|
+
// `nombre` and `fullname` are deliberately EXCLUDED - every one of their hits in the
|
|
189
|
+
// corpus was an entity name (a project, a team, a token, a webhook), which must not be
|
|
190
|
+
// title-cased. With that set: 8 findings, all genuine person-name inputs, 0 false
|
|
191
|
+
// positives; the one file that already sets autocapitalize is correctly cleared.
|
|
192
|
+
pattern: `autocomplete=\\{?["'](?:name|given-name|family-name|additional-name|honorific-prefix)["']|(?:\\bname|\\bid|\\bfor|formControlName)=\\{?["'](?:first[-_]?name|last[-_]?name|given[-_]?name|family[-_]?name|surname|apellidos?)["']`,
|
|
193
|
+
absent: "autocapitalize|enigma:allow-no-capitalize",
|
|
194
|
+
message: 'Person-name field with no capitalization rule. Phone keyboards capitalize SENTENCES, so a name typed on mobile is stored as "juan perez": add `autocapitalize="words"` (plus `spellcheck="false"` and `autocorrect="off"`, and the matching `autocomplete` token). The attribute only covers typing, so normalize the value too - trim, collapse inner spaces, and uppercase the first letter of every word - on blur and again on the server, uppercasing ONLY that letter so `McDonald`, `O\'Brien` and `van der Berg` survive. Best placed once in the shared Input/TextField component, selected by a prop. For a field that must keep what was typed, add an `enigma:allow-no-capitalize` note (frontend-policy, validation-policy).',
|
|
195
|
+
severity: "block",
|
|
196
|
+
skill: "frontend-policy"
|
|
197
|
+
},
|
|
88
198
|
{
|
|
89
199
|
id: "fe-no-native-dialog",
|
|
90
200
|
label: "No native browser dialogs",
|
|
@@ -464,6 +574,12 @@ var BUILTIN_RULES = [
|
|
|
464
574
|
message: "Process spawned without windowsHide. On Windows a console child started by a process that has no console of its own - a daemon, an editor hook, a detached background task - pops a real console window on screen and closes it again, which reads as something crashing. Add `windowsHide: true` to the options object; it is inert on macOS and Linux, and inert on Windows when the parent already has a console, so it is safe on every call that is not deliberately opening a terminal for the user. For one that IS (a login flow that must show a terminal), mark the call with an `enigma:` note.",
|
|
465
575
|
severity: "block"
|
|
466
576
|
},
|
|
577
|
+
// NOTE: no rule for "render the cached snapshot, then patch only what changed". Whether a
|
|
578
|
+
// fresh response REPLACES the rendered list or is reconciled into it is a property of the
|
|
579
|
+
// state update, which normally lives in a store, a query library's cache or a parent - and
|
|
580
|
+
// `setRows(data)` is correct code in the many views that have no snapshot to reconcile
|
|
581
|
+
// against. There is no file-local evidence of the defect, so it stays in frontend-policy's
|
|
582
|
+
// Client-Side Caching section.
|
|
467
583
|
{
|
|
468
584
|
id: "fe-mobile-drawer-full-width",
|
|
469
585
|
label: "An off-canvas panel fills the phone screen",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enigma-cli",
|
|
3
|
-
"version": "1.32.
|
|
3
|
+
"version": "1.32.5",
|
|
4
4
|
"description": "Everything you need to work with a coding agent: install shared policy skills for Claude Code, OpenAI Codex and opencode, and set up portable git security hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|