indira-workspace-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: indira-api
3
+ description: Scaffold and extend Node/Express backend services in the Indira API architecture — routes/controller/service/repository/model layering, validated per-environment config, PM2 process definitions, and Pino structured HTTP logging with an audit trail on every mutation. Ensures every backend app shares the same folder structure and constraints. Use for any new Express service, or any new module (patient, auth, appointment, audit, billing, ...) added to an existing one.
4
+ ---
5
+
6
+ # indira-api
7
+
8
+ You build Node/Express backend services in the **Indira API architecture**.
9
+ The full spec lives at `docs/api-architecture.md` in the target repository —
10
+ read it completely before doing anything else; every path, filename and rule
11
+ you need is in it, and this skill does not repeat them.
12
+
13
+ Your job has four parts, in order. Do not skip Part 0 or Part 1.
14
+
15
+ ## Part 0 — Establish the spec (new repo only)
16
+
17
+ Check whether the target repo already has `docs/api-architecture.md`.
18
+
19
+ - **It exists** — read it. It is the source of truth for *this* repo, even
20
+ where a later edition of this skill's own copy has moved on. Follow what's
21
+ written there; if it's silent on something this skill covers, follow this
22
+ skill and consider proposing an addition to the repo's doc.
23
+ - **It does not exist** — this is a new service. Copy
24
+ `reference/api-architecture.md` from this skill into `docs/api-architecture.md`
25
+ in the target repo verbatim, then fill in the one placeholder it leaves
26
+ open: §2.4 names the DB driver/ORM as "a per-app choice, recorded here" —
27
+ ask which one this app uses (see Part 1) and write it into that section
28
+ before treating the doc as final.
29
+
30
+ Never invent a second architecture doc under a different name or path —
31
+ `docs/api-architecture.md` is the one location, matching how the UI side of
32
+ these apps keeps its spec at `docs/design-system.md`.
33
+
34
+ ## Part 1 — Ask before you scaffold
35
+
36
+ Do not scaffold from a one-line request. For a **new service**, confirm:
37
+
38
+ - **Purpose & callers** — what does this service do, and who calls it (a
39
+ specific frontend, another internal service, a cron, a webhook)?
40
+ - **Database** — Postgres, MySQL, MongoDB, something else? Driver or ORM
41
+ preference, or should it match another Indira service for consistency?
42
+ - **First modules** — beyond the mandatory `auth` and `audit` (§12 of the
43
+ spec builds these first), which domain modules does the initial version
44
+ need? Get the entity name and its core fields for each.
45
+ - **Auth model** — session, JWT, SSO against an existing identity provider?
46
+ Anything from an existing Indira service to reuse rather than reinvent?
47
+ - **Environments** — which of local/uat/production does this need on day
48
+ one, and are there secrets beyond DB + mail that the env schema must
49
+ declare?
50
+
51
+ For a **new module in an existing service**, confirm just:
52
+
53
+ - **Entity & fields** — name and core fields of the thing this module owns.
54
+ - **Operations** — CRUD, or specific actions (e.g. "approve", "cancel")?
55
+ Which need auth scopes beyond a logged-in user?
56
+ - **Cross-module reads** — which existing modules' *services* (never
57
+ repositories) does this one need to call?
58
+ - **Sensitive fields** — anything that must be redacted from logs or kept
59
+ out of the default list response, per §5 and law 4 of the spec.
60
+
61
+ Write the answers up as a short brief and confirm it before Part 2:
62
+
63
+ ```
64
+ Service: <name> New service | New module in <service>
65
+ DB: <engine/ORM> Auth: <model>
66
+ Modules (first cut): <a>, <b>, auth, audit
67
+ For <this module>: entity <name>, fields <...>, operations <...>
68
+ Depends on (via service): <module>, <module>
69
+ Sensitive fields: <...>
70
+ ```
71
+
72
+ ## Part 2 — Plan the files
73
+
74
+ List, module by module, exactly the files §1–§2 of the spec require —
75
+ `routes.js dto.js controller.js service.js repository.js model.js`, plus
76
+ `errors.js` only if the module needs a domain-specific error and
77
+ `test.js` for its unit tests. No extra files, no extra top-level folders.
78
+
79
+ Checks before you build — answer each with yes:
80
+
81
+ - Does every route have a matching DTO validating body/params/query (§7,
82
+ law 6)?
83
+ - Does the controller do nothing but translate HTTP ↔ domain — no DB import,
84
+ no business `if` (§2.2)?
85
+ - Does the repository expose plain async functions only, with zero business
86
+ rules (§2.4)?
87
+ - Does every mutation route sit behind the shared write path that writes to
88
+ `audit` (law 5)?
89
+ - Does every write route (bar the ones explicitly public) sit behind
90
+ `auth(...)` (§10)?
91
+ - Are cross-module calls going through the other module's *service*, never
92
+ its repository or model (law 7)?
93
+ - Will a missing/invalid env var stop boot rather than fail mid-request
94
+ (§3, law 2)?
95
+
96
+ If any answer is no, fix the plan before writing code.
97
+
98
+ ## Part 3 — Build
99
+
100
+ 1. If Part 0 created a new doc, scaffold the tree in §1 exactly: `app.js`,
101
+ `server.js`, `config/`, `shared/{middleware,errors,mail,logger,utils}`,
102
+ `routes.js`, `ecosystem.config.js`, `.env.example`.
103
+ 2. Wire `config/index.js` (§3) with a Zod schema covering every env var this
104
+ app needs so far. Confirm it exits non-zero on a missing required key —
105
+ check this once by actually removing a required var and running it,
106
+ don't assume the schema does what it looks like it does.
107
+ 3. Wire the shared Pino logger + `pino-http` (§5) into `app.js` **before**
108
+ any route, then `helmet`, CORS from config, a body size limit, and the
109
+ `/api/auth/*` rate limiter (§10).
110
+ 4. Wire `errorHandler.js` (§6) as the last middleware, after `routes.js` is
111
+ mounted.
112
+ 5. Build `auth` and `audit` first if they don't exist yet — every other
113
+ module depends on both.
114
+ 6. For each domain module, write files in this order so nothing imports
115
+ something that doesn't exist yet: `dto.js` → `model.js` →
116
+ `repository.js` → `service.js` → `controller.js` → `routes.js`, then
117
+ mount the router in `routes.js`.
118
+ 7. Add `ecosystem.config.js` and `.env.example` (or extend them) so every
119
+ key the config schema declares has a placeholder.
120
+ 8. Write one integration test per new route under `tests/`, and unit tests
121
+ for the service's business rules beside the module.
122
+ 9. Verify (§12 of the spec): the server boots, `GET /api/health` returns
123
+ 200, one request produces one structured Pino log line carrying a
124
+ request id, and a deliberately-triggered error returns the `{ error:
125
+ { code, message, requestId } }` shape with that same id.
126
+
127
+ ## What "done" looks like
128
+
129
+ Give back, in this order: the confirmed brief, the file plan, what was
130
+ scaffolded vs. added to an existing module, the verification results from
131
+ step 9, and — if this touched `docs/api-architecture.md` — a note of what
132
+ changed there and why, since that file governs every other app too.
133
+
134
+ ## Things you never do
135
+
136
+ - Read `process.env` anywhere outside `config/index.js`.
137
+ - Let a controller import a repository or a DB driver, or a repository
138
+ contain a business decision.
139
+ - Call `console.log`/`console.error` for application logging.
140
+ - Return an error value from a service for a controller to branch on —
141
+ services throw.
142
+ - Add a write route without wiring it to the audit trail, or without
143
+ `auth(...)` unless it's deliberately public.
144
+ - Commit a real `.env.local`/`.env.uat`/`.env.production` file, or put a
145
+ secret in `ecosystem.config.js`.
146
+ - Invent a second architecture doc, a new top-level `src/` folder, or a
147
+ driver/ORM choice that contradicts what `docs/api-architecture.md`
148
+ already recorded for this repo.
@@ -0,0 +1,337 @@
1
+ # Indira API architecture specification
2
+
3
+ A build-from-scratch specification for the Node/Express backend services used
4
+ across Indira apps. Written so an agent (or a person) can scaffold a new
5
+ service, or extend an existing one, and land on the exact same shape every
6
+ time. Where a path or a filename appears, it is the path — not a suggestion.
7
+
8
+ The reference stack is Express + a validated env config + Pino logging. The
9
+ database driver/ORM is a per-app choice (see §2.4); everything else here is
10
+ fixed.
11
+
12
+ ---
13
+
14
+ ## 0. The three sentences
15
+
16
+ 1. **A request flows in one direction only**: route → controller → service →
17
+ repository → database, and back out the same way in reverse. No layer
18
+ calls sideways or skips the one below it.
19
+ 2. **Config is validated once, at boot, in one place.** If an environment
20
+ variable is missing or malformed, the process refuses to start — it never
21
+ fails later, mid-request, in a way that's hard to trace.
22
+ 3. **Every request is logged as one structured line, and every domain
23
+ mutation is written to the audit trail.** Nothing is inferred from
24
+ `console.log` output after the fact.
25
+
26
+ ---
27
+
28
+ ## 1. Folder structure
29
+
30
+ ```
31
+ server/
32
+ ├── src/
33
+ │ ├── app.js # express app: middleware, mount routes
34
+ │ ├── server.js # listen, graceful shutdown
35
+ │ ├── config/
36
+ │ │ ├── index.js # validated env (zod), single import point
37
+ │ │ └── db.js # connection/pool setup, exported once
38
+ │ ├── modules/
39
+ │ │ ├── <module>/
40
+ │ │ │ ├── <module>.routes.js
41
+ │ │ │ ├── <module>.controller.js
42
+ │ │ │ ├── <module>.service.js
43
+ │ │ │ ├── <module>.repository.js # DB access only
44
+ │ │ │ ├── <module>.model.js # schema / entity
45
+ │ │ │ ├── <module>.dto.js # request/response shapes + validation
46
+ │ │ │ ├── <module>.errors.js # domain errors (optional)
47
+ │ │ │ └── <module>.test.js # unit tests, colocated
48
+ │ │ ├── auth/
49
+ │ │ ├── audit/
50
+ │ │ └── ... # one directory per domain module
51
+ │ ├── shared/
52
+ │ │ ├── middleware/ # auth, validate(dto), errorHandler, requestId
53
+ │ │ ├── errors/ # AppError, NotFoundError, ForbiddenError, ...
54
+ │ │ ├── mail/ # transport.js, graph.js
55
+ │ │ ├── logger/ # pino instance + pino-http middleware
56
+ │ │ └── utils/
57
+ │ └── routes.js # mounts every module's router under /api
58
+ ├── tests/ # integration tests only; unit tests live in modules
59
+ ├── scripts/ # seed, migrate, one-off ops
60
+ ├── ecosystem.config.js # committed; PM2 definition for every environment
61
+ ├── .env.example # committed; placeholders only
62
+ ├── .env.local # gitignored; developer's machine
63
+ ├── .env.uat # gitignored; lives only on the UAT box
64
+ ├── .env.production # gitignored; lives only on the prod box
65
+ └── package.json
66
+ ```
67
+
68
+ Nothing is created above `server/src/modules/*` beyond what's listed here. A
69
+ new top-level folder under `src/` is a spec change, not a per-app decision —
70
+ raise it, don't add it quietly.
71
+
72
+ ---
73
+
74
+ ## 2. Layering — one direction, one job per file
75
+
76
+ ### 2.1 Routes (`<module>.routes.js`)
77
+ Declares the Express `Router()`, wires `validate(dto)` and `auth(...)`
78
+ middleware, and points each path at a controller method. No logic. No
79
+ `req`/`res` handling beyond what the router itself needs.
80
+
81
+ ```js
82
+ router.post("/", auth("patient:write"), validate(createPatientDto), controller.create);
83
+ ```
84
+
85
+ ### 2.2 Controller (`<module>.controller.js`)
86
+ Translates HTTP ↔ domain: reads `req.params/query/body` (already validated by
87
+ the DTO middleware), calls exactly one service method, and shapes the
88
+ response envelope (§8.2). A controller never imports the repository or the
89
+ database driver, and never contains business rules — if it has an `if` that
90
+ decides an outcome rather than a status code, that branch belongs in the
91
+ service.
92
+
93
+ ### 2.3 Service (`<module>.service.js`)
94
+ All business logic. Orchestrates one or more repositories, calls other
95
+ modules' services directly (never their repositories), throws domain errors
96
+ (§6) instead of returning error objects, and knows nothing about `req`/`res`
97
+ or HTTP status codes.
98
+
99
+ ### 2.4 Repository (`<module>.repository.js`)
100
+ The only file in the module allowed to import the DB driver/ORM. Exposes
101
+ plain async functions (`findById`, `create`, `listByStatus`, ...) that return
102
+ plain objects or `null` — never a query builder, never a raw driver row.
103
+ Contains no business rules: a repository method that decides *whether* to do
104
+ something, not just *how*, has leaked a service concern.
105
+
106
+ The specific driver/ORM (pg, Prisma, Mongoose, ...) is chosen per app and
107
+ recorded in that app's `docs/api-architecture.md` addendum — the layering
108
+ contract above is what's fixed, not the driver.
109
+
110
+ ### 2.5 Model (`<module>.model.js`)
111
+ The schema/entity definition in whatever the chosen ORM/driver expects, or a
112
+ plain shape comment if the app is schema-less. Read only by the repository.
113
+
114
+ ### 2.6 DTO (`<module>.dto.js`)
115
+ Zod schemas for every request shape the module accepts, and for the response
116
+ shape(s) it returns. The `validate(dto)` middleware (§7) runs these against
117
+ `req.body/params/query` before the controller ever sees the request.
118
+
119
+ ### 2.7 Errors (`<module>.errors.js`, optional)
120
+ Module-specific subclasses of the shared `AppError` (§6) — e.g.
121
+ `PatientAlreadyDischargedError` — for failures that need a distinct code or
122
+ message beyond the generic `NotFoundError`/`ConflictError`.
123
+
124
+ ---
125
+
126
+ ## 3. Config & environments
127
+
128
+ `src/config/index.js` is the **only** place `process.env` is read directly
129
+ anywhere in the codebase. It:
130
+
131
+ 1. Picks the env file by `NODE_ENV` (`local` → `.env.local`, `uat` →
132
+ `.env.uat`, `production` → `.env.production`), loaded with `dotenv`.
133
+ 2. Validates the merged result against a Zod schema — every variable the app
134
+ uses is declared, typed, and either required or given a default.
135
+ 3. Exports one frozen config object. Everywhere else imports from here:
136
+ `import { config } from "../config/index.js"`.
137
+
138
+ If validation fails, the process logs the specific missing/invalid keys and
139
+ exits non-zero — it does not start in a half-configured state.
140
+
141
+ `.env.example` is committed with every key the schema declares, set to a
142
+ placeholder value. `.env.local`, `.env.uat`, `.env.production` are gitignored
143
+ and never leave the machine/box they belong to.
144
+
145
+ ---
146
+
147
+ ## 4. Process management (PM2)
148
+
149
+ `ecosystem.config.js` is committed and defines one app entry per environment
150
+ that box runs, e.g.:
151
+
152
+ ```js
153
+ module.exports = {
154
+ apps: [
155
+ {
156
+ name: "api-uat",
157
+ script: "src/server.js",
158
+ env: { NODE_ENV: "uat" },
159
+ instances: 1,
160
+ exec_mode: "fork",
161
+ watch: false,
162
+ autorestart: true,
163
+ },
164
+ ],
165
+ };
166
+ ```
167
+
168
+ PM2 supplies `NODE_ENV`; `src/config/index.js` does the rest. PM2 captures
169
+ stdout/stderr for process supervision, but log **content** is Pino's job
170
+ (§5) — PM2 log files are a fallback, not where anyone reads structured logs
171
+ from.
172
+
173
+ ---
174
+
175
+ ## 5. Logging — Pino, always on
176
+
177
+ `shared/logger/index.js` exports one Pino instance for the whole app:
178
+
179
+ - **Pretty-printed** (`pino-pretty`) only when `NODE_ENV === "local"`.
180
+ Every other environment emits newline-delimited JSON to stdout.
181
+ - **Redacts** `req.headers.authorization`, `req.headers.cookie`, and any
182
+ field named `password`/`token`/`otp` at any depth.
183
+ - Log level from config (`debug` local, `info` uat/production), never
184
+ hardcoded.
185
+
186
+ `shared/middleware/httpLogger.js` wires `pino-http` into `app.js` **before**
187
+ the routes are mounted:
188
+
189
+ ```js
190
+ app.use(pinoHttp({ logger, genReqId: (req) => req.headers["x-request-id"] ?? randomUUID() }));
191
+ ```
192
+
193
+ This gives one structured line per request — method, path, status, response
194
+ time, and the request id — and that same request id is attached to every
195
+ `req.log.*` call made deeper in the stack (service, repository) and to the
196
+ error response body (§6.2), so a single request's logs and its error can be
197
+ correlated by id. **No module ever calls `console.log`.**
198
+
199
+ ---
200
+
201
+ ## 6. Errors
202
+
203
+ ### 6.1 Hierarchy
204
+ `shared/errors/AppError.js` is the base class: `{ statusCode, code, message,
205
+ details? }`. Concrete subclasses in the same folder: `NotFoundError` (404),
206
+ `ValidationError` (400), `UnauthorizedError` (401), `ForbiddenError` (403),
207
+ `ConflictError` (409). Modules add their own subclasses (§2.7) only for
208
+ failures the generic set doesn't name.
209
+
210
+ ### 6.2 Central handler
211
+ `shared/middleware/errorHandler.js` is the last middleware in `app.js`. Any
212
+ `AppError` maps straight to its status/code/message; any other thrown error
213
+ is logged at `error` level with the request id and returned as a generic 500
214
+ — its internal message is never sent to the client. Response shape:
215
+
216
+ ```json
217
+ { "error": { "code": "NOT_FOUND", "message": "Patient not found", "requestId": "..." } }
218
+ ```
219
+
220
+ Every service throws; nothing downstream of a controller returns an error
221
+ value for the controller to inspect.
222
+
223
+ ---
224
+
225
+ ## 7. Validation
226
+
227
+ `shared/middleware/validate.js` takes a DTO's Zod schema and validates
228
+ `req.body`, `req.params`, and `req.query` before the controller runs,
229
+ throwing a `ValidationError` (with the Zod issue list as `details`) on
230
+ failure. A route with a body, params, or query shape always has a matching
231
+ DTO — a controller reading unvalidated `req.body` directly is a bug, not a
232
+ shortcut.
233
+
234
+ ---
235
+
236
+ ## 8. API surface
237
+
238
+ ### 8.1 Mounting
239
+ `src/routes.js` imports every module's router and mounts it under `/api`:
240
+
241
+ ```js
242
+ router.use("/patients", patientRoutes);
243
+ router.use("/auth", authRoutes);
244
+ ```
245
+
246
+ A version prefix (`/api/v1`) is added only when the app has already shipped
247
+ a v1 and needs to change it — new apps start unversioned.
248
+
249
+ ### 8.2 Response envelope
250
+ Success: `{ "data": <result> }` (a single object, or an array for a list
251
+ endpoint). Paginated lists: `{ "data": [...], "page": { "cursor", "limit",
252
+ "hasMore" } }`. Errors: §6.2's shape. A route never returns a bare array or
253
+ a bare object at the top level — the envelope is constant so a client never
254
+ has to branch on shape.
255
+
256
+ ### 8.3 Health check
257
+ `GET /api/health` (no auth) returns `{ "data": { "status": "ok" } }` and is
258
+ wired before any DB-dependent middleware, so it answers even if a downstream
259
+ dependency is degraded — that's what makes it useful to PM2/monitoring.
260
+
261
+ ---
262
+
263
+ ## 9. Testing
264
+
265
+ - **Unit tests** live next to the code they test (`<module>.test.js`),
266
+ exercise the service and repository logic directly, and mock across
267
+ layers (a service test mocks its repository; it doesn't hit a database).
268
+ - **Integration tests** live in the top-level `tests/`, boot the real
269
+ `app.js` with `supertest`, and hit it over HTTP against a real (test)
270
+ database — they verify the layers wired together correctly, not business
271
+ logic already covered by a unit test.
272
+ - Both run against `.env.local`'s test counterpart or an in-memory/test
273
+ database — never against `.env.uat` or `.env.production`.
274
+
275
+ ---
276
+
277
+ ## 10. Security baseline
278
+
279
+ `app.js` always includes, in this order: `helmet()`, CORS configured from
280
+ `config.corsOrigins` (never `*` outside local), a body size limit, and a rate
281
+ limiter on `/api/auth/*`. Every write route sits behind `auth(...)` unless
282
+ it's explicitly public (health check, login). No raw string-concatenated
283
+ SQL anywhere — parameterized queries or the chosen ORM's query builder only.
284
+ Secrets (DB passwords, JWT signing keys, mail credentials) exist only in the
285
+ gitignored `.env.*` files and in the box's process environment — never in
286
+ `ecosystem.config.js`, never in a committed file.
287
+
288
+ ---
289
+
290
+ ## 11. Laws
291
+
292
+ 1. **One direction.** Route → controller → service → repository → DB, and
293
+ back. A controller that queries the DB, or a repository that contains an
294
+ `if` deciding business outcome, is a layering violation — fix the layer,
295
+ don't work around it.
296
+ 2. **Fail fast on config.** A missing or invalid env var stops boot. It never
297
+ surfaces later as an obscure runtime error.
298
+ 3. **No naked errors.** Every thrown error is an `AppError` subclass or gets
299
+ wrapped into one by the central handler before it reaches a client.
300
+ 4. **No naked console.** All logging goes through the shared Pino instance;
301
+ `req.log` inside a request, `logger` outside one.
302
+ 5. **Every mutation is audited.** A `POST`/`PATCH`/`PUT`/`DELETE` that
303
+ changes domain state writes an audit entry (actor, action, entity,
304
+ before/after or a diff, timestamp) via the `audit` module — this is not
305
+ optional per-module, it's wired once in the shared write path.
306
+ 6. **DTOs are mandatory.** A route accepting a body, params, or query has a
307
+ DTO validating it before the controller runs.
308
+ 7. **One module, one concern.** Cross-module reads go through the other
309
+ module's service, never its repository or model directly.
310
+ 8. **Consistency beats local optimisation.** If a new module wants a shape
311
+ the spec doesn't have, the answer is usually the existing pattern used
312
+ plainly. Extend this document (and every app's copy of it) before
313
+ inventing a one-off.
314
+
315
+ ---
316
+
317
+ ## 12. Building it for a new app
318
+
319
+ 1. Scaffold the tree in §1 exactly. Nothing above `modules/*` beyond what's
320
+ listed.
321
+ 2. Wire `config/index.js` (§3) and confirm the process exits non-zero when a
322
+ required var is missing — check this once, don't assume it.
323
+ 3. Wire the shared Pino logger + `pino-http` (§5) into `app.js` before any
324
+ route is mounted, then `helmet`, CORS, and the body parser (§10).
325
+ 4. Wire `errorHandler.js` (§6) as the last middleware, after `routes.js`.
326
+ 5. Add the `auth` and `audit` modules first — every other module depends on
327
+ both (identity for `auth(...)`, the write path for the audit law).
328
+ 6. For each domain module: routes → dto → controller → service →
329
+ repository → model, in that order, so each file only ever imports things
330
+ that already exist.
331
+ 7. Add `ecosystem.config.js` (§4) and `.env.example` with every key the
332
+ config schema declares.
333
+ 8. Write one integration test per new route in `tests/`, and unit tests for
334
+ the service's business rules in the module.
335
+ 9. Confirm: the server boots, `/api/health` returns 200, one request
336
+ produces one structured Pino log line carrying a request id, and a
337
+ deliberately-triggered error returns the §6.2 shape with that same id.
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: indira-ui
3
+ description: Design and build screens in the Indira Workspace UI language. Interviews the developer or business team first (who uses it, what they come to do, what must be found in two seconds), turns the answers into a component hierarchy that shows no more data than the task needs, then builds it from the design-system spec. Use for any new page, dashboard, form, list, or internal tool that should look and behave like the BRD Governance Tool.
4
+ ---
5
+
6
+ # indira-ui
7
+
8
+ You are a senior product designer who builds. You work in the **Indira
9
+ Workspace UI** language, specified in `docs/design-system.md` in this
10
+ repository. Read that file completely before doing anything else; every
11
+ number and rule you need is in it, and this skill does not repeat them.
12
+
13
+ Your job has three parts, in order, and you do not skip the first one.
14
+
15
+ ## Part 1 — Grill before you draw
16
+
17
+ Do not design from a one-line request. Interview until you can answer every
18
+ question below in the requester's own words. Ask in batches of three or four,
19
+ not one at a time, and stop as soon as the answers are clear — this is a
20
+ discovery, not a form. Use the question tool when one is available; otherwise
21
+ ask in prose and wait.
22
+
23
+ **Who**
24
+ - Who opens this screen, by role? How often — several times a day, once a
25
+ week, once a quarter?
26
+ - What do they already live in all day (Outlook, Excel, a specific ERP)? Their
27
+ habits from that tool are the habits to borrow.
28
+
29
+ **Why they come**
30
+ - What is the *one thing* they most often come here to do? That becomes the
31
+ primary action — the single filled button, top-right.
32
+ - What is the second and third thing? Those are secondary controls or row
33
+ actions, never a second filled button.
34
+ - What question are they trying to answer when they land? ("Is anything
35
+ waiting on me?" "Where is X?") The answer to that question is the first
36
+ thing the screen shows.
37
+
38
+ **What they need to see**
39
+ - For each record shown, what are the three facts they scan for? Everything
40
+ else is a click or a hover away.
41
+ - What do they currently squint at, scroll for, or export to Excel to find?
42
+ - What has been shown on the old screen that nobody uses? (Ask this directly.
43
+ The answer is what you leave out.)
44
+
45
+ **What must not go wrong**
46
+ - Which action is irreversible or expensive? It gets a named, status-coloured
47
+ button and a confirmation with the consequence spelled out.
48
+ - Which data is sensitive to show broadly? It goes behind a hover or a detail
49
+ view, not a list column.
50
+
51
+ **Where it lives**
52
+ - Which sidebar section does it belong under? (Its sidebar item is its name —
53
+ the screen gets no page-title heading.)
54
+ - Is it a list, a detail, a form, or a dashboard? Mixed screens are almost
55
+ always two screens.
56
+
57
+ When the requester says "show everything" or "add a description under each
58
+ field", push back once with the reason (laws 3 and 4 in the spec), then build
59
+ what they decide. When they ask for a second primary action, ask which one a
60
+ newcomer should find first; the other becomes tonal.
61
+
62
+ Write the answers up as a short brief — five lines, not a document — and
63
+ confirm it before Part 2:
64
+
65
+ ```
66
+ Screen: <name> Under: <sidebar section> Kind: list | detail | form | dashboard
67
+ Users: <roles, frequency> Habitat: <Outlook / Excel / …>
68
+ Landing question: "<…>" Primary action: <verb noun> Secondary: <…>, <…>
69
+ Per-record facts: <a>, <b>, <c> Hidden until hover/click: <…>
70
+ Irreversible: <action> → confirm with "<consequence>"
71
+ ```
72
+
73
+ ## Part 2 — Component hierarchy
74
+
75
+ Turn the brief into a hierarchy using **only** components from §4 of the spec.
76
+ Write it as an indented tree with the component name, what it shows, and which
77
+ law justifies it. Example shape:
78
+
79
+ ```
80
+ Shell (§3)
81
+ └─ Content panel
82
+ ├─ Toolbar row — law 2: no title; the sidebar names the screen
83
+ │ ├─ Filter chips (.fchip) by status — counts from the full set
84
+ │ └─ [Primary action] filled button — law 1: the one thing, top-right
85
+ ├─ Record grid (.brd-card ×N) — state-first list; orb = status
86
+ │ └─ per card: title · dept · who · date · approver stack · progress
87
+ │ (id, priority, status text → title= / sr-only) — law 3
88
+ └─ Empty state — one tonal button
89
+ ```
90
+
91
+ Checks before you build — answer each with yes:
92
+
93
+ - Is there exactly one filled button, top-right of the content panel?
94
+ - Is there no page-title heading? (A record title on a detail screen is
95
+ content, and allowed.)
96
+ - Does every list row show ≤ 5 facts, and is everything else reachable by
97
+ hover (contact card, tooltip) or click?
98
+ - Is every explanatory sentence gone — replaced by a `title=` tooltip, a
99
+ contact card, or nothing?
100
+ - Is every status expressed as a shape *and* a colour from the six?
101
+ - Do views use segmented tabs and filters use chips, never the other way round?
102
+ - Does every person render through the Person component (hoverable card)?
103
+ - Can a 4-item form or dialog fit a 900px-tall viewport without scrolling its
104
+ chrome?
105
+ - Will nothing overflow at 1180px — column plan for >5 columns, truncate +
106
+ tooltip on single-line cells, clamp-2 on titles?
107
+ - Is the spacing on the 4/8 grid using the recurring numbers in §1.6?
108
+
109
+ If any answer is no, fix the hierarchy before writing code.
110
+
111
+ ## Part 3 — Build
112
+
113
+ Build from the spec, not from memory of other design systems.
114
+
115
+ 1. Reuse the existing tokens, shell and components in the repository if they
116
+ exist (`client/src/app/index.css`, `client/src/shared/ui`, `client/src/entities/user/ui/Person.jsx`).
117
+ In a fresh project, create them in the order §7 of the spec gives.
118
+ 2. Never introduce a colour, radius, shadow, font size or icon outside the
119
+ scales. If the framework lets you type one, the framework is misconfigured
120
+ (§1.5: delete the defaults).
121
+ 3. Use state layers for hover/press, never a second colour.
122
+ 4. Skeletons in the shape of the content while loading; a spinner only inside
123
+ a working control.
124
+ 5. Every clickable person is `<Person>`; every avatar is `<Avatar>` (photo
125
+ when present, tinted initials otherwise).
126
+ 6. Tables over five columns get a `<colgroup>` plan and `table-layout: fixed`.
127
+ 7. Finish with an overflow audit at 1440 and 1180 (scan for text wider than
128
+ its box, clipped without ellipsis, or past the viewport) and a screenshot
129
+ of each new screen. Report zero offenders, or fix until it is zero.
130
+
131
+ ## What "done" looks like
132
+
133
+ Give back, in this order: the confirmed brief, the hierarchy tree, the
134
+ screens built, the audit result, and a two-line note of any place the request
135
+ was pushed back on and what was decided. If a requested element was left out
136
+ for a law, name the law — the requester should be able to overrule it
137
+ knowingly, not discover it missing.
138
+
139
+ ## Things you never do
140
+
141
+ - Add a page-name heading, a hero, a gradient, an emoji icon, or a second
142
+ filled button.
143
+ - Write a sentence of help text under a control.
144
+ - Show an id, a status word and a status colour for the same record in the
145
+ same place.
146
+ - Invent a colour for a new status. Six statuses; a seventh is a product
147
+ decision, not a design one.
148
+ - Use a spinner for page content, or animate anything on a loop.
149
+ - Design a screen for a user you have not asked about.