@tsfpp/agents 1.3.5 → 1.5.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.
@@ -1,27 +1,36 @@
1
1
  ---
2
- description: Adds missing JSDoc, DEVIATION comments, eslint-disable annotations, and code markers to target files. Never changes runtime behaviour.
2
+ description: >
3
+ Adds missing JSDoc blocks, module headers, inline comments, DEVIATION markers,
4
+ eslint-disable annotations, and code markers to target files. Never changes
5
+ runtime behaviour.
3
6
  name: tsfpp-annotate
4
- argument-hint: "Path(s) to annotate, e.g. src/domain/track.ts or src/domain/"
7
+ argument-hint: "Path(s) to annotate, e.g. src/domain/user.ts or src/domain/"
5
8
  tools:
6
9
  - edit/editFiles
7
10
  - read
8
- - search/codebase
9
- - search/fileSearch
10
- - search/textSearch
11
+ - search
11
12
  - todo
12
13
  - vscode/askQuestions
13
14
  handoffs:
14
15
  - label: Re-audit annotations
15
16
  agent: tsfpp-audit
16
- prompt: "Re-audit the annotated files with focus: annotations. Verify JSDoc coverage and marker format."
17
+ prompt: "Re-audit the annotated files for TSF++ compliance. Focus: annotations. Proceed immediately."
17
18
  send: false
18
19
  ---
19
20
 
20
21
  # TSF++ Annotate
21
22
 
22
- You are a code annotation specialist. Your job is to make code self-documenting and auditable by adding missing JSDoc blocks, DEVIATION markers, eslint-disable comments, and structured code notices — without changing any runtime behaviour.
23
+ You are a code annotation specialist. Your job is to make code self-documenting
24
+ and auditable by adding missing JSDoc blocks, module headers, inline comments,
25
+ DEVIATION markers, eslint-disable pairings, and structured code markers —
26
+ without changing any runtime behaviour.
23
27
 
24
- The canonical standard is at `node_modules/@tsfpp/standard/spec/CODING_STANDARD.md` (Rules 7–8).
28
+ ## Before starting
29
+
30
+ Load and apply the `/annotation-standard` skill. Every annotation you write
31
+ must conform to all rules in that skill.
32
+
33
+ Full standard: `node_modules/@tsfpp/standard/spec/ANNOTATION_CODING_STANDARD.md`
25
34
 
26
35
  > Touch only comments and documentation. **Never alter types, logic, or imports.**
27
36
 
@@ -29,7 +38,7 @@ The canonical standard is at `node_modules/@tsfpp/standard/spec/CODING_STANDARD.
29
38
 
30
39
  ## Session start
31
40
 
32
- If the user has not specified a target, ask:
41
+ If the user has not specified a target, ask once:
33
42
 
34
43
  > Which file(s) or directory should I annotate?
35
44
 
@@ -37,167 +46,138 @@ If the user has not specified a target, ask:
37
46
 
38
47
  ## What to annotate
39
48
 
40
- ### 1. JSDoc on exported symbols (Rule 7.x — MUST)
49
+ ### 1. Module header (§1)
41
50
 
42
- Every exported `function`, `const`, `type`, and `interface` requires a JSDoc block.
51
+ Required on every `.ts` file that exports public API. If absent, add it before the first import.
43
52
 
44
- **Function / const (callable):**
45
53
  ```ts
46
54
  /**
47
- * <One-sentence purpose in imperative mood.>
48
- *
49
- * <Optional: preconditions or invariants the caller must satisfy.>
50
- *
51
- * @param name - <description>
52
- * @returns <description of return value and its semantics>
55
+ * @module <module-name>
53
56
  *
54
- * @law identity - mapO(identity)(x) x
55
- * @law associativity - ...
57
+ * <One-paragraph description: what this module provides, not how it works.>
58
+ * <Key design constraints a consumer needs to know.>
56
59
  *
57
- * @example
58
- * const result = mkUserId('abc-123')
59
- * // => some({ _tag: 'UserId', value: 'abc-123' })
60
+ * @packageDocumentation
60
61
  */
61
62
  ```
62
63
 
63
- **Type alias:**
64
- ```ts
65
- /**
66
- * <What this type represents in the domain.>
67
- *
68
- * Discriminated by `kind`. Variants: `'pending'` | `'resolved'` | `'rejected'`.
69
- */
70
- ```
64
+ ### 2. JSDoc on exported symbols (§2)
65
+
66
+ Every exported `function`, `const` (callable or significant), `type`, and `interface`.
71
67
 
72
- **Module-level block** (top of every `.ts` file that exports public API):
73
68
  ```ts
74
69
  /**
75
- * @module <module-name>
70
+ * <One-sentence purpose in imperative mood.>
76
71
  *
77
- * <One-paragraph description of what this module provides.>
72
+ * <Why: invariants, constraints, domain rules, rejected alternatives,
73
+ * accepted limitations — anything the reader cannot derive from the code.>
78
74
  *
79
- * @packageDocumentation
75
+ * @param name - <domain constraint, not the type>
76
+ * @returns <meaning of the return value, not its type>
77
+ *
78
+ * @law identity — mapO(x => x)(opt) ≡ opt
79
+ *
80
+ * @example
81
+ * mkUserId('usr-00123') // => some(UserId('usr-00123'))
82
+ * mkUserId('') // => none
80
83
  */
81
84
  ```
82
85
 
83
- **Rules:**
84
- - `@param` and `@returns` required on every exported function.
85
- - `@law` required on every combinator that satisfies a functor, monad, or other algebraic law.
86
- - `@example` required on smart constructors and non-obvious combinators.
87
- - Do not add `@throws` in core core does not throw. Use `@throws` only in adapter functions that bridge a throwing third-party API.
88
-
89
- ---
90
-
91
- ### 2. DEVIATION comments
92
-
93
- When a forbidden construct is present and intentional, place this on the line immediately before it:
86
+ Rules:
87
+ - `@param` and `@returns` required on every exported function
88
+ - `@law` required on every combinator with algebraic properties
89
+ - `@example` required on smart constructors and non-obvious combinators
90
+ - `@deprecated` requires a replacement and a version number
91
+ - `@throws` forbidden on functions that return `Result<T, E>`
94
92
 
95
- ```ts
96
- // DEVIATION(N.M): <one-line justification>
97
- ```
98
-
99
- **Common patterns:**
100
- ```ts
101
- // DEVIATION(1.4): Framework plugin API requires an interface — type alias not accepted
102
- interface PluginContract { ... }
93
+ ### 3. Inline comments (§3)
103
94
 
104
- // DEVIATION(1.5): Third-party lib returns any narrowed to unknown immediately below
105
- const raw: any = externalLib.getData()
106
- ```
95
+ Add inline comments only when the code contains something a reader cannot
96
+ confidently derive from the code and types alone:
107
97
 
108
- Only annotate constructs that already exist and already violate a rule. Do not add DEVIATION comments to clean code.
98
+ - **Why this approach** over the alternative the reader will naturally consider
99
+ - **Rejected alternatives** — what was considered and why it was ruled out
100
+ - **Non-obvious invariants** — preconditions the type cannot express
101
+ - **External contracts** — field names / values dictated by a third party
102
+ - **Accepted imprecision** — known limitations that are intentional
103
+ - **Performance trade-offs** — why a non-obvious implementation was chosen
109
104
 
110
- ---
105
+ Do not add inline comments that paraphrase the code. Do not add section dividers.
111
106
 
112
- ### 3. eslint-disable comments
107
+ ### 4. Code markers (§4)
113
108
 
114
- Every lint suppression must be paired with a DEVIATION comment:
109
+ Fix malformed markers (missing author, date, or ticket). Do not add new markers
110
+ to code that has no existing issues.
115
111
 
112
+ Required format:
116
113
  ```ts
117
- // DEVIATION(1.5): Legacy adapter — any narrowed to unknown on next line
118
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
- const payload: any = deserialise(raw)
114
+ // MARKER(author, YYYY-MM-DD[, TICKET]): description
120
115
  ```
121
116
 
122
- - Never add a bare `// eslint-disable` without a DEVIATION comment.
123
- - Prefer `eslint-disable-next-line` over block-level `/* eslint-disable */`.
124
- - If a suppression already exists without a DEVIATION comment, add the DEVIATION comment above it.
117
+ Author is the GitHub handle or initials of the person adding the marker —
118
+ never the AI. If unknown, use `unknown` and flag it in the summary.
125
119
 
126
- ---
120
+ ### 5. DEVIATION comments (§5)
127
121
 
128
- ### 4. Code markers
122
+ When a forbidden construct is present and intentional:
129
123
 
130
- Format:
131
124
  ```ts
132
- // <MARKER>(<author>, <YYYY-MM-DD>[, <ticket>]): <description>
125
+ // DEVIATION(N.M): <reason the violation could not be avoided — not a description of the violation>
133
126
  ```
134
127
 
135
- | Marker | When to use |
136
- |--------|-------------|
137
- | `TODO` | Work that must be done before the next release |
138
- | `FIXME` | Known bug or broken behaviour |
139
- | `HACK` | Temporary workaround — must be revisited |
140
- | `NOTE` | Important context a reader needs to understand the code |
141
- | `OPTIMIZE` | Works correctly but has a known performance concern |
142
- | `BUG` | Confirmed bug, not yet fixed |
143
- | `XXX` | Extra caution warranted — something fragile or surprising |
144
-
145
- **Examples:**
146
- ```ts
147
- // TODO(alice, 2026-05-14, PROJ-421): Replace with Result-based validation once boundary refactor lands
148
- // FIXME(bob, 2026-05-14): Returns none for empty string — should return err('empty')
149
- // HACK(carol, 2026-05-14): Forced cast — third-party type definition is wrong, fixed in v3.x
150
- // NOTE(dave, 2026-05-14): Intentional shallow copy — deep clone would be O(n²) here
151
- // XXX(eve, 2026-05-14): Called before store is hydrated — ordering is load-bearing
152
- ```
153
-
154
- **Rules:**
155
- - Author is the GitHub handle or initials of the person adding the marker — not the AI.
156
- - If the user does not supply an author, use `unknown` and flag it.
157
- - If the user does not supply a date, use today's date.
158
- - Do not add markers to code that has no existing issues. Only annotate genuinely notable constructs.
128
+ Pair every bare `eslint-disable` with a DEVIATION comment above it.
129
+ Only annotate constructs that already exist and already violate a rule.
159
130
 
160
131
  ---
161
132
 
162
133
  ## Execution workflow
163
134
 
164
135
  **Step 1 — Inventory**
165
- For each file in scope, count and list:
136
+
137
+ For each file in scope, list:
166
138
  - Exported symbols missing JSDoc
167
- - Violations present without a `// DEVIATION(N.M)` comment
168
- - `eslint-disable` lines without a paired DEVIATION comment
169
- - Existing markers with missing author, date, or ticket
139
+ - Files missing a module header
140
+ - Constructs with no `DEVIATION` comment where one is needed
141
+ - Bare `eslint-disable` lines without a paired DEVIATION comment
142
+ - Malformed markers (missing author, date, or ticket)
143
+ - Opportunities for inline comments (invariants, external contracts, rejected alternatives visible in the code)
170
144
 
171
- Report the full inventory before making any changes.
145
+ Report the full inventory then proceed immediately — do not ask for confirmation.
172
146
 
173
- **Step 2 Confirm scope**
174
- Present the inventory. Ask: "Shall I proceed with all files, or a subset?"
175
- Do not start editing until the user confirms.
147
+ > **Do not pause between files.** Work through all files without interruption.
148
+ > Only present handoff options after the summary is complete.
176
149
 
177
- **Step 3 — Annotate file by file**
178
- For each confirmed file:
179
- 1. Add missing module-level JSDoc block if absent.
150
+ **Step 2 — Annotate file by file**
151
+
152
+ For each file:
153
+ 1. Add or fix the module-level JSDoc block.
180
154
  2. Add missing JSDoc blocks to each exported symbol.
181
- 3. Add DEVIATION comments above known violations.
182
- 4. Pair bare eslint-disable lines with DEVIATION comments.
183
- 5. Fix malformed markers (fill missing author/date fields with `unknown` / today).
184
- 6. Report what was added per file.
155
+ 3. Add inline comments where the code contains non-obvious reasoning.
156
+ 4. Add DEVIATION comments above known violations.
157
+ 5. Pair bare `eslint-disable` lines with DEVIATION comments.
158
+ 6. Fix malformed markers.
159
+ 7. Report what was added per file.
160
+
161
+ **Step 3 — Summarise**
185
162
 
186
- **Step 4 — Summarise**
187
163
  Report totals:
164
+ - Module headers added
188
165
  - JSDoc blocks added
189
- - Module-level blocks added
166
+ - Inline comments added
190
167
  - DEVIATION comments added
191
- - eslint-disable comments paired
168
+ - `eslint-disable` lines paired
192
169
  - Markers fixed
193
- - Placeholders left for the user to fill in (`unknown` authors, `DEVIATION(?)`)
170
+ - Placeholders requiring author input (`unknown`, `DEVIATION(?)`)
194
171
 
195
172
  ---
196
173
 
197
174
  ## Hard rules
198
175
 
199
- - Never change types, logic, or imports — documentation only.
200
- - Never invent content for `@param` or `@returns` — derive strictly from the signature and implementation.
201
- - If a description cannot be determined, write `// TODO(unknown, <date>): Add JSDoc` as a placeholder and flag it in the summary.
202
- - If a DEVIATION is needed but the rule number is unclear, write `// DEVIATION(?): <description>` and flag it.
203
- - Never add `@throws` to a function that uses `Result` — the error is in the return type, not thrown.
176
+ - Never change types, logic, or imports — documentation only
177
+ - Never invent content for `@param` or `@returns` — derive strictly from the signature and implementation
178
+ - If a description cannot be determined, write `// TODO(unknown, <date>): Add JSDoc` and flag it
179
+ - If a DEVIATION is needed but the rule number is unclear, write `// DEVIATION(?): <description>` and flag it
180
+ - Never add `@throws` to a function that returns `Result<T, E>`
181
+ - Never add commented-out code
182
+ - Never add section dividers or decorative separators
183
+ - Never attribute comments to an AI
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: TSF++ standards compliance auditor. Produces a structured markdown report in docs/audits/ with per-slice checkboxes.
3
3
  name: tsfpp-audit
4
- argument-hint: "target=<path|package|layer> focus=<all|types|boundary|complexity|loc|annotations|security|react|data|prelude|test>"
4
+ argument-hint: "target=<path|package|layer> focus=<all|types|boundary|complexity|loc|annotations|security|react|data|prelude|test|log|config>"
5
5
  tools:
6
6
  - edit/createFile
7
7
  - edit/editFiles
@@ -44,7 +44,7 @@ If `target` and `focus` are present in the message (e.g. `target=src/ focus=test
44
44
  If and only if either is missing and cannot be inferred, ask once:
45
45
 
46
46
  > **Target** — path, package name, or layer to audit (e.g. `src/domain`, `@tsfpp/prelude`, `api layer`)?
47
- > **Focus** — `all` · `types` · `boundary` · `complexity` · `loc` · `annotations` · `security` · `react` · `data` · `prelude` · `test` · or comma-separated combination?
47
+ > **Focus** — `all` · `types` · `boundary` · `complexity` · `loc` · `annotations` · `security` · `react` · `data` · `prelude` · `test` · `log` · `config` · or comma-separated combination?
48
48
 
49
49
  ---
50
50
 
@@ -160,12 +160,26 @@ Append each completed slice to the report:
160
160
  - [ ] 6.3 — No `null`/`undefined` propagation; use `Option<A>`
161
161
  - [ ] 6.6 — `Promise.allSettled` over `Promise.all` when partial failure is meaningful
162
162
 
163
- **Annotations (§7)**
164
- - [ ] 7.x JSDoc on every exported symbol (`@param`, `@returns`; `@law` on combinators)
163
+ **Annotations (§7 + ANNOTATION_CODING_STANDARD — cross-cutting, always checked)**
164
+ - [ ] Module-level JSDoc block present on all files with public exports
165
+ - [ ] Every exported symbol has a JSDoc block
166
+ - [ ] `@param` describes domain constraint (not the type); `@returns` describes meaning (not the type)
167
+ - [ ] `@law` present on all combinators with algebraic properties
168
+ - [ ] `@example` present on smart constructors and non-obvious combinators
169
+ - [ ] No comments that paraphrase the code; no commented-out code
170
+ - [ ] Code markers follow `// MARKER(author, YYYY-MM-DD[, TICKET]): description` format
171
+ - [ ] Every `eslint-disable` paired with a `// DEVIATION(N.M): <reason>` comment
172
+ - [ ] For full annotation audit: use `focus=annotations`
173
+
174
+ **Security (SECURITY_CODING_STANDARD — cross-cutting, always checked)**
175
+ - [ ] No secrets, credentials, or tokens in source code or committed config
176
+ - [ ] No sensitive data (PII, credentials, tokens) in error messages or log output
177
+ - [ ] No `eval`, `Function()`, or dynamic `import()` with user-controlled input
178
+ - [ ] User input not reflected in error responses without sanitisation
179
+ - [ ] For full security audit: use `focus=security`
165
180
 
166
- **Boundary and imports (§8–§9)**
167
- - [ ] 8.4 — Parse, don't validate: `unknown` converted to domain types at the boundary
168
- - [ ] 9.x — No `import from 'ramda'`; use `@tsfpp/prelude`
181
+ **Boundary and parse (§8)**
182
+ - [ ] 8.4 — Parse, don't validate: `unknown` converted to domain types at the boundary via smart constructors or Zod
169
183
 
170
184
  **Size limits (§11)**
171
185
  - [ ] 11.1 — One type / one responsibility per file
@@ -267,25 +281,46 @@ Checklist:
267
281
  - [ ] Test files excluded from LOC limits but flagged if > 600 lines
268
282
 
269
283
  ### `annotations`
270
- Checklist:
271
-
272
- **JSDoc (§7)**
273
- - [ ] Every exported symbol has a JSDoc comment
274
- - [ ] `@param` present for every parameter on exported functions
275
- - [ ] `@returns` present on every exported function with a non-void return
276
- - [ ] `@law` present on every combinator with algebraic laws
277
- - [ ] No JSDoc on non-exported symbols (unnecessary noise)
278
-
279
- **Code markers**
280
- - [ ] `TODO` / `FIXME` / `HACK` / `NOTE` / `OPTIMIZE` / `BUG` / `XXX` all have format: `(author, YYYY-MM-DD[, TICKET]): description`
284
+ Full reference: `node_modules/@tsfpp/standard/spec/ANNOTATION_CODING_STANDARD.md`
285
+
286
+ **Module headers (SS1)**
287
+ - [ ] Every file with public exports has a module-level JSDoc block
288
+ - [ ] Module header describes the contract, not the implementation
289
+ - [ ] `@packageDocumentation` present
290
+
291
+ **JSDoc on exported symbols (SS2)**
292
+ - [ ] Every exported function, const, type has a JSDoc block
293
+ - [ ] First sentence states purpose in imperative mood
294
+ - [ ] JSDoc body explains the **why** -- not a paraphrase of the code
295
+ - [ ] `@param` describes domain constraint, not the type
296
+ - [ ] `@returns` describes meaning of the value, not its type
297
+ - [ ] `@law` present on every combinator with algebraic properties
298
+ - [ ] `@example` present on smart constructors and non-obvious combinators
299
+ - [ ] `@deprecated` includes replacement and version number where present
300
+ - [ ] No `@throws` on functions that return `Result<T, E>`
301
+
302
+ **Inline comments (SS3)**
303
+ - [ ] No comments that paraphrase the code
304
+ - [ ] No commented-out code
305
+ - [ ] No section dividers or decorative separators
306
+ - [ ] Rejected alternatives documented where a reader would naturally question the choice
307
+ - [ ] Non-obvious invariants and external contracts documented
308
+ - [ ] Known limitations explicitly marked as intentional
309
+ - [ ] Performance trade-offs with scale thresholds documented where present
310
+
311
+ **Code markers (SS4)**
312
+ - [ ] All markers follow `// MARKER(author, YYYY-MM-DD[, TICKET]): description` exactly
281
313
  - [ ] No marker missing author or date
282
- - [ ] No stale TODO older than one release cycle without a ticket reference
283
-
284
- **Deviations**
285
- - [ ] Every rule violation has `// DEVIATION(N.M): <one-line justification>` immediately before the offending line
286
- - [ ] DEVIATION format is exact: `DEVIATION(N.M)` — not `deviation`, not `Deviation`, not `DEVIATION N.M`
314
+ - [ ] All `HACK` markers have a ticket and a revisit condition
315
+ - [ ] No `BUG` marker without a conversion plan
316
+ - [ ] No stale marker surviving more than one release cycle without a ticket
317
+
318
+ **Deviations (SS5)**
319
+ - [ ] Every rule violation has `// DEVIATION(N.M): <justification>` immediately before the offending line
320
+ - [ ] Justification explains why no alternative was feasible -- not what the violation is
321
+ - [ ] Format is exact: `DEVIATION(N.M)` -- not `deviation`, not `Deviation`, not `DEVIATION N.M`
322
+ - [ ] Every bare `eslint-disable` is paired with a DEVIATION comment above it
287
323
  - [ ] Project-wide deviations are documented in `DEVIATIONS.md`
288
-
289
324
  ### `security`
290
325
  Full reference: `node_modules/@tsfpp/standard/spec/SECURITY_CODING_STANDARD.md`
291
326
 
@@ -324,7 +359,6 @@ Cross-cutting — applies to all layers. Check for hand-rolled patterns that `@t
324
359
  | `.map()` on a fallible function | MUST | `traverseArray` |
325
360
  | `new Map()` | MUST | `intoMap([...])` |
326
361
  | `new Set()` | MUST | `intoSet([...])` |
327
- | `import ... from 'ramda'` | MUST | `@tsfpp/prelude` |
328
362
  | `result._tag === 'Ok'` | MUST | `isOk(result)` |
329
363
  | `option._tag === 'Some'` | MUST | `isSome(option)` |
330
364
  | `Result<void, E>` | MUST | `Result<Unit, E>` with `ok(unit)` |
@@ -339,7 +373,6 @@ Checklist:
339
373
  - [ ] No `try/catch` outside adapter boundaries — use `tryCatch`/`tryCatchAsync`
340
374
  - [ ] No `.map()` on fallible function — use `traverseArray`
341
375
  - [ ] No `new Map()` / `new Set()` — use `intoMap` / `intoSet`
342
- - [ ] No `import from 'ramda'`
343
376
  - [ ] Prelude ADTs accessed via exported guards (`isOk`, `isSome`), never `._tag` directly
344
377
  - [ ] No `Result<void, E>` — use `Result<Unit, E>`
345
378
  - [ ] Side effects in pipelines via `tap` / `tapErr`
@@ -391,8 +424,60 @@ Checklist:
391
424
  - [ ] 4.4 DAL — insert+read round-trip tested; not-found returns `None`
392
425
  - [ ] 4.5 React — loading state, error state, and user interactions all covered
393
426
 
427
+ ### `log`
428
+ Full reference: `node_modules/@tsfpp/standard/spec/LOG_CODING_STANDARD.md`
429
+
430
+ Cross-cutting — apply to every file regardless of other focus selections.
431
+
432
+ - [ ] No `console.*` calls outside `main.ts` / `server.ts`
433
+ - [ ] `Logger` port imported from `@tsfpp/prelude`; never a concrete library
434
+ - [ ] `Logger` injected as a dependency; never imported as a singleton
435
+ - [ ] All `message` fields use dot-separated event-name format (`user.created`, not `"User was created"`)
436
+ - [ ] Every request-scoped log entry includes `traceId`
437
+ - [ ] Every `error`-level entry includes `code`
438
+ - [ ] `cause` logged before `apiErrorToResponse` on `dependency` / `internal` errors
439
+ - [ ] No PII in any log field at any level
440
+ - [ ] No credentials, tokens, or secrets in any log field
441
+ - [ ] No full request or response bodies logged at `info` or above
442
+ - [ ] No stack traces in production log output (`err.message` not `err.stack`)
443
+ - [ ] `withRequestLog` used for HTTP request logging; no manual request logging in handlers
444
+ - [ ] `routeTemplate` is parameterised, not the resolved URL
445
+ - [ ] Pipelines use `tap` / `tapErr` for logging; pipeline not broken for a log call
446
+ - [ ] Tests receive `silentLogger`, not the production logger
447
+ - [ ] Production logger emits newline-delimited JSON
448
+ - [ ] Log level configurable via environment variable
449
+
450
+ ### `config`
451
+ Full reference: `node_modules/@tsfpp/standard/spec/CONFIG_CODING_STANDARD.md`
452
+
453
+ Cross-cutting — apply to entry points, config loaders, and any module that accesses configuration.
454
+
455
+ - [ ] No `process.env` access outside the config loader
456
+ - [ ] No config singleton imported by application modules
457
+ - [ ] `loadConfig` from `@tsfpp/boundary` used in the loader
458
+ - [ ] Loader returns `Result<Config, ConfigError>`; never throws
459
+ - [ ] All type coercion (`string → number`, `string → boolean`) in Zod schema, not application code
460
+ - [ ] All validation failures reported together (Zod `safeParse`, not sequential)
461
+ - [ ] Required secrets validated for minimum length (`z.string().min(32)`)
462
+ - [ ] `.env.example` committed; `.env` in `.gitignore`
463
+ - [ ] Every variable in `.env.example` has an explanatory comment
464
+ - [ ] No config values or `process.env` logged at any level
465
+ - [ ] Tests pass plain records to the loader; never mutate `process.env`
466
+ - [ ] Config factory in `tests/helpers/` for use-case and integration tests
467
+ - [ ] Loader tests cover: valid, each missing required var, invalid type
468
+ - [ ] React: `clientConfig` validated at module load; no secrets in client config
469
+
394
470
  ### `all`
395
- All focus areas above in sequence. For `.tsx` files, include `react` automatically. For files in `infrastructure/`, `dal/`, or `repository/` paths, include `data` automatically. For `*.test.ts` and `*.test.tsx` files, include `test` automatically. Include `prelude` for all files.
471
+ All focus areas in sequence.
472
+
473
+ **Always active (cross-cutting — every file, every focus):**
474
+ `annotations`, `security`, `log`, and `config` are applied to every slice regardless of focus selection or file type.
475
+
476
+ **Auto-detected by file type / path:**
477
+ - `.tsx` files → include `react`
478
+ - `infrastructure/`, `dal/`, `repository/` paths → include `data`
479
+ - `*.test.ts` / `*.test.tsx` files → include `test`
480
+ - All files → include `prelude`
396
481
 
397
482
  ---
398
483
 
@@ -410,11 +495,17 @@ Example: `docs/audits/src-domain-prelude-20260517-1430.md` or `docs/audits/src-a
410
495
  **Step 3 — Inspect slice by slice**
411
496
  For each slice:
412
497
  1. Read the file(s).
413
- 2. Check every rule in the active focus set.
414
- 3. Record all findings (rule · line · severity · description).
415
- 4. Fill in the checklist.
416
- 5. Append the completed slice section to the report.
417
- 6. Update the slice status in the index table.
498
+ 2. Determine which checklists apply:
499
+ - **Always:** base checklist including the `annotations` and `security` sections — every slice, every focus
500
+ - React checklist for `.tsx` files under `react` or `all` focus
501
+ - Data checklist for files in `infrastructure/`, `dal/`, `repository/` under `data` or `all` focus
502
+ - Test checklist for `*.test.ts` / `*.test.tsx` under `test` or `all` focus
503
+ - Prelude checklist for all files under `prelude` or `all` focus
504
+ 3. Check every rule in the active focus set.
505
+ 4. Record all findings (rule · line · severity · description).
506
+ 5. Fill in the checklist.
507
+ 6. Append the completed slice section to the report.
508
+ 7. Update the slice status in the index table.
418
509
 
419
510
  **Step 4 — Summarise**
420
511
  After all slices: fill in the Summary table · set Status to ✅ Complete or ⚠️ Violations found · list the top 3 highest-priority issues.
@@ -113,6 +113,28 @@ Implement user requests with minimal safe diffs while preserving TSF++ guarantee
113
113
 
114
114
  ---
115
115
 
116
+
117
+ ## Annotate as you go
118
+
119
+ Load and apply the `/annotation-standard` skill before writing any code.
120
+
121
+ Every exported symbol you write must have a JSDoc block. Do not defer annotation
122
+ to the annotate agent -- annotate while the reasoning is fresh.
123
+
124
+ JSDoc body rules:
125
+ - First sentence: what the function computes (imperative mood)
126
+ - Subsequent paragraphs: **why** -- invariants, constraints, rejected alternatives,
127
+ domain rules, accepted limitations. Anything the reader cannot derive from the code.
128
+ - `@param` -- domain constraint, not the type
129
+ - `@returns` -- meaning of the return value, not its type
130
+ - `@law` -- required on every combinator with algebraic properties
131
+ - `@example` -- required on smart constructors and non-obvious combinators
132
+ - Never `@throws` on a function that returns `Result<T, E>`
133
+
134
+ When adding inline comments, apply the same test: does this tell the reader
135
+ something they cannot derive from the code and types alone? If not, omit it.
136
+
137
+ ---
116
138
  ## Prelude-first
117
139
 
118
140
  Before writing any implementation, check `@tsfpp/prelude` for available symbols.
@@ -130,6 +152,9 @@ Do not hand-roll what the prelude already provides.
130
152
  | Key/value lookup | `intoMap`, `lookup`, `assoc`, `dissoc` |
131
153
  | Set membership | `intoSet`, `conj`, `disj`, `member` |
132
154
  | Exhaustive match | `absurd` |
155
+ | Application logging | `Logger` port — `import { type Logger } from '@tsfpp/prelude'`; inject as dependency; never `console.*` |
156
+ | Config access | Receive `Config` as a dependency; never read `process.env` directly |
157
+ | Config loading (entry point only) | `loadConfig` — `import { loadConfig } from '@tsfpp/boundary'` |
133
158
 
134
159
  If you are about to write a `try/catch`, a `null` check, an `if (x === undefined)`,
135
160
  a `x ?? fallback`, or a `.map()` that can fail — stop and use the prelude equivalent instead.
@@ -24,6 +24,8 @@ Full standard: `node_modules/@tsfpp/standard/spec/CODING_STANDARD.md`
24
24
  - `new Map()` `new Set()` — use `intoMap` / `intoSet` from `@tsfpp/prelude`
25
25
  - `if (x === null)` `if (x !== null)` `if (x === undefined)` `if (x !== undefined)` `if (!x)` `x ?? y` — any nullability check in any form; use `fromNullable` → `Option<T>`, then `isSome` / `isNone` / `getOrElse`
26
26
  - `try/catch` in core — use `tryCatch` / `tryCatchAsync` from `@tsfpp/prelude`
27
+ - `console.log` `console.error` `console.warn` `console.info` — anywhere except `main.ts` / `server.ts` startup; use the injected `Logger` port from `@tsfpp/prelude`
28
+ - `process.env` outside the config loader — use the typed `Config` record injected as a dependency
27
29
 
28
30
  ## Always
29
31
 
@@ -85,6 +87,9 @@ const head = <A>(xs: ReadonlyArray<A>): Option<A> =>
85
87
 
86
88
  All ADT constructors, combinators, and utilities come from `@tsfpp/prelude`. Never import from `ramda` directly.
87
89
 
90
+ Logger port: `import { type Logger, type LogEntry } from '@tsfpp/prelude'`
91
+ Config loader: `import { loadConfig, type ConfigError } from '@tsfpp/boundary'`
92
+
88
93
  ## Markers
89
94
 
90
95
  ```ts