-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathyama.config.yaml
More file actions
237 lines (216 loc) · 13.1 KB
/
Copy pathyama.config.yaml
File metadata and controls
237 lines (216 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# =============================================================================
# Yama PR Review configuration
# =============================================================================
# Consumed by the @juspay/yama GitHub Action (.github/workflows). Auto-discovered
# from the repo root (search order: yama.config.yaml → config/yama.config.yaml →
# .yama/config.yaml) and also passed explicitly via the workflow `config-path`.
#
# WHERE THE REVIEW RULES COME FROM
# Yama automatically loads, from this repo root, if present:
# • CLAUDE.md — your authoritative engineering rules
# • CONTRIBUTING.md — contribution + commit conventions
# and injects them into the reviewer as <project-rules>. Keep repo-specific
# rules THERE; this file tunes WHAT the reviewer focuses on and WHAT blocks a
# merge.
#
# PROVIDER / MODEL ARE INTENTIONALLY NOT SET HERE
# They are controlled by the workflow inputs (ai-provider / ai-model), exposed
# as AI_PROVIDER / AI_MODEL env. Yama merges this file OVER those env values, so
# setting ai.provider / ai.model here would silently override the workflow.
# =============================================================================
version: 2
configType: "yama"
display:
showBanner: false
streamingMode: false
verboseToolCalls: false
showAIThinking: false
ai:
# provider/model OMITTED on purpose — controlled by the workflow.
temperature: 0.2 # deterministic, review-grade output
timeout: "15m"
retryAttempts: 3
# The explore worker reads project rules (CLAUDE.md / CONTRIBUTING.md) and
# codebase context. Keep enabled so the reviewer sees your repo's rules.
explore:
enabled: true
timeout: "5m"
cacheResults: true
# GitHub MCP only. Jira/Bitbucket off so no extra credentials are needed. Yama's
# built-in denylist already blocks repo-mutating GitHub tools; this is a
# read + review-comment workflow.
mcpServers:
github:
enabled: true
# transport defaults to "http" (hosted GitHub MCP at api.githubcopilot.com).
jira:
enabled: false
review:
enabled: true
# High-level instruction the reviewer follows for THIS repo.
workflowInstructions: |
You are reviewing a pull request for @juspay/rescript-bindgen — a
DETERMINISTIC TypeScript(.d.ts) → ReScript binding generator. There is NO AI
at runtime: it drives the TypeScript type-checker (src/extract.mjs), builds an
IR, and emits ReScript 12 (src/emit.mjs) via a FIXED mapping table; src/cli.mjs
and src/resolve.mjs wire input resolution. Same input must always produce the
same output, with no Obj.magic / @unwrap / bare %identity in generated code.
Treat the <project-rules> block (this repo's CLAUDE.md / CONTRIBUTING.md and,
by reference, docs/TYPE_MAPPING.md) as the AUTHORITATIVE contract. When a
mapping or emit change conflicts with the documented rules ("no unsafe casts",
"flag, don't fake", unknown→JSON.t, multi-type→@unboxed/opaque, the
maintenance loop), cite the exact rule.
Do NOT duplicate the mechanical checks CI already runs and BLOCKS on
(.github/workflows/ci.yml): the smoke + golden snapshot diff (npm test), the
ReScript-12 golden compile (npm run test:compile), and the fixture-guard (a PR
touching src/extract|emit|resolve.mjs must also touch test/golden/cases/ or
docs/TYPE_MAPPING.md). Do not re-flag formatting, snapshot drift, or a missing
fixture — the build already fails on those. The benchmark workflow (real
pinned packages) is a separate opt-in gate.
Focus instead on what those checks cannot catch: whether the EMITTED ReScript
is actually correct and type-safe per docs/TYPE_MAPPING.md, whether new logic
handles edge-case .d.ts inputs (overloads, intersections, Omit/Pick, generics,
indexed access, undiscriminable unions), determinism (no Map/Set iteration
order, Date, or filesystem ordering leaking into output), and whether an
intentional output change silently regresses previously clean bindings.
CRITICAL LENS — compilation is NOT soundness. The CI gates (npm test, golden
compile, bench) prove the output COMPILES and MATCHES the committed baseline; they
do NOT prove the emitted types are sound. An unsound `'a` external always compiles;
a fake `string` hidden inside a shared *Types.res entry compiles and may not even
appear in the report (imperfection bucketing stops at a typeRef boundary, so a fake
behind a shared type reference does not bubble to the prop). Judge each `'a`, each
@unboxed / views-module arm, and each shared-type field by REASONING about runtime
values and data-flow direction — never by whether it builds.
POLARITY — for every emitted type ask who PRODUCES the value: the consumer (INPUT —
a prop, a method/constructor param, the return of a callback the consumer implements)
or the library (OUTPUT — a getter/method return, or a callback PARAM the consumer
receives). A type variable `'a` is sound ONLY when it appears in a consumer-SUPPLIED
input (it round-trips); an `any`/unmodellable type in an OUTPUT position must stay a
flagged placeholder, never a free `'a` (which unifies with anything).
Be specific and constructive: every comment cites the exact file+line, the
concrete risk, and a suggested fix. Match the terse, heavily-commented style of
src/*.mjs. Only block per <blocking-criteria>; otherwise leave inline comments
and approve.
# What the review prioritises for THIS repo. Priority is advisory; see
# blockingCriteria below for what actually fails the check.
focusAreas:
- name: "Mapping correctness & type safety of emitted ReScript"
priority: "CRITICAL"
description: |
The whole product is the generated output, so a wrong type maps real
users onto a wrong binding. Verify each TS→ReScript change against
docs/TYPE_MAPPING.md (the contract):
- NO unsafe casts in emitted code: no Obj.magic, @unwrap, or bare
%identity. The only allowed %identity is the zero-cost from*/as*
accessor of an opaque module, or the documented <prop>Fn render-prop
wrapper (#46).
- "Flag, don't fake": an unmodellable type must emit a `string`
placeholder + ⚪/🔍/🛑 bucket comment, never a plausible-but-wrong type.
- unknown → JSON.t (not a type variable); 'a only for a genuine
round-tripping generic; multi-type props → @unboxed untagged variant
(distinct runtime tags) or an opaque module; `any` is a defect, never
silently typed.
- All-cases-or-flag: every TS variant of a union / overload must be
reachable in the emitted output, or the WHOLE prop is flagged — never
silently drop one (e.g. two string literals colliding on one constructor
ident like `'trap-focus'`/`'trapFocus'`, or a dropped overload arm, both
still compile).
- name: "Determinism & reproducibility"
priority: "CRITICAL"
description: |
Same input MUST yield byte-identical output on every run and machine.
Flag anything that can leak nondeterminism into emitted code or reports:
unordered Map/Set/object-key iteration, Date/time, randomness, absolute
paths, locale-sensitive sort, or filesystem readdir order used without an
explicit stable sort.
- name: "Edge-case .d.ts input handling in the extract/resolve pipeline"
priority: "MAJOR"
description: |
extract.mjs / resolve.mjs drive the TS type-checker over arbitrary real
packages. Look for crashes, silent drops, or wrong IR on hard inputs:
overloads, intersections, Omit/Pick/Partial, imported enums,
RefAttributes, generics, indexed-access, undiscriminable unions, missing
or malformed declaration entries. Prefer an honest flag over a throw or a
bad emit.
- name: "Backward compatibility of generated bindings"
priority: "MAJOR"
description: |
An intentional change to one mapping can silently regress previously
clean bindings (the 50+ benchmarked packages, ~93% clean). When golden or
baseline output changes, confirm the diff is the intended improvement and
does NOT downgrade ready bindings to loose/review/broken or rename public
labels/variant cases without cause.
- name: "Process integrity of the mapping change"
priority: "MINOR"
description: |
Per CLAUDE.md the maintenance loop is a unit: a new/changed mapping needs
a docs/TYPE_MAPPING.md row, a self-contained fixture under
test/golden/cases/, and its regenerated golden — together. (CI's
fixture-guard already blocks the missing-fixture case; here, sanity-check
that the fixture actually exercises the new behaviour rather than just
existing to satisfy the guard.)
# What BLOCKS a merge. ONLY these escalate the decision to BLOCKED (failing the
# check in enforcing mode). Kept tight — everything else is an advisory inline
# comment. CI already blocks snapshot drift, compile failures, and the
# fixture-guard, so those are intentionally NOT repeated here.
blockingCriteria:
- condition: "Any hardcoded secret, API key, token, password or credential committed in source (non-test, non-example)"
action: "BLOCK"
reason: "Security: secrets must never be committed"
- condition: "Any CRITICAL severity security vulnerability in the tooling itself (command injection via package spec / path, arbitrary file write outside --out, unsafe install of untrusted input)"
action: "BLOCK"
reason: "Critical security risk in the generator"
- condition: "Generated ReScript introduces an unsafe cast that violates the contract — Obj.magic, @unwrap, or a bare %identity outside the two documented allowances (opaque-module from*/as* accessor, render-prop <prop>Fn wrapper)"
action: "BLOCK"
reason: "Breaks the core 'no unsafe casts' guarantee in docs/TYPE_MAPPING.md / CLAUDE.md"
- condition: "Generated ReScript introduces a type variable ('a) that does NOT round-trip — it appears only in OUTPUT positions (a getter/method return, or a callback param the consumer receives) and never in a consumer-supplied input, so it unifies with anything (e.g. an `any` field surfacing as `routes(): RouterRoute<'a>[]`)"
action: "BLOCK"
reason: "An output-only 'a is an unsound cast (rule #4: a type var is only for a genuine round-tripping generic) — strictly worse than a flagged placeholder, and it compiles, so no other gate catches it"
- condition: "A fake / plausible-but-wrong type is emitted UNFLAGGED inside a shared *Types.res entry (an @unboxed arm, a record field, or a views-module member) — no ⚪/🔍/🛑 bucket comment, because the prop only holds a typeRef and the imperfection does not bubble up. ONLY block after VERIFYING against the original .d.ts that the TS type was genuinely richer (e.g. a union, object, or callback) and got silently narrowed to `string` — a field that is legitimately `string` in TypeScript is NOT a violation"
action: "BLOCK"
reason: "'Flag, don't fake' must hold even when the fake hides behind a shared type reference and the report shows the prop as usable — but only when the source .d.ts confirms the type was actually narrowed (do not block genuine string fields)"
- condition: "A change makes generated output nondeterministic (same input → differing output across runs) — e.g. unordered iteration, time, or randomness reaching emitted code"
action: "BLOCK"
reason: "Determinism is a core product guarantee; nondeterministic output is unshippable"
# Generated / vendored / binary files are skipped. NOTE: golden .res files under
# test/golden/cases/*/expected/ are intentionally NOT excluded — they ARE the
# output and are the point of review; the AI should read them to judge the diff.
excludePatterns:
- "*.lock"
- "package-lock.json"
- "*.map"
- "*.svg"
# Generated artifacts / scratch caches (per .gitignore conventions):
- "generated/**" # default --out dir for ad-hoc local runs
- "coverage/**"
# ReScript compile sandbox build output (not source):
- "test/sandbox/lib/**"
- "**/*.res.mjs" # ReScript-compiled JS
- "**/*.res.js"
# Benchmark scratch + heavy committed baselines (reviewed via the bench gate,
# not file-by-file): keep packages.json reviewable, skip the work dir.
- "benchmark/.work/**"
# Vendored stubs inside fixtures (third-party .d.ts copied in for offline cases):
- "test/golden/cases/**/node_modules/**"
contextLines: 3
maxFilesPerReview: 100
fileAnalysisTimeout: "2m"
# Review-only: never rewrite PR descriptions (also forced by the workflow input
# skip-description-enhance: "true").
descriptionEnhancement:
enabled: false
# Stateless reviewer: no memory-bank dir, no knowledge-base file, no commits back
# to the repo (needs no write access beyond posting the review).
memoryBank:
enabled: false
knowledgeBase:
enabled: false
memory:
enabled: false
autoCommit: false
# Cost / runtime guardrails.
performance:
maxReviewDuration: "15m"
tokenBudget:
maxTokensPerReview: 800000
warningThreshold: 600000