@jjchill/probity-rules 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/GLOSSARY.template.md +33 -0
  3. package/README.md +96 -0
  4. package/dist/index.d.ts +20 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +20 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/presets/js.d.ts +63 -0
  9. package/dist/presets/js.d.ts.map +1 -0
  10. package/dist/presets/js.js +112 -0
  11. package/dist/presets/js.js.map +1 -0
  12. package/dist/presets/kmp.d.ts +27 -0
  13. package/dist/presets/kmp.d.ts.map +1 -0
  14. package/dist/presets/kmp.js +253 -0
  15. package/dist/presets/kmp.js.map +1 -0
  16. package/dist/presets/kotlin.d.ts +40 -0
  17. package/dist/presets/kotlin.d.ts.map +1 -0
  18. package/dist/presets/kotlin.js +172 -0
  19. package/dist/presets/kotlin.js.map +1 -0
  20. package/dist/presets/swift.d.ts +10 -0
  21. package/dist/presets/swift.d.ts.map +1 -0
  22. package/dist/presets/swift.js +285 -0
  23. package/dist/presets/swift.js.map +1 -0
  24. package/dist/rules/acceptance-language.d.ts +95 -0
  25. package/dist/rules/acceptance-language.d.ts.map +1 -0
  26. package/dist/rules/acceptance-language.js +443 -0
  27. package/dist/rules/acceptance-language.js.map +1 -0
  28. package/dist/rules/gates.d.ts +125 -0
  29. package/dist/rules/gates.d.ts.map +1 -0
  30. package/dist/rules/gates.js +285 -0
  31. package/dist/rules/gates.js.map +1 -0
  32. package/dist/rules/kotlin.d.ts +323 -0
  33. package/dist/rules/kotlin.d.ts.map +1 -0
  34. package/dist/rules/kotlin.js +722 -0
  35. package/dist/rules/kotlin.js.map +1 -0
  36. package/dist/rules/ports-and-adapters.d.ts +86 -0
  37. package/dist/rules/ports-and-adapters.d.ts.map +1 -0
  38. package/dist/rules/ports-and-adapters.js +366 -0
  39. package/dist/rules/ports-and-adapters.js.map +1 -0
  40. package/dist/rules/scoping.d.ts +68 -0
  41. package/dist/rules/scoping.d.ts.map +1 -0
  42. package/dist/rules/scoping.js +93 -0
  43. package/dist/rules/scoping.js.map +1 -0
  44. package/dist/rules/spec-test-parity.d.ts +164 -0
  45. package/dist/rules/spec-test-parity.d.ts.map +1 -0
  46. package/dist/rules/spec-test-parity.js +456 -0
  47. package/dist/rules/spec-test-parity.js.map +1 -0
  48. package/dist/rules/swift.d.ts +50 -0
  49. package/dist/rules/swift.d.ts.map +1 -0
  50. package/dist/rules/swift.js +50 -0
  51. package/dist/rules/swift.js.map +1 -0
  52. package/dist/rules/ubiquitous-language.d.ts +36 -0
  53. package/dist/rules/ubiquitous-language.d.ts.map +1 -0
  54. package/dist/rules/ubiquitous-language.js +140 -0
  55. package/dist/rules/ubiquitous-language.js.map +1 -0
  56. package/dist/scripts/scope-report.d.ts +3 -0
  57. package/dist/scripts/scope-report.d.ts.map +1 -0
  58. package/dist/scripts/scope-report.js +184 -0
  59. package/dist/scripts/scope-report.js.map +1 -0
  60. package/kiro/README.md +20 -0
  61. package/kiro/kiro-agent.template.json +45 -0
  62. package/kiro/kiro-transcript-to-claude.py +183 -0
  63. package/kiro/probity-kiro-translate.py +132 -0
  64. package/kiro/probity-kiro.sh +87 -0
  65. package/kiro/skill-activation-forced-eval.sh +45 -0
  66. package/package.json +68 -0
  67. package/probity.config.kmp.ts +40 -0
  68. package/probity.config.kotlin.ts +37 -0
  69. package/probity.config.swift.ts +38 -0
  70. package/probity.config.ts +43 -0
  71. package/scripts/spec-parity.mjs +345 -0
@@ -0,0 +1,443 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ const DEFAULT_MAX_GLOSSARY_CHARS = 8000;
4
+ const STRICT_VOCABULARY = `### Vocabulary — strict mode (overrides the leniency above)
5
+
6
+ This project enforces "the glossary conversation happens first": when
7
+ a glossary is provided, spec content that introduces a domain concept
8
+ with NO glossary entry IS a violation. Name the missing term(s) in
9
+ the reason so the entry can be added before the spec lands. Judge
10
+ concepts, not words — articles, generic verbs, and quantities need no
11
+ entry; nouns that carry domain meaning do.
12
+
13
+ `;
14
+ // reason comes FIRST in the shape: an autoregressive model commits to
15
+ // each field in order, so kind-first forces the verdict before the
16
+ // analysis — observed live producing a "violation" whose reason
17
+ // reasoned its way to "Passing." Reason-first lets the model conclude,
18
+ // then label.
19
+ const RESPONSE_SPEC = `## Response format
20
+
21
+ Respond with a single JSON object of exactly this shape:
22
+ {"reason":"<your analysis>","kind":"pass"|"violation"}
23
+ Write reason FIRST and set kind to the conclusion your reason reached —
24
+ the two must agree. Keep reason brief on a clear pass ("" is fine).
25
+ Return JSON only. No prose, no code fences.`;
26
+ const PROCESS_INSTRUCTIONS = `## Role
27
+
28
+ You are an acceptance-specification validator. Judge whether the
29
+ pending write keeps executable specifications in the language of the
30
+ problem domain, per the rules below.
31
+
32
+ ## Inputs
33
+
34
+ You will see up to four inputs:
35
+
36
+ 1. "Ubiquitous language glossary" (optional) — the project's agreed
37
+ domain vocabulary.
38
+ 2. "Current file content" — what's on disk right now at the file the
39
+ agent is about to write. May be a parenthesized marker (e.g.
40
+ \`(file does not exist)\`).
41
+ 3. "Pending action" — the file path and what the agent is about to
42
+ write. Content may be raw file text or a patch/diff.
43
+
44
+ ## What you judge
45
+
46
+ Judge only the specification content this write adds or changes, not
47
+ pre-existing text it leaves untouched. This rule is scoped to
48
+ specification files (layer 1 of the four-layer model). If the file is
49
+ clearly NOT a test-case layer — it is a DSL implementation, a protocol
50
+ driver, a step-definition file that only parses and delegates, or
51
+ plain production code — pass: those layers are supposed to know about
52
+ UI mechanics and protocols.
53
+
54
+ A transient file state (unresolved import, half-finished scenario) is
55
+ never itself a violation.
56
+
57
+ A block recorded earlier in the session is a past verdict, not a rule.
58
+ Re-derive your judgment from the rules below. When the user tells you
59
+ in the session to let this change through, treat it as authoritative
60
+ and pass.`;
61
+ const DEFAULT_LANGUAGE_RULES = `## Specification language rules
62
+
63
+ An acceptance test is an executable specification: a concrete example,
64
+ in the language of the problem domain, that demonstrates a user's need
65
+ is met. It states WHAT the system does for an external user and says
66
+ nothing about HOW the system works.
67
+
68
+ ### The Language Test
69
+
70
+ The least technical person who understands the problem domain must be
71
+ able to read the specification and confirm it says what they want.
72
+ Block spec content that mentions implementation mechanics:
73
+
74
+ - UI mechanics: clicking, typing into fields, pages, screens,
75
+ buttons, menus, CSS selectors, element IDs, XPath
76
+ - Protocol mechanics: URLs, endpoints, HTTP verbs, status codes,
77
+ JSON/XML payloads, headers, cookies
78
+ - Persistence mechanics: tables, rows, columns, SQL, cache keys
79
+ - Named internal services, queues, or modules
80
+
81
+ The fix is always the same: state the outcome in domain terms and push
82
+ the mechanics down into the DSL or a protocol driver.
83
+
84
+ ### One standard across artifacts
85
+
86
+ A Markdown/Gherkin scenario and the test case that claims it are both
87
+ layer 1 and are held to the SAME standard: wording that would violate
88
+ in a test-case step ("the backend rejects creation") violates in a
89
+ \`## Scenario:\` step too, and vice versa. Never pass mechanism
90
+ language in a spec document on the grounds that it is prose — the
91
+ spec is the source that tests transcribe, so a leak passed there
92
+ resurfaces in every claiming test. Named internal actors — "the
93
+ backend", "the server", "the gateway", "the database", "the API",
94
+ "a queue", "the store" — are mechanism unless the glossary records
95
+ them as domain concepts: state the condition as the user experiences
96
+ it ("creation fails", "the parcel cannot be registered right now")
97
+ and push what failed and why into the DSL, driver, or stub
98
+ programming.
99
+
100
+ ### Structure
101
+
102
+ - Each specification asserts a single outcome. Block scenarios with
103
+ long When/Then chains asserting many unrelated outcomes.
104
+ - Express specifications as outcomes ("should ...", "is granted
105
+ access"), not procedures.
106
+ - No sleeps or fixed waits in specs — a delay is a race condition
107
+ with a timer on it; drivers poll for the concluding event.
108
+ - Test-case code talks only to the DSL. Block spec-layer code that
109
+ reaches the system directly (HTTP clients, page objects, database
110
+ handles imported into the spec file).
111
+
112
+ ### Vocabulary
113
+
114
+ When a glossary is provided, use its terms verbatim — one term per
115
+ concept. Block spec content that names a domain concept with a term
116
+ that conflicts with the glossary (a synonym or a redefinition). A
117
+ concept the glossary simply doesn't cover yet is NOT a violation on
118
+ its own; mention it in the reason only alongside a real violation.
119
+
120
+ %STRICT_VOCABULARY%### Judgment bar
121
+
122
+ Block on clear mechanics leaking into a specification. Domain terms
123
+ that happen to sound technical (the domain of a deployment tool
124
+ includes "server"; a payments domain includes "card") are not
125
+ violations — judge against the problem domain, not a banned-word list.
126
+ Grammatical tense and phrasing preferences are not mechanism: a
127
+ future- or conditional-tense precondition ("creation will fail",
128
+ "the parcel is going to be rejected") is equivalent to its
129
+ present-tense form and never blocks on its own — and when a step does
130
+ violate, name only the offending phrase, not neighboring wording that
131
+ merely reads awkwardly. When genuinely ambiguous, pass.`;
132
+ const PRECONDITION_PROCESS_INSTRUCTIONS = `## Role
133
+
134
+ You are an acceptance-test precondition validator. Judge whether the
135
+ pending write keeps scenario preconditions CONTROLLED BY THE TEST
136
+ rather than inherited from the ambient environment.
137
+
138
+ ## Inputs
139
+
140
+ You will see two inputs:
141
+
142
+ 1. "Current file content" — what's on disk right now at the file the
143
+ agent is about to write. May be a parenthesized marker (e.g.
144
+ \`(file does not exist)\`).
145
+ 2. "Pending action" — the file path and what the agent is about to
146
+ write. Content may be raw file text or a patch/diff.
147
+
148
+ ## What you judge
149
+
150
+ Judge only the change this write makes. The scope is layer 3 and the
151
+ test-control layer of the four-layer acceptance model: protocol
152
+ drivers, port fakes, fixture wiring, and acceptance composition
153
+ roots. A transient file state (unresolved symbol, half-finished
154
+ multi-step change) is never itself a violation — a driver method may
155
+ legitimately be written before the fixture it will set, so block only
156
+ when the write itself declares the environmental dependency (a no-op
157
+ body presented as done, a comment saying the environment covers it,
158
+ or the removal of existing control).
159
+
160
+ A block recorded earlier in the session is a past verdict, not a
161
+ rule. Re-derive your judgment from the rules below. When the user
162
+ tells you in the session to let this change through, treat it as
163
+ authoritative and pass.`;
164
+ const PRECONDITION_RULES = `## Controlled-precondition rules
165
+
166
+ Every Given of an executable specification must be ESTABLISHED by the
167
+ test — through a fixture key, launch environment, programmed stub, or
168
+ substituted port — never satisfied by whatever the environment
169
+ happens to do (no network, no credentials, empty state, an
170
+ unreachable backend).
171
+
172
+ Block:
173
+
174
+ - **The no-op precondition driver.** A driver method whose name or
175
+ doc comment states a precondition ("whose sign-in will fail",
176
+ "with an expired subscription", "while offline") but whose body
177
+ establishes no state: it only navigates, launches, or asserts,
178
+ with nothing that configures a fixture, stub, launch environment,
179
+ or fake. Especially when a comment admits the environment covers
180
+ it ("fails in the simulator anyway").
181
+ - **Deleting control because the test is green without it.** A
182
+ write that removes fixture wiring, a fake registration, or a
183
+ fixture key on the grounds that the scenario already passes. The
184
+ correct move is the inverse scenario: the spec for the other side
185
+ of the same port (the success path when only failure happens for
186
+ free) is genuinely red and legitimately drives the fixture; the
187
+ original scenario then adopts the explicit fixture under green.
188
+ Name this route in the reason when blocking.
189
+
190
+ Do NOT block:
191
+
192
+ - Driver methods whose names state actions or observations rather
193
+ than preconditions.
194
+ - Preconditions genuinely established elsewhere and visibly reached
195
+ from this body (a shared helper that sets the fixture, a base
196
+ launch method the diff calls into).
197
+ - Refactors that move control without removing it.
198
+ - Removal of control the user has explicitly directed in the
199
+ session.
200
+
201
+ When genuinely ambiguous — the method name is vague, or you cannot
202
+ tell whether a called helper establishes the state — pass.`;
203
+ function formatBefore(before) {
204
+ switch (before.kind) {
205
+ case 'present':
206
+ return before.content;
207
+ case 'absent':
208
+ return '(file does not exist)';
209
+ case 'unknown':
210
+ return '(current file content unavailable)';
211
+ }
212
+ }
213
+ function truncate(text, maxChars) {
214
+ if (text.length <= maxChars)
215
+ return text;
216
+ return `${text.slice(0, maxChars)}\n(...glossary truncated...)`;
217
+ }
218
+ function buildPrompt(rules, glossary, before, action) {
219
+ const sections = [PROCESS_INSTRUCTIONS, rules];
220
+ if (glossary) {
221
+ sections.push(`## Ubiquitous language glossary\n\n${glossary}`);
222
+ }
223
+ sections.push(`## Current file content\n\n${formatBefore(before)}`);
224
+ sections.push(`## Pending action\n\nFile: ${action.path}\n\n${action.content}`);
225
+ sections.push(RESPONSE_SPEC);
226
+ return sections.join('\n\n');
227
+ }
228
+ // Deterministic screen used by the fast-path: mechanism words that
229
+ // domain-language test steps should never contain. Deliberately
230
+ // broad — a false hit just falls through to the AI validator.
231
+ const MECHANISM_SCREEN = /\b(?:backend|server|gateway|database|sql|endpoint|https?|url|api|queue|json|xml|payload|click|button|css|xpath|selector|cookie|header|mock)\b/i;
232
+ const KOTLIN_CALL_KEYWORDS = new Set([
233
+ 'fun',
234
+ 'if',
235
+ 'when',
236
+ 'while',
237
+ 'for',
238
+ 'catch',
239
+ 'return',
240
+ 'super',
241
+ 'this',
242
+ 'Test',
243
+ ]);
244
+ const DECLARATION = /(?:fun|class|object|interface)\s+`?([A-Za-z_]\w*)/g;
245
+ function declaredNames(content, out) {
246
+ for (const match of content.matchAll(DECLARATION))
247
+ out.add(match[1]);
248
+ }
249
+ /**
250
+ * Wraps `enforceAcceptanceLanguage` so the most common spec-layer
251
+ * write — a Kotlin test file gaining exactly one new `@Test` whose
252
+ * added lines only call vocabulary that already exists in the
253
+ * suite's DSL/Robot/driver files — passes deterministically, with no
254
+ * model call. Rationale: when two rule scopes overlap (the TDD rule's
255
+ * Kotlin fast-path plus this rule on `acceptance/**`), a single-test
256
+ * write otherwise still pays one AI call despite being advertised as
257
+ * free.
258
+ *
259
+ * The fast-path is conservative on three axes; failing any one falls
260
+ * through to the wrapped AI rule (it can only skip work, never
261
+ * block):
262
+ * 1. the write must add exactly one `@Test`;
263
+ * 2. no added line may contain mechanism vocabulary
264
+ * (backend/server/database/url/click/... — the deterministic
265
+ * screen errs broad);
266
+ * 3. every identifier the added lines call must already be declared
267
+ * in this file or a sibling `.kt` file in the same directory —
268
+ * a brand-new DSL step name is exactly what the validator should
269
+ * judge.
270
+ *
271
+ * Markdown/Gherkin spec writes never fast-path — prose is where the
272
+ * Language Test earns its keep.
273
+ */
274
+ export function withAcceptanceLanguageFastPath(rule) {
275
+ const wrapped = async function acceptanceLanguageFastPath(action, ctx) {
276
+ if (action.kind !== 'write' || !/\.kt$/.test(action.path)) {
277
+ return rule(action, ctx);
278
+ }
279
+ const before = await ctx?.readFile?.(action.path);
280
+ if (!before || before.kind === 'unknown')
281
+ return rule(action, ctx);
282
+ const beforeText = before.kind === 'present' ? before.content : '';
283
+ const testDelta = (action.content.match(/@Test\b/g)?.length ?? 0) -
284
+ (beforeText.match(/@Test\b/g)?.length ?? 0);
285
+ if (testDelta !== 1)
286
+ return rule(action, ctx);
287
+ const beforeLines = new Set(beforeText.split('\n').map((line) => line.trim()));
288
+ const added = action.content
289
+ .split('\n')
290
+ .map((line) => line.trim())
291
+ .filter((line) => line.length > 0 && !beforeLines.has(line));
292
+ if (added.some((line) => MECHANISM_SCREEN.test(line))) {
293
+ return rule(action, ctx);
294
+ }
295
+ const known = new Set();
296
+ declaredNames(action.content, known);
297
+ try {
298
+ for (const entry of readdirSync(dirname(action.path))) {
299
+ if (!entry.endsWith('.kt'))
300
+ continue;
301
+ declaredNames(readFileSync(join(dirname(action.path), entry), 'utf8'), known);
302
+ }
303
+ }
304
+ catch {
305
+ return rule(action, ctx);
306
+ }
307
+ for (const line of added) {
308
+ for (const call of line.matchAll(/([A-Za-z_]\w*)\s*\(/g)) {
309
+ const name = call[1];
310
+ if (KOTLIN_CALL_KEYWORDS.has(name))
311
+ continue;
312
+ if (!known.has(name))
313
+ return rule(action, ctx);
314
+ }
315
+ }
316
+ return { kind: 'pass', notes: [{ kind: 'fast-path' }] };
317
+ };
318
+ Object.defineProperty(wrapped, 'name', {
319
+ value: `acceptanceLanguageFastPath(${rule.name || 'rule'})`,
320
+ });
321
+ return wrapped;
322
+ }
323
+ /**
324
+ * AI-validated enforcement of the `acceptance-testing` skill's
325
+ * Language Test: executable specifications stay in domain language —
326
+ * no UI mechanics, protocols, or persistence details — assert a
327
+ * single outcome, and (when a glossary is supplied) use the
328
+ * ubiquitous language verbatim.
329
+ *
330
+ * Applies to: write actions. Scope it with a `{ files, rules }` block
331
+ * to the spec layer only (e.g. `specs/**`, `**\/*.feature`) — the DSL
332
+ * and protocol-driver layers are supposed to contain the mechanics
333
+ * this rule blocks, and every matching write costs an AI call.
334
+ *
335
+ * @param options.glossaryPath — absolute path to the project's
336
+ * ubiquitous-language glossary (see the `ubiquitous-language`
337
+ * skill). Resolve it in the config file, e.g.
338
+ * `fileURLToPath(new URL('./docs/GLOSSARY.md', import.meta.url))`.
339
+ * When set and readable, the glossary is included in the
340
+ * validator's prompt and vocabulary conflicts become violations.
341
+ * @param options.requireGlossaryEntry — strict vocabulary mode: when
342
+ * true (and a glossary is supplied and readable), a domain concept
343
+ * in spec content with no glossary entry becomes a violation —
344
+ * "the glossary conversation happens first" (ubiquitous-language
345
+ * skill). Default false: only conflicts with existing entries
346
+ * violate, so an empty or young glossary doesn't block everything.
347
+ * @param options.instructions — overrides or extends the default
348
+ * language rules text. Pass a string to replace it, or a function
349
+ * `(defaults) => ...` to extend it.
350
+ * @param options.maxGlossaryChars — truncate the glossary beyond this
351
+ * length when building the prompt (default 8000).
352
+ *
353
+ * @example
354
+ * { files: ['specs/**', 'acceptance/**', '**\/*.feature'], rules: [enforceAcceptanceLanguage()] }
355
+ */
356
+ export function enforceAcceptanceLanguage(options = {}) {
357
+ const baseRules = DEFAULT_LANGUAGE_RULES.replace('%STRICT_VOCABULARY%', options.requireGlossaryEntry ? STRICT_VOCABULARY : '');
358
+ const rules = typeof options.instructions === 'function'
359
+ ? options.instructions(baseRules)
360
+ : (options.instructions ?? baseRules);
361
+ const maxGlossaryChars = options.maxGlossaryChars ?? DEFAULT_MAX_GLOSSARY_CHARS;
362
+ return async function enforceAcceptanceLanguage(action, ctx) {
363
+ if (action.kind !== 'write')
364
+ return { kind: 'pass' };
365
+ if (!ctx?.agent) {
366
+ return {
367
+ kind: 'violation',
368
+ reason: 'enforceAcceptanceLanguage: no AI agent available; configure Config.ai or use a vendor that ships one.',
369
+ };
370
+ }
371
+ let glossary;
372
+ if (options.glossaryPath && ctx.readFile) {
373
+ const file = await ctx.readFile(options.glossaryPath);
374
+ if (file.kind === 'present') {
375
+ glossary = truncate(file.content, maxGlossaryChars);
376
+ }
377
+ }
378
+ const before = (await ctx.readFile?.(action.path)) ?? {
379
+ kind: 'unknown',
380
+ };
381
+ const verdict = await ctx.agent.reason(buildPrompt(rules, glossary, before, action));
382
+ if (verdict.kind === 'violation') {
383
+ return { kind: 'violation', reason: verdict.reason };
384
+ }
385
+ return { kind: 'pass', reason: verdict.reason };
386
+ };
387
+ }
388
+ /**
389
+ * AI-validated enforcement of the `acceptance-testing` skill's
390
+ * controlled-precondition principle: a scenario's Given is established
391
+ * by the test (fixture, programmed stub, substituted port), never
392
+ * inherited from the ambient environment. Catches the two moves that
393
+ * silently hand a Given to the environment:
394
+ *
395
+ * 1. a driver method whose name states a precondition but whose body
396
+ * sets no fixture/stub/launch state (the no-op precondition
397
+ * driver), and
398
+ * 2. removing control fixtures because the scenario passes without
399
+ * them — the brownfield trap where the environment produces the
400
+ * sad path for free, and the deleted fixture leaves the success
401
+ * path unspecifiable.
402
+ *
403
+ * Applies to: write actions. Scope it with a `{ files, rules }` block
404
+ * to the driver and test-control layers (e.g.
405
+ * `AcceptanceTests/Drivers/**` plus the acceptance composition root) —
406
+ * the opposite scoping from `enforceAcceptanceLanguage`, which
407
+ * excludes those layers. Every matching write costs an AI call.
408
+ *
409
+ * @param options.instructions — overrides or extends the default
410
+ * precondition rules text. Pass a string to replace it, or a
411
+ * function `(defaults) => ...` to extend it.
412
+ */
413
+ export function enforceControlledPreconditions(options = {}) {
414
+ const rules = typeof options.instructions === 'function'
415
+ ? options.instructions(PRECONDITION_RULES)
416
+ : (options.instructions ?? PRECONDITION_RULES);
417
+ return async function enforceControlledPreconditions(action, ctx) {
418
+ if (action.kind !== 'write')
419
+ return { kind: 'pass' };
420
+ if (!ctx?.agent) {
421
+ return {
422
+ kind: 'violation',
423
+ reason: 'enforceControlledPreconditions: no AI agent available; configure Config.ai or use a vendor that ships one.',
424
+ };
425
+ }
426
+ const before = (await ctx.readFile?.(action.path)) ?? {
427
+ kind: 'unknown',
428
+ };
429
+ const prompt = [
430
+ PRECONDITION_PROCESS_INSTRUCTIONS,
431
+ rules,
432
+ `## Current file content\n\n${formatBefore(before)}`,
433
+ `## Pending action\n\nFile: ${action.path}\n\n${action.content}`,
434
+ RESPONSE_SPEC,
435
+ ].join('\n\n');
436
+ const verdict = await ctx.agent.reason(prompt);
437
+ if (verdict.kind === 'violation') {
438
+ return { kind: 'violation', reason: verdict.reason };
439
+ }
440
+ return { kind: 'pass', reason: verdict.reason };
441
+ };
442
+ }
443
+ //# sourceMappingURL=acceptance-language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptance-language.js","sourceRoot":"","sources":["../../rules/acceptance-language.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAMzC,MAAM,0BAA0B,GAAG,IAAI,CAAA;AAEvC,MAAM,iBAAiB,GAAG;;;;;;;;;CASzB,CAAA;AAED,sEAAsE;AACtE,mEAAmE;AACnE,gEAAgE;AAChE,uEAAuE;AACvE,cAAc;AACd,MAAM,aAAa,GAAG;;;;;;4CAMsB,CAAA;AAE5C,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCnB,CAAA;AAEV,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDAsEyB,CAAA;AAExD,MAAM,iCAAiC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+BlB,CAAA;AAExB,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2DAsCgC,CAAA;AAE3D,SAAS,YAAY,CAAC,MAAmB;IACvC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,OAAO,CAAA;QACvB,KAAK,QAAQ;YACX,OAAO,uBAAuB,CAAA;QAChC,KAAK,SAAS;YACZ,OAAO,oCAAoC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAA;IACxC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,8BAA8B,CAAA;AACjE,CAAC;AAED,SAAS,WAAW,CAClB,KAAa,EACb,QAA4B,EAC5B,MAAmB,EACnB,MAAyC;IAEzC,MAAM,QAAQ,GAAG,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAA;IAC9C,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,IAAI,CAAC,sCAAsC,QAAQ,EAAE,CAAC,CAAA;IACjE,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,8BAA8B,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACnE,QAAQ,CAAC,IAAI,CACX,8BAA8B,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,EAAE,CACjE,CAAA;IACD,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAC5B,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED,mEAAmE;AACnE,gEAAgE;AAChE,8DAA8D;AAC9D,MAAM,gBAAgB,GACpB,gJAAgJ,CAAA;AAElJ,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,KAAK;IACL,IAAI;IACJ,MAAM;IACN,OAAO;IACP,KAAK;IACL,OAAO;IACP,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;CACP,CAAC,CAAA;AAEF,MAAM,WAAW,GAAG,oDAAoD,CAAA;AAExE,SAAS,aAAa,CAAC,OAAe,EAAE,GAAgB;IACtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;AACvE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAU;IACvD,MAAM,OAAO,GAAG,KAAK,UAAU,0BAA0B,CACvD,MAAc,EACd,GAAiB;QAEjB,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC1B,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACjD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAClE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QAClE,MAAM,SAAS,GACb,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;YAC/C,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAA;QAC7C,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC7C,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAClD,CAAA;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO;aACzB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;QAC9D,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC1B,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;QAC/B,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC;YACH,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBACpC,aAAa,CACX,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,EACvD,KAAK,CACN,CAAA;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAE,CAAA;gBACrB,IAAI,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAQ;gBAC5C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YAChD,CAAC;QACH,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAA;IACzD,CAAC,CAAA;IACD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE;QACrC,KAAK,EAAE,8BAA8B,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG;KAC5D,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAKI,EAAE;IAEN,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,CAC9C,qBAAqB,EACrB,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CACtD,CAAA;IACD,MAAM,KAAK,GACT,OAAO,OAAO,CAAC,YAAY,KAAK,UAAU;QACxC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC;QACjC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,SAAS,CAAC,CAAA;IACzC,MAAM,gBAAgB,GACpB,OAAO,CAAC,gBAAgB,IAAI,0BAA0B,CAAA;IACxD,OAAO,KAAK,UAAU,yBAAyB,CAC7C,MAAc,EACd,GAAiB;QAEjB,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QACpD,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO;gBACL,IAAI,EAAE,WAAW;gBACjB,MAAM,EACJ,uGAAuG;aAC1G,CAAA;QACH,CAAC;QACD,IAAI,QAA4B,CAAA;QAChC,IAAI,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;YACrD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC5B,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAA;YACrD,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAgB,CAAC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI;YACjE,IAAI,EAAE,SAAS;SAChB,CAAA;QACD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,CACpC,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAC7C,CAAA;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;QACtD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;IACjD,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,8BAA8B,CAC5C,UAEI,EAAE;IAEN,MAAM,KAAK,GACT,OAAO,OAAO,CAAC,YAAY,KAAK,UAAU;QACxC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC;QAC1C,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,kBAAkB,CAAC,CAAA;IAClD,OAAO,KAAK,UAAU,8BAA8B,CAClD,MAAc,EACd,GAAiB;QAEjB,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QACpD,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO;gBACL,IAAI,EAAE,WAAW;gBACjB,MAAM,EACJ,4GAA4G;aAC/G,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAgB,CAAC,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI;YACjE,IAAI,EAAE,SAAS;SAChB,CAAA;QACD,MAAM,MAAM,GAAG;YACb,iCAAiC;YACjC,KAAK;YACL,8BAA8B,YAAY,CAAC,MAAM,CAAC,EAAE;YACpD,8BAA8B,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,EAAE;YAChE,aAAa;SACd,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACd,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC9C,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;QACtD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;IACjD,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,125 @@
1
+ import type { Rule, RuleContext } from '@nizos/probity';
2
+ /**
3
+ * Language-neutral gate rules. These accumulated in the Kotlin preset
4
+ * (`kotlin.ts`) because that's where the real-project trials happened,
5
+ * but nothing in them is Kotlin-specific — they take every
6
+ * language-shaped decision (what a test command looks like, what an
7
+ * ambient-effect call looks like) as a pattern option. This module is
8
+ * their canonical home; `kotlin.ts` re-exports wrappers that apply the
9
+ * Kotlin/Gradle defaults, so existing preset configs are unchanged.
10
+ *
11
+ * All rules here are deterministic — no AI call — and delta-based
12
+ * where they judge file content: only what a write INTRODUCES blocks,
13
+ * so brownfield files migrate incrementally instead of freezing.
14
+ */
15
+ /** A labeled content pattern; the label names the hit in deny text. */
16
+ export type NamedPattern = {
17
+ label: string;
18
+ pattern: RegExp;
19
+ };
20
+ /** Patterns whose occurrence count grows from before → after. */
21
+ export declare function introducedPatterns(action: {
22
+ path: string;
23
+ content: string;
24
+ }, ctx: RuleContext | undefined, patterns: NamedPattern[]): Promise<string[]>;
25
+ /**
26
+ * Ambient-effect calls for a JS/TS core: OS clock, randomness, and
27
+ * environment reads. Pass to {@link forbidNewAmbientEffects} from a
28
+ * JS/TS config. `process.env` reads belong in the composition root or
29
+ * a config adapter; if your core legitimately branches on injected
30
+ * config, that config should arrive through a port, not the OS.
31
+ */
32
+ export declare const JS_AMBIENT_EFFECT_PATTERNS: NamedPattern[];
33
+ /**
34
+ * Blocks production writes that introduce direct ambient-effect calls.
35
+ * Under ports-and-adapters these are unowned OS dependencies: clock,
36
+ * randomness, and environment are ports.
37
+ *
38
+ * Delta-based: pre-existing call sites in a brownfield codebase don't
39
+ * block edits to their files; only net-new occurrences do. Scope to
40
+ * production sources — tests and adapter implementations (e.g. a
41
+ * `SystemClock`) legitimately touch the real OS, so exclude adapter
42
+ * paths via globs or negations.
43
+ *
44
+ * @param options.patterns — what an ambient-effect call looks like in
45
+ * your language ({@link JS_AMBIENT_EFFECT_PATTERNS} for JS/TS; the
46
+ * Kotlin preset supplies JVM patterns). Each RegExp needs `g`.
47
+ * @param options.seamHint — appended to the block message to point
48
+ * the agent at the project's canonical seam(s), e.g.
49
+ * "inject the Clock port from src/ports/clock.ts".
50
+ */
51
+ export declare function forbidNewAmbientEffects(options: {
52
+ patterns: NamedPattern[];
53
+ seamHint?: string;
54
+ }): Rule;
55
+ /**
56
+ * Commit-on-green, strictly: Probity's `requireCommand` checks only
57
+ * that a matching test invocation was *recorded* after the last write
58
+ * and would happily pass a transcript whose latest run FAILED. This
59
+ * rule additionally judges the recorded run's output: the last
60
+ * matching test command after the last write must look green
61
+ * (`successPattern` present, `failurePattern` absent).
62
+ *
63
+ * Inherent limit (unchanged from requireCommand): the gate sees only
64
+ * the session transcript. A green run in another terminal, CI, or a
65
+ * wrapper script is invisible — rerun the suite in-session, and keep
66
+ * the CI mirror for human commits.
67
+ *
68
+ * Applies to: command actions matching `git commit`. Deterministic —
69
+ * no AI call.
70
+ *
71
+ * @param options.command — regex matching a test invocation.
72
+ * @param options.successPattern — output must match to count as green.
73
+ * @param options.failurePattern — output matching this is red even if
74
+ * the success pattern also appears.
75
+ * @param options.enforceForPaths — only demand the run when the
76
+ * pending commit stages a file whose repo-relative path matches.
77
+ * Scopes an expensive suite to the code it covers: infra/docs/
78
+ * tooling-only commits (CI config, Markdown, the Probity config
79
+ * itself) pay no friction, since they change no behaviour the suite
80
+ * validates and the prior green run still stands. Commit-accurate —
81
+ * read from git's staged set, not the session's write history. Any
82
+ * error listing files falls through to enforcing (fail safe).
83
+ * @param options.listCommitFiles — injectable staged-file lister
84
+ * (defaults to reading git's staged set); present for testing.
85
+ * @param options.reason — appended to the no-run deny text to name
86
+ * the suite and any setup it needs (e.g. "supabase start").
87
+ */
88
+ export declare function requireGreenTestRun(options: {
89
+ command: RegExp;
90
+ successPattern: RegExp;
91
+ failurePattern: RegExp;
92
+ enforceForPaths?: RegExp;
93
+ listCommitFiles?: (command: string) => string[];
94
+ reason?: string;
95
+ }): Rule;
96
+ /**
97
+ * Surfaces the "renamed a button, broke the E2E suite" failure at
98
+ * write time: when a write REMOVES a string literal that still appears
99
+ * verbatim in files under `searchRoots` (typically your E2E/UI-test
100
+ * specs, which select elements by visible text or accessible name),
101
+ * the write is blocked with the list of dependent files.
102
+ *
103
+ * The inverse of a glossary guard: `surfaceGlossaryTermBreakage`
104
+ * protects the vocabulary file from code that depends on it; this
105
+ * protects test selectors from the UI code they depend on. Scope it to
106
+ * your UI sources, with `searchRoots` pointing at the spec layers the
107
+ * quick local loop does NOT run (E2E, smoke) — specs the inner loop
108
+ * runs will fail red on their own.
109
+ *
110
+ * Deterministic, delta-based — no AI call. The write goes through once
111
+ * the dependent specs are updated in the same session (or the string
112
+ * genuinely stops being referenced).
113
+ *
114
+ * @param options.searchRoots — absolute paths to scan for usages.
115
+ * @param options.searchPattern — which files count as usage sites
116
+ * (default: `*.spec.*` / `*.test.*` under the roots).
117
+ * @param options.minLength — ignore removed literals shorter than
118
+ * this after trimming (default 8; short strings false-positive).
119
+ */
120
+ export declare function surfaceRemovedStringUsage(options: {
121
+ searchRoots: string[];
122
+ searchPattern?: RegExp;
123
+ minLength?: number;
124
+ }): Rule;
125
+ //# sourceMappingURL=gates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../rules/gates.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAU,IAAI,EAAE,WAAW,EAAc,MAAM,gBAAgB,CAAA;AAE3E;;;;;;;;;;;;GAYG;AAEH,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAQ7D,iEAAiE;AACjE,wBAAsB,kBAAkB,CACtC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACzC,GAAG,EAAE,WAAW,GAAG,SAAS,EAC5B,QAAQ,EAAE,YAAY,EAAE,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAcnB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,YAAY,EAOpD,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,GAAG,IAAI,CAqBP;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,IAAI,CAuDP;AA0CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,IAAI,CA+CP"}