@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.
- package/CHANGELOG.md +30 -0
- package/README.md +218 -31
- package/bin/bootstrap.sh +0 -0
- package/copilot/agents/tsfpp-annotate.agent.md +99 -119
- package/copilot/agents/tsfpp-audit.agent.md +123 -32
- package/copilot/agents/tsfpp-guarded-coding.agent.md +25 -0
- package/copilot/instructions/tsfpp-base.instructions.md +5 -0
- package/copilot/prompts/trunk-changelog.prompt.md +160 -0
- package/copilot/skills/annotation-standard/SKILL.md +196 -0
- package/copilot/skills/config-standard/SKILL.md +205 -0
- package/copilot/skills/log-standard/SKILL.md +148 -0
- package/init.mjs +4 -0
- package/package.json +6 -6
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
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/
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
49
|
+
### 1. Module header (§1)
|
|
41
50
|
|
|
42
|
-
|
|
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
|
-
* <
|
|
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
|
-
*
|
|
55
|
-
*
|
|
57
|
+
* <One-paragraph description: what this module provides, not how it works.>
|
|
58
|
+
* <Key design constraints a consumer needs to know.>
|
|
56
59
|
*
|
|
57
|
-
* @
|
|
58
|
-
* const result = mkUserId('abc-123')
|
|
59
|
-
* // => some({ _tag: 'UserId', value: 'abc-123' })
|
|
60
|
+
* @packageDocumentation
|
|
60
61
|
*/
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
*
|
|
70
|
+
* <One-sentence purpose in imperative mood.>
|
|
76
71
|
*
|
|
77
|
-
* <
|
|
72
|
+
* <Why: invariants, constraints, domain rules, rejected alternatives,
|
|
73
|
+
* accepted limitations — anything the reader cannot derive from the code.>
|
|
78
74
|
*
|
|
79
|
-
* @
|
|
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
|
-
|
|
84
|
-
- `@param` and `@returns` required on every exported function
|
|
85
|
-
- `@law` required on every combinator
|
|
86
|
-
- `@example` required on smart constructors and non-obvious combinators
|
|
87
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
107
|
+
### 4. Code markers (§4)
|
|
113
108
|
|
|
114
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
122
|
+
When a forbidden construct is present and intentional:
|
|
129
123
|
|
|
130
|
-
Format:
|
|
131
124
|
```ts
|
|
132
|
-
//
|
|
125
|
+
// DEVIATION(N.M): <reason the violation could not be avoided — not a description of the violation>
|
|
133
126
|
```
|
|
134
127
|
|
|
135
|
-
|
|
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
|
-
|
|
136
|
+
|
|
137
|
+
For each file in scope, list:
|
|
166
138
|
- Exported symbols missing JSDoc
|
|
167
|
-
-
|
|
168
|
-
- `
|
|
169
|
-
-
|
|
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
|
|
145
|
+
Report the full inventory then proceed immediately — do not ask for confirmation.
|
|
172
146
|
|
|
173
|
-
**
|
|
174
|
-
|
|
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
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
182
|
-
4.
|
|
183
|
-
5.
|
|
184
|
-
6.
|
|
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
|
-
-
|
|
166
|
+
- Inline comments added
|
|
190
167
|
- DEVIATION comments added
|
|
191
|
-
- eslint-disable
|
|
168
|
+
- `eslint-disable` lines paired
|
|
192
169
|
- Markers fixed
|
|
193
|
-
- Placeholders
|
|
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`
|
|
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
|
|
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
|
-
- [ ]
|
|
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
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
**
|
|
273
|
-
- [ ] Every
|
|
274
|
-
- [ ]
|
|
275
|
-
- [ ] `@
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- [ ]
|
|
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
|
-
- [ ]
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
|
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.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|