@olegkoval/agent-skills 1.43.1 → 1.45.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/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +113 -4
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/fix-verifier.md +56 -2
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/fix-mode.md +19 -1
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/pricing.json +9 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-consistency.md +76 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md +158 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-implementation.md +76 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-quality.md +87 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-simplification.md +63 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-test-quality.md +172 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/phase4-comparison.md +42 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profile.md +365 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-deep.md +83 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-medium.md +82 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/context.json +3 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/house-rules.md +11 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/fixtures/revmux-report.json +179 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/install-revmux-prompts.sh +66 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/revmux-adapter.mjs +261 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/revmux-engine.sh +156 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +76 -0
- package/package.json +1 -1
- package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +113 -4
- package/plugins/olko-github-pr/skills/lekker-review/fix-workflow.js +89 -7
- package/plugins/olko-github-pr/skills/lekker-review/references/agents/fix-verifier.md +56 -2
- package/plugins/olko-github-pr/skills/lekker-review/references/fix-mode.md +19 -1
- package/plugins/olko-github-pr/skills/lekker-review/references/pricing.json +9 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-consistency.md +76 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md +158 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-implementation.md +76 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-quality.md +87 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-simplification.md +63 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-test-quality.md +172 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/phase4-comparison.md +42 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profile.md +365 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-deep.md +83 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/revmux/profiles/lekker-medium.md +82 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/context.json +3 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/house-rules.md +11 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/fixtures/revmux-report.json +179 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/install-revmux-prompts.sh +66 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/revmux-adapter.mjs +261 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/revmux-engine.sh +156 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/selftest.mjs +76 -0
- package/plugins/olko-github-pr/skills/lekker-review/workflow.js +43 -6
- package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-consistency.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: cross-layer consistency: one business rule implemented twice must agree, row by row
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-consistency
|
|
5
|
+
|
|
6
|
+
Review the change for **cross-layer consistency**: one business rule enforced in
|
|
7
|
+
more than one place, where the places disagree.
|
|
8
|
+
|
|
9
|
+
This is the defect class that survives every other lens. Each implementation is
|
|
10
|
+
correct read on its own, each has its own tests, and the bug only exists in the
|
|
11
|
+
gap between them. Nobody reads them side by side, so nobody sees it.
|
|
12
|
+
|
|
13
|
+
### Step 1: find the rules implemented more than once
|
|
14
|
+
|
|
15
|
+
A rule is duplicated when the same decision (allow/block, show/hide, include/
|
|
16
|
+
exclude, retry/fail) is made in two code paths that can both run for the same
|
|
17
|
+
input. The usual shapes:
|
|
18
|
+
|
|
19
|
+
- a client-side guard and the server-side validator behind it (a checkout UI
|
|
20
|
+
extension and the Shopify Function, a form check and the API handler);
|
|
21
|
+
- a UI filter and the query that feeds it;
|
|
22
|
+
- a webhook handler and the cron reconciler that backfills the same state;
|
|
23
|
+
- a feature flag read in two clients that must agree on the same gate;
|
|
24
|
+
- a permission checked in a route guard and again in the service.
|
|
25
|
+
|
|
26
|
+
Search the worktree (`{{WORKDIR}}`), not only the diff. The second
|
|
27
|
+
implementation is very often a file this change never touched; that is exactly
|
|
28
|
+
how the two drift apart.
|
|
29
|
+
|
|
30
|
+
### Step 2: print the two decision tables side by side
|
|
31
|
+
|
|
32
|
+
For every duplicated rule, build the table before judging anything. One row per
|
|
33
|
+
input class, one column per implementation, cell = the decision that
|
|
34
|
+
implementation makes:
|
|
35
|
+
|
|
36
|
+
| Input | Layer A (`file:line`) | Layer B (`file:line`) |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| value present, active | allow | allow |
|
|
39
|
+
| value present, inactive | block | block |
|
|
40
|
+
| **value missing / undefined / empty** | **block** | **allow** |
|
|
41
|
+
| gate disabled | allow | allow |
|
|
42
|
+
|
|
43
|
+
Rows that must always appear, because they are where layers actually diverge:
|
|
44
|
+
|
|
45
|
+
- the missing / `undefined` / `null` / empty-string input;
|
|
46
|
+
- the not-applicable actor (a D2C shopper where the rule is B2B, an
|
|
47
|
+
unauthenticated caller, a shop with no config);
|
|
48
|
+
- the gate or feature flag being off;
|
|
49
|
+
- the error path (one layer fails open, the other fails closed).
|
|
50
|
+
|
|
51
|
+
Put the real table in the finding. A reader who cannot see both columns cannot
|
|
52
|
+
check your claim, and the table is the whole evidence.
|
|
53
|
+
|
|
54
|
+
### Step 3: judge the divergence
|
|
55
|
+
|
|
56
|
+
Any row where the two columns differ is a finding. Severity:
|
|
57
|
+
|
|
58
|
+
- **critical**: the strict layer is the one that can be bypassed, or the
|
|
59
|
+
divergence blocks a legitimate action (a user who should be able to check out
|
|
60
|
+
cannot) or admits one that should be blocked.
|
|
61
|
+
- **major**: the layers disagree but the authoritative layer is still correct,
|
|
62
|
+
so the visible effect is a confusing or wrong message rather than a wrong
|
|
63
|
+
outcome.
|
|
64
|
+
|
|
65
|
+
Name which layer is authoritative and say so explicitly: the server-side,
|
|
66
|
+
unbypassable one is the specification, and the advisory client-side one must
|
|
67
|
+
match it. **A client layer that is STRICTER than the server is still a bug**, and
|
|
68
|
+
the easy one to wave through, because it looks like extra safety. It is not: it
|
|
69
|
+
blocks work the system allows, and the person hitting it has no way around a
|
|
70
|
+
rule the server would have permitted.
|
|
71
|
+
|
|
72
|
+
Also compare both tables against the acceptance criteria in `{{CONTEXT}}`. When
|
|
73
|
+
an AC governs the same decision and one layer disagrees with it, quote the AC
|
|
74
|
+
verbatim in the finding. When the AC and a scoping decision in `{{CONTEXT}}`
|
|
75
|
+
disagree with each other, report that as its own finding, quote both, and do not
|
|
76
|
+
pick a side; that contradiction is the author's call to make.
|
package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-conventions.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: deviations from Teifi's own codebase conventions — the "strong teammate" non-blocking lens
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-conventions
|
|
5
|
+
|
|
6
|
+
Review the change for deviations from Teifi's established codebase
|
|
7
|
+
conventions and idioms. This is the "strong teammate" lens: the suggestions a
|
|
8
|
+
senior Teifi engineer leaves — non-blocking, but they make the code match how
|
|
9
|
+
the rest of the codebase is written. Look beyond the diff only for
|
|
10
|
+
convention-specific precedent and reuse searches. Other lenses may inspect the
|
|
11
|
+
runtime context they need for their own cross-file checks.
|
|
12
|
+
|
|
13
|
+
Read `{{PROFILE}}` now, before forming any opinion — it carries the Teifi
|
|
14
|
+
conventions text. Its §1 (naming matrix), §2 (comment policy), §5 (commit
|
|
15
|
+
hygiene) and §6 (generated code) are yours — they are the house style, so a
|
|
16
|
+
deviation needs NO codebase precedent beyond that file (the file IS the
|
|
17
|
+
precedent; cite the section, e.g. "teifi-conventions §1 verbs").
|
|
18
|
+
Everything else in this lens still requires a cited precedent from the code.
|
|
19
|
+
|
|
20
|
+
Axes to cover:
|
|
21
|
+
- Naming (teifi-conventions §1): every symbol the diff INTRODUCES against the
|
|
22
|
+
matrix — boolean without `is`/`has`, async I/O named `get`, a row lock or a
|
|
23
|
+
throw-on-miss or a cache read absent from the name (`…ForUpdate`,
|
|
24
|
+
`…OrThrow`, `…Cached`), a collidable component without its domain prefix, a
|
|
25
|
+
bare generic noun (`line`, `node`, `row`) where the domain has two variants in
|
|
26
|
+
scope. NEVER flag a boundary name (DB column, GraphQL/oRPC field, enum value,
|
|
27
|
+
route string, wire key) — renaming it breaks callers outside the diff.
|
|
28
|
+
- Comments (teifi-conventions §2): one finding per over-commenting offender the
|
|
29
|
+
diff ADDED, with the deletion as the fix. Never a vague "too many comments",
|
|
30
|
+
never a pre-existing comment, never a lint/type pragma or a genuine
|
|
31
|
+
non-obvious "why".
|
|
32
|
+
- Generated code (teifi-conventions §6): a changed `.sql` / `.graphql` /
|
|
33
|
+
`.json` schema / `prisma/schema.prisma` whose generated artifact is absent
|
|
34
|
+
from the diff (or the reverse) — major. Raise NO naming, comment, or
|
|
35
|
+
complexity finding inside a `generated/` directory. A hand-rolled `fetch` to
|
|
36
|
+
the Shopify Admin GraphQL endpoint instead of the genql client — major.
|
|
37
|
+
- Commits (teifi-conventions §5): non-conventional or vague commit subjects, and
|
|
38
|
+
any Claude Code / assistant mention in the commit or PR text.
|
|
39
|
+
- Type-system idioms:
|
|
40
|
+
* A hand-written interface/type that duplicates an existing Zod schema —
|
|
41
|
+
should be `z.infer<typeof zSchema>` so the schema stays the single source
|
|
42
|
+
of truth. (Grep for a matching z-schema in the same feature folder.)
|
|
43
|
+
* Raw `string` used for a Shopify GID or an entity id where a branded
|
|
44
|
+
`ID<'Customer'>` (or similar) type exists and is used elsewhere.
|
|
45
|
+
* A union typed as `as readonly string[]` / a hand-rolled `is...` guard where
|
|
46
|
+
a `z.enum([...])` + `z.infer` would give validation, narrowing, and the
|
|
47
|
+
options array in one declaration.
|
|
48
|
+
* An unnecessary `satisfies` / redundant type annotation the compiler already
|
|
49
|
+
infers.
|
|
50
|
+
* A GID validated/parsed inline where a shared helper exists (e.g.
|
|
51
|
+
`zNamespacedGid`). Grep the shared libs and the repo before asserting.
|
|
52
|
+
- Reuse (search the worktree AND sibling Teifi repos before flagging):
|
|
53
|
+
* Inline fetch/client logic that should reuse — or be promoted into — a
|
|
54
|
+
shared client (e.g. a company-switcher client) that already exists or that
|
|
55
|
+
the codebase clearly wants.
|
|
56
|
+
* A util/helper that already exists elsewhere being re-implemented inline.
|
|
57
|
+
* A symbol defined locally that is (or should be) exported from a shared
|
|
58
|
+
module — "are we not exporting this somewhere?"
|
|
59
|
+
- Consistency:
|
|
60
|
+
* Cache-key / composite-key separators that disagree with the repo's
|
|
61
|
+
prevailing choice (e.g. `:` vs `::`). Grep existing key-building code to
|
|
62
|
+
find the prevailing pattern, then flag the deviation.
|
|
63
|
+
* Ad-hoc error throwing where the repo has an idiom (e.g. `throw new
|
|
64
|
+
HttpError('...', 403)` instead of a bare string / generic Error).
|
|
65
|
+
* Naming/casing that breaks the convention used by sibling files.
|
|
66
|
+
|
|
67
|
+
MANDATORY SWEEP — do this FIRST, before forming any opinion:
|
|
68
|
+
|
|
69
|
+
The axes above are symptom-driven: they only fire once you already suspect a
|
|
70
|
+
duplication. That is how a re-implemented helper slips through — nobody thinks to
|
|
71
|
+
look. So run these enumerations mechanically, whether or not anything looks wrong.
|
|
72
|
+
|
|
73
|
+
1. **Sibling sweep for every file the diff ADDS.** For each added file, list its
|
|
74
|
+
directory and read the exports of its neighbours. A helper that solves the same
|
|
75
|
+
problem is usually sitting in the same folder.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
REVIEW_CONFIG="{{CONTEXT}}/worktree.json"
|
|
79
|
+
WORKTREE="$(jq -er '.worktreePath | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
80
|
+
BASE_REV="$(jq -er '(.mergeBase // .baseRef) | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
81
|
+
REPO_ROOT="$(jq -er '.repoRoot | select(type == "string" and length > 0)' "$REVIEW_CONFIG")"
|
|
82
|
+
SIBLING_ROOT="$(dirname "$REPO_ROOT")"
|
|
83
|
+
|
|
84
|
+
git -C "$WORKTREE" diff --name-status "$BASE_REV"...HEAD | awk '$1=="A"{print $2}' |
|
|
85
|
+
while IFS= read -r added_file; do
|
|
86
|
+
added_dir="$WORKTREE/$(dirname "$added_file")"
|
|
87
|
+
ls -la "$added_dir" # what already lives beside it
|
|
88
|
+
grep -rn "^export " "$added_dir"/*.ts "$added_dir"/*.tsx 2>/dev/null |
|
|
89
|
+
grep -v -F -- "$added_file"
|
|
90
|
+
done
|
|
91
|
+
grep -rn "^export " "$SIBLING_ROOT" --include='*.ts' --include='*.tsx' \
|
|
92
|
+
--exclude-dir=node_modules 2>/dev/null
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A new `foo/bar-thing.ts` next to an existing `foo/thing.ts` is a finding waiting
|
|
96
|
+
to happen. Read the neighbour, do not just note its name.
|
|
97
|
+
|
|
98
|
+
2. **New-symbol sweep.** For every function/const the diff exports, search the repo
|
|
99
|
+
for something that already does that job, by BEHAVIOUR not just by name. Names
|
|
100
|
+
rarely match; behaviour does.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git -C "$WORKTREE" diff --unified=0 "$BASE_REV"...HEAD | \
|
|
104
|
+
grep '^+' | grep -E 'export (function|const) ' # collect new symbols
|
|
105
|
+
# then for each, search by what it does, e.g. a locale normaliser:
|
|
106
|
+
grep -rln "toLowerCase()\|normalize\|isoCode\|split('-')" "$WORKTREE" \
|
|
107
|
+
--include='*.ts' --include='*.tsx'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Pick 2 or 3 behavioural keywords from the new function's body and grep those.
|
|
111
|
+
Reviewing the diff alone cannot catch this.
|
|
112
|
+
|
|
113
|
+
3. **State what you swept.** In your output, name the directories you listed and the
|
|
114
|
+
behavioural greps you ran, even when they found nothing. A sweep that is not
|
|
115
|
+
reported did not happen, and the next reviewer cannot tell "no duplication exists"
|
|
116
|
+
from "nobody looked".
|
|
117
|
+
|
|
118
|
+
HARD RULES:
|
|
119
|
+
- Only raise a finding when the better pattern PROVABLY ALREADY EXISTS. Cite it:
|
|
120
|
+
the file:line where the helper/type/convention lives, or the sibling file that
|
|
121
|
+
does it the idiomatic way. If you cannot find a concrete precedent, DROP the
|
|
122
|
+
finding — "this would be nicer as X" on taste alone is not allowed.
|
|
123
|
+
- Every finding must still trace to a `+` line in the diff (the deviation must
|
|
124
|
+
be code this change added/changed). The supporting precedent may live outside the
|
|
125
|
+
diff; the deviation may not.
|
|
126
|
+
- These are suggestions, not blockers. Do not inflate severity. Report each as
|
|
127
|
+
`file:line — <deviation> (precedent: <file:line of the existing pattern>)`.
|
|
128
|
+
- ONE EXCEPTION to non-blocking: if the re-implementation DIVERGES in behaviour
|
|
129
|
+
from the helper it duplicates, that is not a style nit, it is two spellings of
|
|
130
|
+
the same value that disagree, and it belongs on lekker-quality's severity
|
|
131
|
+
scale rather than this lens's. Diff the two implementations before deciding:
|
|
132
|
+
same inputs, same outputs? If a real input produces different results, say so
|
|
133
|
+
explicitly and give the input.
|
|
134
|
+
|
|
135
|
+
To find precedents, you may run:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
grep -rn "<symbol or pattern>" "$WORKTREE" --include='*.ts' --include='*.tsx'
|
|
139
|
+
find "$SIBLING_ROOT" \( -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" \
|
|
140
|
+
| xargs grep -l "<symbol>" 2>/dev/null | head
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Always include *.tsx. Extension and frontend code lives in .tsx, so a search that
|
|
144
|
+
omits it silently reports "no precedent exists" for whole directories.
|
|
145
|
+
|
|
146
|
+
A mined record of what other bots (Greptile / Gemini / CodeRabbit) have
|
|
147
|
+
commented on in this repo may be present under `{{CONTEXT}}`. Use it as a prior,
|
|
148
|
+
not as a checklist: a high count means "frequently raised here", not "correct" —
|
|
149
|
+
never raise a finding because a bot once said it, only because it is true here.
|
|
150
|
+
|
|
151
|
+
Rules:
|
|
152
|
+
- Report file:line — description with a precedent citation. No positive
|
|
153
|
+
observations. No taste-only suggestions.
|
|
154
|
+
- Quote the verbatim offending line(s) — never paraphrased, never reconstructed
|
|
155
|
+
from memory — and give a concrete drop-in fix, or when the fix is
|
|
156
|
+
architectural, a minimal skeleton plus one sentence on what else must change.
|
|
157
|
+
- A finding you cannot quote and cannot fix is a finding you have not proven —
|
|
158
|
+
drop it instead.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: correctness, scalability and integration issues — Teifi's GQL-1 hard rule included
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-implementation
|
|
5
|
+
|
|
6
|
+
Review the change for correctness, scalability, and integration issues.
|
|
7
|
+
|
|
8
|
+
Axes to cover:
|
|
9
|
+
- Business Logic / AC coverage: for each acceptance criterion available in
|
|
10
|
+
`{{CONTEXT}}` (a ticket's AC list, when present), mark met / partial / missing.
|
|
11
|
+
Scope creep is also worth flagging.
|
|
12
|
+
Decide each AC against the code that RUNS, not against the diff. Use the
|
|
13
|
+
worktree (`{{WORKDIR}}`) to follow the AC through its runtime path into files
|
|
14
|
+
the diff never touched, and specifically:
|
|
15
|
+
(a) Find what invokes the new code and on what trigger. New code that nothing
|
|
16
|
+
calls satisfies nothing, however correct its body is.
|
|
17
|
+
(b) For an AC with a timing, cadence or SLA component, name the trigger's real
|
|
18
|
+
frequency (the cron expression, the poll interval, the webhook) and compare
|
|
19
|
+
that number against the AC. A reaper on a daily cron cannot meet a two-hour
|
|
20
|
+
SLA.
|
|
21
|
+
(c) For an AC about a failure mode (crash, OOM, timeout, network loss), confirm
|
|
22
|
+
the handling is reachable under that failure. A killed process never reaches
|
|
23
|
+
a `finally` block, a shutdown hook, or the tail of a long-running function.
|
|
24
|
+
An AC can fail with every added line correct, because the defect is what the
|
|
25
|
+
diff left alone. That is a finding, not an absence of one.
|
|
26
|
+
- Binding scoping decisions: when `{{CONTEXT}}` names a resolved flag from the
|
|
27
|
+
ticket's Dev Scoping Session, a diff that contradicts it is major (critical
|
|
28
|
+
when it changes external behaviour or data shape); quote the decision text.
|
|
29
|
+
- Scalability: N+1 queries, missing pagination, unbounded in-memory
|
|
30
|
+
collections, missing rate-limit handling, cron jobs without overlap guard,
|
|
31
|
+
missing DB indexes for new query patterns.
|
|
32
|
+
- Time/Space Complexity: O(n²) where linear exists, large payloads in memory,
|
|
33
|
+
sort/dedup on large arrays that could be done at DB level.
|
|
34
|
+
- Integration Contracts: Shopify API misuse, BC API assumptions, webhook
|
|
35
|
+
idempotency, external API pagination not handled.
|
|
36
|
+
- GraphQL pagination (GQL-1): for every GraphQL query in the diff that uses a
|
|
37
|
+
nodes connection (`nodes { ... }`):
|
|
38
|
+
(a) Check that `pageInfo { hasNextPage endCursor }` is present alongside nodes — if missing, critical.
|
|
39
|
+
(b) Check that all pages are fetched (a loop or recursion using endCursor) — a single-page fetch is a bug, critical.
|
|
40
|
+
(c) Check the page size: must be 250 (Shopify max). If any other size is used without a code comment explaining why, flag as major.
|
|
41
|
+
Title any critical finding under this axis `[GQL-1] ...`.
|
|
42
|
+
- Feature-flag rollout (Reflag repos ONLY): first check the repo actually uses
|
|
43
|
+
Reflag — a `package.json` (any depth, excluding node_modules) depending on
|
|
44
|
+
`@reflag/node-sdk` or `@teifi-digital/reflag-client`. If it does not, SKIP this
|
|
45
|
+
axis entirely and raise nothing; a repo with no flag client cannot act on the
|
|
46
|
+
finding. Where it does apply, ask whether the change should ship behind a flag:
|
|
47
|
+
(a) a client should validate it before everyone sees it; (b) it changes data
|
|
48
|
+
shape or what gets written; (c) it touches orders, money, or fulfilment; (d) it
|
|
49
|
+
cannot be verified without real client data or volume. Do NOT raise it for pure
|
|
50
|
+
UI/copy with no data change, a bug fix that is strictly better and obviously
|
|
51
|
+
correct, internal/admin-only surfaces, or work fully covered by tests and
|
|
52
|
+
verifiable in staging. Tie-breaker: if you would not be comfortable fixing it
|
|
53
|
+
forward at 2am, it needs a flag. Name which of (a)-(d) applies.
|
|
54
|
+
Severity: major at most, usually minor — this is a rollout judgement call, not
|
|
55
|
+
a defect. NEVER title this with a bracketed hard-rule tag (that would force
|
|
56
|
+
critical and imply a policy violation). Never invent a concrete flag key as
|
|
57
|
+
though it exists: flag keys must be confirmed against Reflag, so say a flag is
|
|
58
|
+
needed without naming one.
|
|
59
|
+
Also raise as a structural concern (severity major) when a diff BOTH adds a
|
|
60
|
+
column/table AND changes what is read or written — the SOP requires splitting
|
|
61
|
+
that into expand / migrate / read-switch / contract PRs. Name the split.
|
|
62
|
+
|
|
63
|
+
GQL-1's full rule text lives in `{{PROFILE}}` — read it there before applying it.
|
|
64
|
+
|
|
65
|
+
Rules:
|
|
66
|
+
- Every finding must trace to a `+` line in the diff, with one exception: an
|
|
67
|
+
unmet AC whose defect lives in code the diff did not touch. Anchor that one to
|
|
68
|
+
the unchanged file:line that had to change, and say in the description why the
|
|
69
|
+
unchanged line is the defect. That line may be an unchanged one — do not drop
|
|
70
|
+
an unmet AC for lack of a quotable added line.
|
|
71
|
+
- Report file:line — description. No positive observations.
|
|
72
|
+
- Quote the verbatim offending line(s) — never paraphrased, never reconstructed
|
|
73
|
+
from memory — and give a concrete drop-in fix, or when the fix is
|
|
74
|
+
architectural, a minimal skeleton plus one sentence on what else must change.
|
|
75
|
+
- A finding you cannot quote and cannot fix is a finding you have not proven —
|
|
76
|
+
drop it instead.
|
package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-quality.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: quality, security and data-integrity issues — Teifi's TS-1/TS-2 hard rules included
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-quality
|
|
5
|
+
|
|
6
|
+
Review the change for quality, security, and data-integrity issues.
|
|
7
|
+
|
|
8
|
+
Axes to cover:
|
|
9
|
+
- Data Integrity: missing transactions on multi-step writes, optimistic
|
|
10
|
+
concurrency without locking, partial-failure with no rollback, silent data
|
|
11
|
+
loss in batch loops.
|
|
12
|
+
- Security: SQL/command injection, auth bypass, missing permission checks,
|
|
13
|
+
IDOR, secrets in logs, webhook signature not verified.
|
|
14
|
+
When the diff touches a controller, a route, or an auth decorator, do NOT hand-roll the
|
|
15
|
+
decorator grep. Use the `route-auth-map` skill, which already does exactly this and prints
|
|
16
|
+
method/path/controller/guards plus an explicit unguarded-endpoint section. If the Skill tool
|
|
17
|
+
is unavailable to you, read `~/.claude/skills/route-auth-map/SKILL.md` and follow its steps.
|
|
18
|
+
Compare the map before and after the diff: a route that gains a handler but no guard, or
|
|
19
|
+
loses `@Authenticated`/`@Permission`, is critical.
|
|
20
|
+
- Error Handling: unhandled promise rejections, empty catch blocks, missing
|
|
21
|
+
retries on transient failures, no dead-letter for failed jobs.
|
|
22
|
+
- Catch-block exit paths: when the diff adds or edits a branch inside a `catch`,
|
|
23
|
+
enumerate EVERY way control leaves that block - each early return, each
|
|
24
|
+
rethrow, and the fall-through - and say what the client sees on each. A new
|
|
25
|
+
branch that changes what gets logged or reported, while leaving a rethrow or
|
|
26
|
+
fall-through reachable for the same condition, is a finding: the error is now
|
|
27
|
+
silent AND still escapes. Do not accept "it reports correctly" as covering the
|
|
28
|
+
block; the report and the control flow are separate claims.
|
|
29
|
+
- Schema/Migration: NOT NULL without default, rename without two-step,
|
|
30
|
+
pgtyped queries invalidated, Prisma client out of sync.
|
|
31
|
+
- Naming/Typos: wrong casing convention, mixed conventions in same scope,
|
|
32
|
+
misspelled identifiers (these are bugs-in-waiting).
|
|
33
|
+
- Derived-value consistency: when the diff introduces a transform of some input
|
|
34
|
+
(normalise, trim, lowercase, parse, clamp, default), grep EVERY other use of
|
|
35
|
+
that raw input in the same scope and check they all go through the transform.
|
|
36
|
+
Half-applied transforms are a classic near-miss: the value is normalised at the
|
|
37
|
+
call site but the raw one is still used in a React dependency array, a cache
|
|
38
|
+
key, a log line, an equality check, or a second call site. Two spellings of the
|
|
39
|
+
"same" value then disagree. Enumerate the uses; do not eyeball the hunk.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
grep -n "<rawIdentifier>" <file> # every use, then confirm each is intended
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- Env vars: dead vars, renamed without migration, wrong fallback operator
|
|
46
|
+
(?? vs ||), type mismatch, leaked in logs.
|
|
47
|
+
- TypeScript type safety (TS-1): flag every type cast (`as X`, `<X>expr`)
|
|
48
|
+
and every use of `any`. Test files: only flag blatantly omitted types
|
|
49
|
+
(e.g. `any[]` on a clearly-typed list). All others: critical. Quote the
|
|
50
|
+
cast, explain the correct type, show the fix. Ask if they're Harry Potter.
|
|
51
|
+
Title the finding `[TS-1] ...`.
|
|
52
|
+
- No JavaScript files (TS-2): if the diff adds any `.js` file to a non-Liquid
|
|
53
|
+
theme repo, flag as critical — must be `.ts`. Title the finding `[TS-2] ...`.
|
|
54
|
+
- Dependency changes. Skip this axis entirely unless the diff touches
|
|
55
|
+
`package.json`, a lockfile, or a vendored dependency. Where it applies:
|
|
56
|
+
(a) A version bump is a behaviour change nobody in this PR wrote. If neither
|
|
57
|
+
the PR body nor a commit message cites the changelog or migration notes,
|
|
58
|
+
that is major: semver is a promise the maintainer may not have kept,
|
|
59
|
+
and a "patch" can carry a behavioural change.
|
|
60
|
+
(b) A bulk bump of several unrelated packages in one PR is major. When
|
|
61
|
+
it breaks the build you have lost which package did it. The fix is to
|
|
62
|
+
split it per package, or per genuinely related group.
|
|
63
|
+
(c) A `package.json` dependency change with no matching lockfile change in the
|
|
64
|
+
same diff, or a lockfile change with no `package.json` change and no
|
|
65
|
+
explanation, is critical: the lockfile is what actually ships.
|
|
66
|
+
(d) A new direct dependency that duplicates something already in the stack is
|
|
67
|
+
major. Name the existing thing that already solves it.
|
|
68
|
+
Raise NO naming, comment, complexity, or TS-1 finding inside a lockfile or a
|
|
69
|
+
`node_modules` path.
|
|
70
|
+
|
|
71
|
+
TS-1 and TS-2 are Teifi hard rules: their text is defined in full in `{{PROFILE}}`
|
|
72
|
+
(read it there before applying either). A finding for one of them MUST have its
|
|
73
|
+
title start with the bracketed tag, e.g. `[TS-1] ...` or `[TS-2] ...`, so the
|
|
74
|
+
caller can recognize it as a policy violation rather than an ordinary finding.
|
|
75
|
+
|
|
76
|
+
CI status, Sentry signals, and existing reviews may be present in `{{CONTEXT}}` —
|
|
77
|
+
read what is there before forming an opinion, and skip anything that is absent
|
|
78
|
+
rather than treating its absence as a finding.
|
|
79
|
+
|
|
80
|
+
Rules:
|
|
81
|
+
- Every finding must trace to a `+` line in the diff.
|
|
82
|
+
- Report file:line — description. No positive observations.
|
|
83
|
+
- Quote the verbatim offending line(s) from the diff — never paraphrased, never
|
|
84
|
+
reconstructed from memory — and give a concrete drop-in fix, or when the fix
|
|
85
|
+
is architectural, a minimal skeleton plus one sentence on what else must change.
|
|
86
|
+
- A finding you cannot quote and cannot fix is a finding you have not proven —
|
|
87
|
+
drop it instead of reporting it as a minor observation with no evidence.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: over-engineering and DRY violations, plus Teifi's debug-artifact hygiene table
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-simplification
|
|
5
|
+
|
|
6
|
+
Review the change for over-engineering and DRY violations.
|
|
7
|
+
|
|
8
|
+
Look for:
|
|
9
|
+
- Copy-paste logic: identical blocks that differ only in a constant — flag
|
|
10
|
+
for extraction.
|
|
11
|
+
- Parallel implementations: two functions doing the same thing — one should
|
|
12
|
+
call the other.
|
|
13
|
+
- Unnecessary abstraction inversion: private helper called exactly once, adds
|
|
14
|
+
no reuse — should be inlined.
|
|
15
|
+
- Over-engineered control flow: nested ternaries / promise chains that could
|
|
16
|
+
be plain if/else or async/await.
|
|
17
|
+
- Config spread: same magic constant defined in multiple files.
|
|
18
|
+
- Debug artifacts and hygiene: apply the fixed severity table in §3 of
|
|
19
|
+
`{{PROFILE}}` (the Teifi conventions section) — `debugger` and
|
|
20
|
+
`.only`/`fit`/`fdescribe` are critical, an added `console.log`/`console.debug`
|
|
21
|
+
in production code and a hardcoded URL are major, an unreferenced
|
|
22
|
+
TODO/FIXME and a >3-line commented-out block are minor. Those severities
|
|
23
|
+
are fixed: do not soften them, and only flag occurrences the diff ADDED.
|
|
24
|
+
- Wrapper that adds nothing, factory for single implementation, layer-cake
|
|
25
|
+
anti-pattern (handler → service → repo with no logic in any layer).
|
|
26
|
+
- Feature flags always on/off, fallback that can never trigger, dual
|
|
27
|
+
implementations where old has no callers.
|
|
28
|
+
- Relocated complexity: a refactor that moves code without reducing the number
|
|
29
|
+
of concepts a reader must hold to follow it. Count them before and after. If
|
|
30
|
+
the count is unchanged, the restructuring did not simplify anything, and the
|
|
31
|
+
finding is that a cheaper move was available (deleting a branch, a mode, or a
|
|
32
|
+
layer outright, rather than re-centralising the same logic). Major when
|
|
33
|
+
the change is sold as a cleanup or refactor, minor otherwise.
|
|
34
|
+
- Feature logic in a shared module: feature-specific behaviour added to a
|
|
35
|
+
general-purpose util, a shared client, or a base class. The branch belongs in
|
|
36
|
+
the package that owns the concept. Name the owning layer in the fix.
|
|
37
|
+
- Dead code this diff orphans: when the diff replaces or reroutes something,
|
|
38
|
+
grep the worktree for remaining callers of what it superseded (the old helper,
|
|
39
|
+
the old component, a now-unreferenced constant, a flag that can no longer be
|
|
40
|
+
false). Enumerate what is now unreachable. NEVER propose a silent deletion:
|
|
41
|
+
the fix lists the orphans and asks the author to confirm removal.
|
|
42
|
+
Minor, or major when the dead path is still reachable from production code.
|
|
43
|
+
|
|
44
|
+
Only flag where duplication or complexity creates a real maintenance risk or
|
|
45
|
+
bug surface — not aesthetic preference.
|
|
46
|
+
|
|
47
|
+
When you flag a structural problem, name the move, not just the smell: replace a
|
|
48
|
+
chain of conditionals with a typed model or an explicit dispatcher, collapse
|
|
49
|
+
duplicate branches into one flow, separate orchestration from business logic,
|
|
50
|
+
move feature logic into the package that owns it, reuse the canonical helper
|
|
51
|
+
instead of a near-duplicate, delete a pass-through wrapper. Prefer the remedy
|
|
52
|
+
that removes moving pieces over one that spreads the same complexity around. A
|
|
53
|
+
finding that says "this is complex" without naming the restructuring is not
|
|
54
|
+
actionable: name the move or drop the finding.
|
|
55
|
+
|
|
56
|
+
Rules:
|
|
57
|
+
- Every finding must trace to a `+` line in the diff.
|
|
58
|
+
- Report file:line — description. No positive observations.
|
|
59
|
+
- Quote the verbatim offending line(s) — never paraphrased, never reconstructed
|
|
60
|
+
from memory — and give a concrete drop-in fix, or when the fix is
|
|
61
|
+
architectural, a minimal skeleton plus one sentence on what else must change.
|
|
62
|
+
- A finding you cannot quote and cannot fix is a finding you have not proven —
|
|
63
|
+
drop it instead.
|
package/plugins/olko-github-pr/skills/lekker-review/references/revmux/lenses/lekker-test-quality.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: whether tests actually catch bugs — mutation-slip analysis, mock smell, Teifi test conventions
|
|
3
|
+
---
|
|
4
|
+
## Lens: lekker-test-quality
|
|
5
|
+
|
|
6
|
+
Review the change with a strict focus on test quality. This is NOT about
|
|
7
|
+
coverage numbers — it is about whether the tests actually catch bugs.
|
|
8
|
+
|
|
9
|
+
Step 1 — Inventory the tests. Read the diff at `{{SCOPE}}`. List every test
|
|
10
|
+
file/spec added or modified. If no test files are in the diff, note that and
|
|
11
|
+
continue to axis (f) below.
|
|
12
|
+
|
|
13
|
+
Step 2 — For each changed test file, read the full file from `{{WORKDIR}}`.
|
|
14
|
+
|
|
15
|
+
Step 3 — Evaluate each of these axes:
|
|
16
|
+
|
|
17
|
+
a) Meaningful assertions vs. smoke tests
|
|
18
|
+
- Does the test verify a specific outcome, or just that no exception was
|
|
19
|
+
thrown / a function returned truthy?
|
|
20
|
+
- Are assertions on the correct thing? (e.g. checking the return value vs
|
|
21
|
+
a side-effect that is the actual goal of the operation)
|
|
22
|
+
- Are there tautological assertions that are always true regardless of the
|
|
23
|
+
implementation? (e.g. `expect(true).toBe(true)`,
|
|
24
|
+
`expect(x).toBeDefined()` when x is always defined by construction)
|
|
25
|
+
|
|
26
|
+
b) Condition coverage
|
|
27
|
+
- Is the happy path tested?
|
|
28
|
+
- Are failure / error paths tested? (invalid input, external API error,
|
|
29
|
+
empty collection, null/undefined, 0 or negative numbers, pagination edge
|
|
30
|
+
cases, missing env vars)
|
|
31
|
+
- For every new if/else or switch in the changed business logic, is each
|
|
32
|
+
branch exercised by at least one test case?
|
|
33
|
+
|
|
34
|
+
c) Regression tests
|
|
35
|
+
- If the change fixes a bug (the ticket mentions a bug, or the diff
|
|
36
|
+
contains "fix" language), is there a regression test that would have
|
|
37
|
+
caught the original bug? Flag if absent.
|
|
38
|
+
- If the change adds a new feature, are the known edge cases of that feature
|
|
39
|
+
tested?
|
|
40
|
+
|
|
41
|
+
d) Mutation-slip analysis (mental mutation testing)
|
|
42
|
+
For the most critical assertions in the test suite, ask: would a simple
|
|
43
|
+
mutation in the production code slip through undetected?
|
|
44
|
+
|
|
45
|
+
Consider these mutation classes:
|
|
46
|
+
- Off-by-one: `> N` changed to `>= N`
|
|
47
|
+
- Operator flip: `&&` to `||`, `===` to `!==`
|
|
48
|
+
- Missing null/undefined guard: remove a `?? default`
|
|
49
|
+
- Wrong variable: using `a` where `b` was intended
|
|
50
|
+
- Return-value swap: returning the wrong field from an object
|
|
51
|
+
- Early-return removed: a guard clause deleted
|
|
52
|
+
|
|
53
|
+
For each mutation class relevant to the changed business logic, determine
|
|
54
|
+
whether at least one test assertion would catch it. Summarize as a short
|
|
55
|
+
paragraph: "Mutations that would slip through: ..." or "No obvious
|
|
56
|
+
mutation-slip gaps found."
|
|
57
|
+
|
|
58
|
+
e) Test isolation and reliability
|
|
59
|
+
- Do tests share mutable state across cases without resetting between runs
|
|
60
|
+
(a beforeEach that does not clean up)?
|
|
61
|
+
- Are there tests that depend on execution order or global singletons?
|
|
62
|
+
- Could a test make a real network/DB call in CI (flaky)? The fix is a
|
|
63
|
+
simple injected fake at the boundary, not blanket module mocking — see
|
|
64
|
+
axis (g).
|
|
65
|
+
- Are async tests properly awaited? (floating promises, missing `await` on
|
|
66
|
+
`expect().resolves`, unhandled rejections)
|
|
67
|
+
|
|
68
|
+
f) Test-to-code ratio signal
|
|
69
|
+
If the diff adds > 50 lines of new business logic with zero new or modified
|
|
70
|
+
test files, flag it explicitly. Then check whether existing test files
|
|
71
|
+
already cover the new code paths:
|
|
72
|
+
```bash
|
|
73
|
+
find <workdir> -name "*.test.ts" -o -name "*.spec.ts" | \
|
|
74
|
+
xargs grep -l "<key changed symbol>" 2>/dev/null | head -5
|
|
75
|
+
```
|
|
76
|
+
Report whether existing coverage closes the gap or not.
|
|
77
|
+
|
|
78
|
+
g) Mock smell — test the behavior, not the way it's built
|
|
79
|
+
(House standard: Notion "To mock or not to mock" —
|
|
80
|
+
https://app.notion.com/p/teifi/To-mock-or-not-to-mock-36ff8ed0f7db80f09495d174e3b86cd6)
|
|
81
|
+
Flag tests coupled to *how* the code is built rather than *what* it does for
|
|
82
|
+
the user. For each smell, do NOT just criticize: give the concrete no-mock
|
|
83
|
+
refactor. The default fix is almost always the same shape — pull the logic
|
|
84
|
+
into a pure function (functional core) and test that directly, leaving a thin
|
|
85
|
+
shell covered by a few real integration tests.
|
|
86
|
+
|
|
87
|
+
- Mocking IO just to reach logic: a GraphQL/fetch/DB client mocked only so a
|
|
88
|
+
test can read a computed value back. It couples to the query shape AND the
|
|
89
|
+
markup while barely testing the logic, and silently rots as the real
|
|
90
|
+
dependency drifts from the mock. This applies just as much on the backend
|
|
91
|
+
as in a component: mocking a service client (e.g. `vi.mocked(epicorClient)`
|
|
92
|
+
returning a canned async generator/array) just to reach a reassembly loop
|
|
93
|
+
or a multi-stream join is the same smell as mocking `fetch` in a component.
|
|
94
|
+
→ Extract the computation into a pure function over plain data and test
|
|
95
|
+
that with literals (no mocks, no render, no mocked client). Cover
|
|
96
|
+
fetch-and-wire once with a real integration test. For streaming/paging
|
|
97
|
+
code specifically: a shared `collect()`/reassembly helper should be a
|
|
98
|
+
pure function of `AsyncGenerator<T[]> → Promise<T[]>` (or similar) fed a
|
|
99
|
+
plain fake generator in its own test — never a mocked client — and any
|
|
100
|
+
call-site logic that combines multiple streams (parallel joins, chunked
|
|
101
|
+
batching) should be its own pure function tested the same way.
|
|
102
|
+
- Spying on calls / asserting call shape: `toHaveBeenCalledWith`,
|
|
103
|
+
`toHaveBeenCalledTimes`, a `vi.fn()` used as a probe. Asserts *how* a
|
|
104
|
+
function was called, freezing batching/page-size/call-count in place even
|
|
105
|
+
when the output is identical.
|
|
106
|
+
→ Assert the output, never the call log. If a boundary is genuinely
|
|
107
|
+
needed, inject a simple fake (a plain function returning canned data),
|
|
108
|
+
not a spy.
|
|
109
|
+
- Mocking a component to read its props back (re-emitting props as `data-*`
|
|
110
|
+
attributes, then asserting on them): the assertions are about the mock, and
|
|
111
|
+
break on a component swap or prop rename that changes nothing a user sees.
|
|
112
|
+
→ Pull the logic (pagination, display state) into a pure function over
|
|
113
|
+
plain values — e.g. `paginate(items, page, pageSize)` — and assert the
|
|
114
|
+
value it returns.
|
|
115
|
+
- Mocking a query hook (`useQuery` / a `use-X` hook) to hand a component
|
|
116
|
+
canned data: re-tests React Query's own plumbing and couples to the hook's
|
|
117
|
+
return shape.
|
|
118
|
+
→ Keep the `queryFn` as IO (integration-tested); move the transform into a
|
|
119
|
+
pure function wired through React Query's `select` option, and unit-test
|
|
120
|
+
that pure function with plain data.
|
|
121
|
+
- Asserting implementation details: that a component is memoized, that a
|
|
122
|
+
specific child renders, that work happens through a fixed sequence of
|
|
123
|
+
calls.
|
|
124
|
+
→ Delete the assertion; assert the user-visible behavior instead.
|
|
125
|
+
|
|
126
|
+
When a mock IS the right call, do NOT flag it: a real IO boundary that must
|
|
127
|
+
be exercised where a fake is impractical, non-determinism that must be pinned
|
|
128
|
+
(time, randomness, injected network failure), or a dependency that genuinely
|
|
129
|
+
cannot run in the test environment. The tell for a good mock: the behavior
|
|
130
|
+
under test only exists because the boundary did something (e.g. a retry
|
|
131
|
+
banner that appears only when the fetch rejects). Even then, prefer an
|
|
132
|
+
injected simple fake over a module-level spy, and assert what the user sees.
|
|
133
|
+
|
|
134
|
+
h) Teifi test conventions
|
|
135
|
+
Read `{{PROFILE}}` and apply its §4 (test conventions) to every test file in
|
|
136
|
+
the diff: `__tests__/` placement beside the source, `.test.ts` vs
|
|
137
|
+
`.test.tsx`, English-sentence test names, `describe` nesting <= 2,
|
|
138
|
+
`afterEach(cleanup)`, `retry: false` on the test QueryClient (major — its
|
|
139
|
+
absence hangs the suite on a failing query), the getByRole → getByLabelText
|
|
140
|
+
→ getByText → getByTestId priority, module-boundary mocking, `safeParse` for
|
|
141
|
+
Zod schemas, and the typed `it.each` matrix with AC tags plus the
|
|
142
|
+
exhaustiveness assertion whenever behaviour depends on 3+ independent inputs.
|
|
143
|
+
A `vi.mock('@lib/common/…')` inside `common/` is major, not style: the
|
|
144
|
+
alias resolves only from `web/`, so the mock silently does nothing and the
|
|
145
|
+
test passes against the real module. Quote the line, give the relative path
|
|
146
|
+
as the fix.
|
|
147
|
+
A comment explaining WHY a test exists (a captured past bug, a subtle
|
|
148
|
+
contract) is sanctioned — never flag it as over-commenting.
|
|
149
|
+
|
|
150
|
+
Report each per-line issue as:
|
|
151
|
+
`test-file:line — <concise description of the gap or weakness>`
|
|
152
|
+
|
|
153
|
+
For every mock-smell finding from axis (g), append the no-mock fix on the next
|
|
154
|
+
line as `→ Fix: <pure-function / simple-fake refactor in one line>`. A
|
|
155
|
+
criticism without a fix is incomplete.
|
|
156
|
+
|
|
157
|
+
Report the mutation-slip analysis as a single paragraph under a
|
|
158
|
+
"**Mutation-slip risk:**" heading — not as line items.
|
|
159
|
+
|
|
160
|
+
Rules:
|
|
161
|
+
- Every per-line finding must trace to test code in the diff, OR to business
|
|
162
|
+
logic added in the diff that has no test coverage at all.
|
|
163
|
+
- Report problems only. No praise for tests that meet the bar.
|
|
164
|
+
- If no test files are changed AND no existing tests cover the new code paths,
|
|
165
|
+
report: "No test coverage for new code paths."
|
|
166
|
+
- Quote the verbatim offending line(s) — never paraphrased, never reconstructed
|
|
167
|
+
from memory. When the finding IS the absence of a test, there is no test line
|
|
168
|
+
to quote: quote the untested production line(s) from the diff instead, and
|
|
169
|
+
give the test that should exist as the fix. Never drop a "no coverage"
|
|
170
|
+
finding just because nothing bad is written down — absence is the finding.
|
|
171
|
+
- A finding you cannot quote and cannot fix (outside the no-coverage case
|
|
172
|
+
above) is a finding you have not proven — drop it instead.
|