clud-bug 0.7.0-rc.2 → 0.7.0-rc.3

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.
@@ -0,0 +1,261 @@
1
+ // SPEC §1.8.1 multi-pass diff: prior `docs/reviews/PR-<n>.md` vs current
2
+ // review findings. Produces the `**Resolved this round:**` /
3
+ // `**Still open:**` lists that `renderReviewFile` consumes.
4
+ //
5
+ // Identity model
6
+ // --------------
7
+ // Each finding's identity is a stable hash of:
8
+ //
9
+ // `${file}:${line}:${severity}:${skillName}:${summary.slice(0, 100)}`
10
+ //
11
+ // A 100-character truncation on `summary` is enough to discriminate
12
+ // distinct findings without being so long that whitespace/punctuation
13
+ // drift between passes breaks identity. A severity-bucket change (e.g.
14
+ // the same skill flags the same line as 🔴 then 🟡 the next round)
15
+ // produces a DIFFERENT identity — by design — so a severity downgrade
16
+ // counts as one resolved finding + one new finding (the user gets
17
+ // credit for the fix AND can see the bot still has a concern, just at
18
+ // a lower severity).
19
+ //
20
+ // The same identity shape is what auto-fix / auto-resolve in
21
+ // clud-bug-app uses for thread anchoring; keeping it identical means a
22
+ // future per-finding cross-reference between the doc-file diff and the
23
+ // inline-thread surface is trivial.
24
+ import { flattenFindings, } from './review-schema-zod.js';
25
+ import { SEVERITY_EMOJI } from './review-writeback.js';
26
+ /**
27
+ * Parse a `docs/reviews/PR-<n>.md` markdown back into structured findings.
28
+ *
29
+ * Robust to:
30
+ * - `null` / `undefined` input → returns `null`
31
+ * - empty / whitespace-only markdown → returns `null`
32
+ * - missing severity sections → those sections contribute 0
33
+ * - lines that don't match the SPEC bullet shape → silently dropped
34
+ * - mixed unknown-file markers (`(unknown file)` vs absent)
35
+ *
36
+ * Parsing strategy: walk top-down, switch active severity on each
37
+ * `### <emoji> <Label>` header (one of the three SPEC §1.8.1 buckets),
38
+ * then collect every line starting with `- **` until the next header
39
+ * (or `---` end marker). Each `- **<file>:<line>** — <skill>: <summary>`
40
+ * line is split into its 4 fields; `:<line>` is optional (cross-cutting).
41
+ *
42
+ * `(unknown file)` files are preserved verbatim — they participate in
43
+ * identity, so the same cross-cutting finding can still be diffed across
44
+ * rounds even when neither pass has a line anchor.
45
+ */
46
+ export function parsePriorReviewFile(markdown) {
47
+ if (markdown == null)
48
+ return null;
49
+ if (markdown.trim() === '')
50
+ return null;
51
+ const lines = markdown.split('\n');
52
+ const out = [];
53
+ let current = null;
54
+ for (const raw of lines) {
55
+ // Strip trailing whitespace; leading indent is meaningful for
56
+ // sub-lines (Reasoning, attribution) which we deliberately ignore.
57
+ const line = raw.replace(/\s+$/, '');
58
+ // The SPEC §1.8.1 trailing `---` separator ends the findings region.
59
+ // Anything after it (the [Link to PR] line) is metadata and parsed
60
+ // by short-circuit so we don't mistake the literal `---` for a
61
+ // missing header.
62
+ if (line === '---') {
63
+ current = null;
64
+ continue;
65
+ }
66
+ // Switch active severity on every `### <emoji> <Label>` heading.
67
+ // We pattern-match on the emoji codepoint (not the label text) so a
68
+ // future SPEC tweak to "Critical" → "Blocking" doesn't break us.
69
+ if (line.startsWith('### ')) {
70
+ if (line.includes(SEVERITY_EMOJI.critical)) {
71
+ current = 'critical';
72
+ }
73
+ else if (line.includes(SEVERITY_EMOJI.minor)) {
74
+ current = 'minor';
75
+ }
76
+ else if (line.includes(SEVERITY_EMOJI.preexisting)) {
77
+ current = 'preexisting';
78
+ }
79
+ else {
80
+ current = null;
81
+ }
82
+ continue;
83
+ }
84
+ // Resolved / Still-open blocks under SPEC §1.8.1 — these list
85
+ // findings from PRIOR rounds, NOT this-round findings. Skip them
86
+ // so a multi-round PR doesn't double-count its own history.
87
+ // (`**Resolved this round:**` / `**Still open:**` headings.)
88
+ if (line.startsWith('**Resolved this round:') || line.startsWith('**Still open:')) {
89
+ current = null;
90
+ continue;
91
+ }
92
+ if (current == null)
93
+ continue;
94
+ // SPEC §1.8.1 bullet: `- **<file>:<line>** — <skill>: <summary>`
95
+ // OR `- **<file>** — <skill>: <summary>`
96
+ // OR `- **(unknown file)** — <skill>: <summary>`
97
+ //
98
+ // The multi-pass renderer prepends `[Pass N — Role · model]` to the
99
+ // bullet; we accept that prefix and discard it for parsing.
100
+ const parsed = parseFindingBullet(line);
101
+ if (parsed) {
102
+ out.push({ ...parsed, severity: current });
103
+ }
104
+ }
105
+ if (out.length === 0)
106
+ return null;
107
+ return { findings: out };
108
+ }
109
+ /**
110
+ * Diff prior vs current.
111
+ *
112
+ * `resolvedFindings`: findings that appeared in `prior` but NOT in
113
+ * `current`. The PR author (or auto-fix) addressed them.
114
+ *
115
+ * `stillOpenFindings`: findings that appeared in BOTH `prior` and
116
+ * `current`. These are persistent — the PR author hasn't fixed them
117
+ * (or the bot still considers them findings post-fix-push).
118
+ *
119
+ * Findings unique to `current` (newly raised this round) appear in
120
+ * neither list — those are surfaced directly by the renderer's normal
121
+ * severity-bucket emission.
122
+ *
123
+ * Order is preserved from `prior` for stability across rounds.
124
+ */
125
+ export function diffFindings(prior, current) {
126
+ if (prior === null || prior.findings.length === 0) {
127
+ return { resolvedFindings: [], stillOpenFindings: [] };
128
+ }
129
+ // Build identity set for the current round. We do not need the
130
+ // current-round ParsedFinding objects — we only need to know which
131
+ // identities are still present.
132
+ //
133
+ // Cast through `Review` shape — the schema's three arrays mirror our
134
+ // input slice exactly (skill / file / line / summary), so we can lean
135
+ // on `flattenFindings` to produce a tagged list.
136
+ const currentReview = {
137
+ status_header: 'clean',
138
+ summary_counts: {
139
+ critical: 0,
140
+ minor: 0,
141
+ preexisting: 0,
142
+ resolved_from_prior: 0,
143
+ still_open: 0,
144
+ },
145
+ per_skill_scan: [],
146
+ critical_findings: current.critical_findings ?? [],
147
+ minor_findings: current.minor_findings ?? [],
148
+ preexisting_findings: current.preexisting_findings ?? [],
149
+ skills_referenced: [],
150
+ last_reviewed_sha: '',
151
+ };
152
+ const currentFlat = flattenFindings(currentReview);
153
+ const currentIds = new Set(currentFlat.map((f) => findingIdentity({
154
+ file: f.file ?? '(unknown file)',
155
+ line: f.line ?? 0,
156
+ severity: f.severity,
157
+ skillName: f.skill,
158
+ summary: f.summary,
159
+ })));
160
+ const resolved = [];
161
+ const stillOpen = [];
162
+ for (const f of prior.findings) {
163
+ const id = findingIdentity(f);
164
+ if (currentIds.has(id)) {
165
+ stillOpen.push(f);
166
+ }
167
+ else {
168
+ resolved.push(f);
169
+ }
170
+ }
171
+ return { resolvedFindings: resolved, stillOpenFindings: stillOpen };
172
+ }
173
+ /**
174
+ * Stable identity for a finding. Used for diffing prior vs current
175
+ * across review rounds AND (by intention) shareable with the
176
+ * clud-bug-app inline-thread anchor hash so future cross-feature
177
+ * surfaces (e.g. "the auto-fix that resolved this thread also resolves
178
+ * this doc-file finding") align without re-computing.
179
+ *
180
+ * Exposed for tests + downstream callers that want to align their own
181
+ * finding storage on the same scheme.
182
+ */
183
+ export function findingIdentity(f) {
184
+ // Truncate summary to 100 chars to absorb whitespace/punctuation
185
+ // drift between rounds without losing discrimination between
186
+ // genuinely-distinct findings (the SPEC §1.8.1 summary line is
187
+ // user-visible so it tends to be stable; 100 chars is enough for any
188
+ // realistic distinct summary while tolerating "fix the X" → "fix X"
189
+ // drift).
190
+ const summaryPart = f.summary.slice(0, 100);
191
+ return `${f.file}:${f.line}:${f.severity}:${f.skillName}:${summaryPart}`;
192
+ }
193
+ // ---------------------------------------------------------------------------
194
+ // Parsing internals
195
+ // ---------------------------------------------------------------------------
196
+ /**
197
+ * Parse one SPEC §1.8.1 bullet line into a {file, line, skill, summary}
198
+ * tuple. Returns null for any line that doesn't match the SPEC shape.
199
+ *
200
+ * Accepts both shapes:
201
+ * `- **<file>:<line>** — <skill>: <summary>`
202
+ * `- **<file>** — <skill>: <summary>`
203
+ *
204
+ * Also tolerates the multi-pass attribution prefix (D.2.5):
205
+ * `- [Pass 1 — Role · model] **<file>:<line>** — <skill>: <summary>`
206
+ *
207
+ * Em-dash recognition: the SPEC pins U+2014 EM DASH between location
208
+ * and skill. Some downstream tools have been observed using `--` or
209
+ * regular hyphens; we accept both for resilience.
210
+ */
211
+ function parseFindingBullet(line) {
212
+ // Strip the leading `- ` bullet marker.
213
+ if (!line.startsWith('- '))
214
+ return null;
215
+ let rest = line.slice(2);
216
+ // Strip optional `[Pass N — ...] ` attribution prefix (D.2.5).
217
+ if (rest.startsWith('[')) {
218
+ const closeIdx = rest.indexOf('] ');
219
+ if (closeIdx === -1)
220
+ return null;
221
+ rest = rest.slice(closeIdx + 2);
222
+ }
223
+ // Expect `**<location>** ` next.
224
+ if (!rest.startsWith('**'))
225
+ return null;
226
+ const locEnd = rest.indexOf('**', 2);
227
+ if (locEnd === -1)
228
+ return null;
229
+ const location = rest.slice(2, locEnd);
230
+ rest = rest.slice(locEnd + 2);
231
+ // Strip the location separator. SPEC pins ` — ` (U+2014 surrounded by
232
+ // spaces). We accept hyphen-minus variants as a courtesy.
233
+ // The separator may be ` — `, ` -- `, or ` - `.
234
+ const sepMatch = rest.match(/^\s+(?:—|--|-)\s+/);
235
+ if (!sepMatch)
236
+ return null;
237
+ rest = rest.slice(sepMatch[0].length);
238
+ // `<skill>: <summary>` — split on the first `: `.
239
+ const sepIdx = rest.indexOf(': ');
240
+ if (sepIdx === -1)
241
+ return null;
242
+ const skillName = rest.slice(0, sepIdx).trim();
243
+ const summary = rest.slice(sepIdx + 2).trim();
244
+ if (skillName === '' || summary === '')
245
+ return null;
246
+ // Split location into file + optional line. Walk RIGHTWARD from the
247
+ // last `:` so file names containing colons (Windows-style, rare) on
248
+ // the LHS don't confuse us.
249
+ const colonIdx = location.lastIndexOf(':');
250
+ let file = location;
251
+ let lineNum = 0;
252
+ if (colonIdx !== -1) {
253
+ const tail = location.slice(colonIdx + 1);
254
+ if (/^\d+$/.test(tail)) {
255
+ file = location.slice(0, colonIdx);
256
+ lineNum = Number(tail);
257
+ }
258
+ }
259
+ return { file, line: lineNum, skillName, summary };
260
+ }
261
+ //# sourceMappingURL=diff-findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-findings.js","sourceRoot":"","sources":["../../src/core/diff-findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /** GitHub's `pulls.createReview` `event` enum, narrowed to the three we use. */
2
+ export type FormalReviewEvent = 'APPROVE' | 'REQUEST_CHANGES' | 'COMMENT';
3
+ /**
4
+ * GitHub `author_association` on a PR. Verbatim union from the REST API
5
+ * (per https://docs.github.com/en/rest/pulls/pulls — `author_association`).
6
+ *
7
+ * The "external" tier for §7.2.1 auto-approve gating is:
8
+ *
9
+ * { 'NONE', 'FIRST_TIME_CONTRIBUTOR', 'FIRST_TIMER', 'MANNEQUIN' }
10
+ *
11
+ * Everything else (OWNER, MEMBER, COLLABORATOR, CONTRIBUTOR) is the
12
+ * "org-trusted" tier and eligible for auto-approve on a clean review.
13
+ *
14
+ * `FIRST_TIMER` is the legacy spelling (October 2018 era); GitHub
15
+ * currently emits `FIRST_TIME_CONTRIBUTOR` but some PR fixtures in
16
+ * downstream tests still carry the old token. We keep both so the
17
+ * external-contributor gate doesn't silently regress on the next
18
+ * REST-API rename.
19
+ */
20
+ export type AuthorAssociation = 'OWNER' | 'MEMBER' | 'COLLABORATOR' | 'CONTRIBUTOR' | 'FIRST_TIME_CONTRIBUTOR' | 'FIRST_TIMER' | 'NONE' | 'MANNEQUIN';
21
+ /**
22
+ * SPEC §7.2.1 + §7.2 rule table.
23
+ *
24
+ * Order matters — earlier rows short-circuit later ones:
25
+ *
26
+ * | Priority | Condition | Event |
27
+ * |----------|--------------------------------------------------------|-----------------|
28
+ * | 1 | PR author === clud-bug[bot] | 'skip' |
29
+ * | 2 | authorAssociation ∈ EXTERNAL | 'COMMENT' |
30
+ * | 3 | criticalCount > 0 AND strictMode=true | REQUEST_CHANGES |
31
+ * | 4 | criticalCount > 0 AND strictMode=false | COMMENT |
32
+ * | 5 | minorCount > 0 (no critical) | COMMENT |
33
+ * | 6 | 0 critical + 0 minor | APPROVE |
34
+ *
35
+ * The external-contributor row (Priority 2) sits BETWEEN self-PR-skip
36
+ * and the severity-driven rules: external contributors who open a
37
+ * critical-finding PR still get COMMENT (NOT REQUEST_CHANGES — we don't
38
+ * block their PR on a bot review; that's a human reviewer's call to
39
+ * make). External contributors who open a clean PR also get COMMENT
40
+ * (NOT APPROVE — the §7.2.1 precondition #3 gate).
41
+ *
42
+ * The 'skip' verdict tells the caller NOT to invoke `pulls.createReview`
43
+ * — GitHub returns 422 when a user reviews their own PR, so we
44
+ * short-circuit before the network round-trip. Self-PRs land during D.7
45
+ * migration fan-out (the App opens cross-repo update PRs under its own
46
+ * identity).
47
+ */
48
+ export interface SelectReviewEventInput {
49
+ /** Count of `severity: critical` findings on the review. */
50
+ criticalCount: number;
51
+ /** Count of `severity: minor` findings on the review. */
52
+ minorCount: number;
53
+ /**
54
+ * `strictMode` flag read from `.clud-bug.json` at the PR's BASE ref.
55
+ * Older manifests may not carry this field — callers MUST pass
56
+ * `undefined` in that case so this function applies the safe default
57
+ * (false) rather than surprising users with REQUEST_CHANGES.
58
+ */
59
+ strictMode?: boolean;
60
+ /**
61
+ * GitHub login of the PR author. When it equals 'clud-bug[bot]' we
62
+ * skip the formal review entirely (GitHub disallows self-review with
63
+ * 422). This is the structural guard for D.7 migration fan-out PRs.
64
+ */
65
+ prAuthorLogin: string;
66
+ /**
67
+ * GitHub `author_association` on the PR. NEW in v0.7.0-rc.3 / SPEC
68
+ * §7.2.1: when this is in EXTERNAL_ASSOCIATIONS, a clean review gets
69
+ * COMMENT (not APPROVE) so external-contributor PRs require a human
70
+ * reviewer to satisfy the `required_approving_review_count: 1` floor.
71
+ *
72
+ * Callers that don't have this metadata (older webhook payloads,
73
+ * tests, etc.) should pass `'CONTRIBUTOR'` as the safe default — that
74
+ * tier is org-trusted and preserves pre-§7.2.1 behaviour.
75
+ */
76
+ authorAssociation: AuthorAssociation;
77
+ }
78
+ export declare function selectReviewEvent(input: SelectReviewEventInput): FormalReviewEvent | 'skip';
79
+ //# sourceMappingURL=formal-review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formal-review.d.ts","sourceRoot":"","sources":["../../src/core/formal-review.ts"],"names":[],"mappings":"AAuBA,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE1E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,iBAAiB,GACzB,OAAO,GACP,QAAQ,GACR,cAAc,GACd,aAAa,GACb,wBAAwB,GACxB,aAAa,GACb,MAAM,GACN,WAAW,CAAC;AAkBhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;;;OASG;IACH,iBAAiB,EAAE,iBAAiB,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,iBAAiB,GAAG,MAAM,CAsC5B"}
@@ -0,0 +1,71 @@
1
+ // SPEC §7.2.1 formal-review event selector.
2
+ //
3
+ // This is the PURE half of clud-bug-app's `lib/formal-review.ts` (the
4
+ // Octokit-side `postFormalReview` IO wrapper stays App-side — it depends
5
+ // on `getInstallationOctokit` + `@octokit/rest` which we don't want to
6
+ // pull into core). The pure rule-table lives here so:
7
+ //
8
+ // 1. The npm workflow template's new post-step (added in v0.7.0-rc.3)
9
+ // can `import { selectReviewEvent } from 'clud-bug/core'` and post
10
+ // formal `pulls.createReview` calls under the workflow path — until
11
+ // this PR shipped, the workflow path NEVER satisfied the canonical
12
+ // ruleset's `required_approving_review_count: 1` floor because no
13
+ // APPROVE review was ever posted.
14
+ //
15
+ // 2. clud-bug-app (Phase 7 PR B) deletes its local copy and imports
16
+ // this version, gaining the §7.2.1 author_association extension
17
+ // (clud-bug-app's PR #40 shipped APPROVE without it, so the App
18
+ // currently auto-approves drive-by external-contributor PRs — a
19
+ // security bug closed by Phase 7 PR B's dep bump).
20
+ //
21
+ // Ported from clud-bug-app/lib/formal-review.ts (PR #40, MERGED 2026-06-10)
22
+ // with the §7.2.1 `authorAssociation` extension new in Phase 7 PR A.
23
+ /**
24
+ * Author-association values that route a clean-review APPROVE *down* to
25
+ * COMMENT per SPEC §7.2.1 precondition #3 ("PR author is an org member —
26
+ * NOT a first-time external contributor"). These authors still get a
27
+ * COMMENT review (so they SEE the bot's verdict) but the formal APPROVE
28
+ * vote stays withheld — a human reviewer must click Approve before the
29
+ * PR can merge under the canonical ruleset.
30
+ *
31
+ * Drive-by exploitation prevention: a malicious external contributor
32
+ * who opens a clean-looking PR cannot leverage clud-bug[bot]'s APPROVE
33
+ * vote to auto-merge.
34
+ */
35
+ const EXTERNAL_ASSOCIATIONS = new Set(['NONE', 'FIRST_TIME_CONTRIBUTOR', 'FIRST_TIMER', 'MANNEQUIN']);
36
+ export function selectReviewEvent(input) {
37
+ // Priority 1: self-PR guard. GitHub returns 422 on a self-review;
38
+ // we short-circuit before the network round-trip. Self-PRs land
39
+ // during D.7 migration fan-out (the App opens cross-repo update PRs
40
+ // under its own identity).
41
+ if (input.prAuthorLogin === 'clud-bug[bot]') {
42
+ return 'skip';
43
+ }
44
+ // Priority 2: external-contributor gate (SPEC §7.2.1 precondition #3).
45
+ // External contributors NEVER get APPROVE (no auto-merge bypass via
46
+ // drive-by) and NEVER get REQUEST_CHANGES (we don't block their PR on
47
+ // a bot review — that escalation is a human reviewer's call). They
48
+ // always get an advisory COMMENT so they see the bot's verdict.
49
+ if (EXTERNAL_ASSOCIATIONS.has(input.authorAssociation)) {
50
+ return 'COMMENT';
51
+ }
52
+ // Priority 6 (note: priorities 3-5 fall through to here when there
53
+ // are no findings): clean review on an org-trusted author → APPROVE.
54
+ // APPROVE flips the `required_approving_review_count: 1` ruleset and
55
+ // lets auto-merge fire under the canonical SPEC §7.2 ruleset.
56
+ if (input.criticalCount === 0 && input.minorCount === 0) {
57
+ return 'APPROVE';
58
+ }
59
+ // Priority 3 + 4: critical finding → gate on strictMode. Default
60
+ // `strictMode === undefined` → false (advisory-only). REQUEST_CHANGES
61
+ // blocks the PR until the author dismisses or fixes; we only honor
62
+ // it when the repo opted in.
63
+ if (input.criticalCount > 0) {
64
+ return input.strictMode === true ? 'REQUEST_CHANGES' : 'COMMENT';
65
+ }
66
+ // Priority 5: minor-only or preexisting-only finding → advisory
67
+ // COMMENT. We never REQUEST_CHANGES on a minor — those are noted,
68
+ // not blocking.
69
+ return 'COMMENT';
70
+ }
71
+ //# sourceMappingURL=formal-review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formal-review.js","sourceRoot":"","sources":["../../src/core/formal-review.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,uEAAuE;AACvE,sDAAsD;AACtD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,uEAAuE;AACvE,uCAAuC;AACvC,EAAE;AACF,sEAAsE;AACtE,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AACrE,wDAAwD;AACxD,EAAE;AACF,4EAA4E;AAC5E,qEAAqE;AAgCrE;;;;;;;;;;;GAWG;AACH,MAAM,qBAAqB,GAAmC,IAAI,GAAG,CAEnE,CAAC,MAAM,EAAE,wBAAwB,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;AA4DlE,MAAM,UAAU,iBAAiB,CAC/B,KAA6B;IAE7B,kEAAkE;IAClE,gEAAgE;IAChE,oEAAoE;IACpE,2BAA2B;IAC3B,IAAI,KAAK,CAAC,aAAa,KAAK,eAAe,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,uEAAuE;IACvE,oEAAoE;IACpE,sEAAsE;IACtE,mEAAmE;IACnE,gEAAgE;IAChE,IAAI,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,mEAAmE;IACnE,qEAAqE;IACrE,qEAAqE;IACrE,8DAA8D;IAC9D,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,iEAAiE;IACjE,sEAAsE;IACtE,mEAAmE;IACnE,6BAA6B;IAC7B,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,CAAC;IAED,gEAAgE;IAChE,kEAAkE;IAClE,gBAAgB;IAChB,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -6,6 +6,8 @@ export { render, renderFile, pickTemplate, templateLanguage, DEFAULTS, type Rend
6
6
  export { durationToGitSince, renderAuditHeader, type AuditHeaderInput, } from './audit.js';
7
7
  export { reviewSchema, crossCheckSchema, findingItemSchema, findingSchema, perSkillScanItemSchema, dedicatedSectionSchema, summaryCountsSchema, severitySchema, statusHeaderSchema, crossCheckVerdictSchema, severityValues, statusHeaderValues, flattenFindings, unflattenFindings, deriveSummaryCounts, deriveSkillsReferenced, buildReviewFromFindings, type Review, type CrossCheck, type CrossCheckVerdictSchema, type Severity, type StatusHeader, type SummaryCounts, type FindingItem, type PerSkillScanItem as ZodPerSkillScanItem, type DedicatedSection as ZodDedicatedSection, type Finding as ZodFinding, } from './review-schema-zod.js';
8
8
  export { buildReviewPrompt, buildCrossCheckPrompt, buildConsensusPrompt, skillMatchesDiff, globMatch, truncatePatch, sliceUtf8Bytes, MAX_PATCH_BYTES_PER_FILE, DEFAULT_MAX_SKILL_BYTES, type BuildReviewPromptInput, type BuildCrossCheckPromptInput, type BuiltPrompt, type ChangedFile, type ChangedFileStatus, type PullRequestDiff, type PromptAppliesToRule, type PromptSkillFrontmatter, type PromptLoadedSkill, } from './prompt-builder.js';
9
- export { renderReviewFile, renderMultiPassMarkdown, reviewFilePath, reviewCommitMessage, PROTOCOL_VERSION, WRITTEN_BY, SEVERITY_EMOJI as REVIEW_FILE_SEVERITY_EMOJI, type RenderReviewFileInput, type RenderMultiPassMarkdownInput, type MultiPassReview, type UnifiedFinding, type PassAttribution, type PassSource, type ReviewPassMode, type MultiPassVerdict, } from './review-writeback.js';
9
+ export { renderReviewFile, renderMultiPassMarkdown, reviewFilePath, reviewCommitMessage, PROTOCOL_VERSION, WRITTEN_BY, SEVERITY_EMOJI as REVIEW_FILE_SEVERITY_EMOJI, type RenderReviewFileInput, type RenderedFindingRef, type CacheStats, type RenderMultiPassMarkdownInput, type MultiPassReview, type UnifiedFinding, type PassAttribution, type PassSource, type ReviewPassMode, type MultiPassVerdict, } from './review-writeback.js';
10
+ export { selectReviewEvent, type FormalReviewEvent, type AuthorAssociation, type SelectReviewEventInput, } from './formal-review.js';
11
+ export { parsePriorReviewFile, diffFindings, findingIdentity, type ParsedFinding, type ParsedReview, } from './diff-findings.js';
10
12
  export { API_BASE, MAX_SKILLS, SkillsClient, normalizeList, rankAndCap, readReviewMode, readAppliesTo, appliesToPr, partitionByReviewMode, extractPerSkillLine, selectReviewHeader, extractFirstReviewHeaderLine, selectReviewBody, extractStatsHeader, isCriticalReviewHeader, classifyPerSkillOutcome, parseFrontmatter, stripFrontmatter, type SkillDescriptor, type RankableSkill, type AppliesToRule, type SkillWithOptionalContent, type PrComment, type ReviewStatsHeader, type SkillFrontmatter, type SkillSource, type SkillReviewMode, } from './skills.js';
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -27,5 +27,19 @@ export { buildReviewPrompt, buildCrossCheckPrompt, buildConsensusPrompt, skillMa
27
27
  // from the App's `renderReview` to `renderReviewFile` to disambiguate
28
28
  // from the CLI's `renderReview` (summary PR-comment shape) above.
29
29
  export { renderReviewFile, renderMultiPassMarkdown, reviewFilePath, reviewCommitMessage, PROTOCOL_VERSION, WRITTEN_BY, SEVERITY_EMOJI as REVIEW_FILE_SEVERITY_EMOJI, } from './review-writeback.js';
30
+ // SPEC §7.2.1 formal-review event selector. Pure rule-table half of
31
+ // clud-bug-app's `lib/formal-review.ts`; the Octokit-side IO wrapper
32
+ // (`postFormalReview`) stays App-side. v0.7.0-rc.3 adds the
33
+ // `authorAssociation` extension so a clean review on an external
34
+ // contributor's PR routes to COMMENT (not APPROVE) — the canonical
35
+ // ruleset's `required_approving_review_count: 1` floor then requires
36
+ // a human reviewer.
37
+ export { selectReviewEvent, } from './formal-review.js';
38
+ // SPEC §1.8.1 Resolved / Still-open block helpers. `parsePriorReviewFile`
39
+ // reads a prior `docs/reviews/PR-<n>.md`; `diffFindings` splits prior vs
40
+ // current into resolved + still-open lists that `renderReviewFile`
41
+ // emits as the §1.8.1 blocks. Identity scheme is shareable with future
42
+ // inline-thread anchoring in clud-bug-app.
43
+ export { parsePriorReviewFile, diffFindings, findingIdentity, } from './diff-findings.js';
30
44
  export { API_BASE, MAX_SKILLS, SkillsClient, normalizeList, rankAndCap, readReviewMode, readAppliesTo, appliesToPr, partitionByReviewMode, extractPerSkillLine, selectReviewHeader, extractFirstReviewHeaderLine, selectReviewBody, extractStatsHeader, isCriticalReviewHeader, classifyPerSkillOutcome, parseFrontmatter, stripFrontmatter, } from './skills.js';
31
45
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gDAAgD;AAChD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,iBAAiB;AAEjB,OAAO,EAAE,YAAY,EAAuD,MAAM,cAAc,CAAC;AACjG,OAAO,EACL,aAAa,EACb,sBAAsB,GAQvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EACL,MAAM,EACN,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,EACd,aAAa,EACb,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,MAAM,EACN,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,QAAQ,GAIT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAElB,MAAM,YAAY,CAAC;AACpB,+DAA+D;AAC/D,kEAAkE;AAClE,oEAAoE;AACpE,qBAAqB;AACrB,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,oEAAoE;AACpE,yBAAyB;AACzB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GAWxB,MAAM,wBAAwB,CAAC;AAChC,+DAA+D;AAC/D,gEAAgE;AAChE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,cAAc,EACd,wBAAwB,EACxB,uBAAuB,GAUxB,MAAM,qBAAqB,CAAC;AAC7B,oEAAoE;AACpE,sEAAsE;AACtE,kEAAkE;AAClE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,cAAc,IAAI,0BAA0B,GAS7C,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,GAUjB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gDAAgD;AAChD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,iBAAiB;AAEjB,OAAO,EAAE,YAAY,EAAuD,MAAM,cAAc,CAAC;AACjG,OAAO,EACL,aAAa,EACb,sBAAsB,GAQvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EACL,MAAM,EACN,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,cAAc,EACd,aAAa,EACb,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,MAAM,EACN,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,QAAQ,GAIT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAElB,MAAM,YAAY,CAAC;AACpB,+DAA+D;AAC/D,kEAAkE;AAClE,oEAAoE;AACpE,qBAAqB;AACrB,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,oEAAoE;AACpE,yBAAyB;AACzB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GAWxB,MAAM,wBAAwB,CAAC;AAChC,+DAA+D;AAC/D,gEAAgE;AAChE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,cAAc,EACd,wBAAwB,EACxB,uBAAuB,GAUxB,MAAM,qBAAqB,CAAC;AAC7B,oEAAoE;AACpE,sEAAsE;AACtE,kEAAkE;AAClE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,cAAc,IAAI,0BAA0B,GAW7C,MAAM,uBAAuB,CAAC;AAC/B,oEAAoE;AACpE,qEAAqE;AACrE,4DAA4D;AAC5D,iEAAiE;AACjE,mEAAmE;AACnE,qEAAqE;AACrE,oBAAoB;AACpB,OAAO,EACL,iBAAiB,GAIlB,MAAM,oBAAoB,CAAC;AAC5B,0EAA0E;AAC1E,yEAAyE;AACzE,mEAAmE;AACnE,uEAAuE;AACvE,2CAA2C;AAC3C,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,GAUjB,MAAM,aAAa,CAAC"}
@@ -8,6 +8,32 @@ export declare const SEVERITY_EMOJI: {
8
8
  readonly minor: "🟡";
9
9
  readonly preexisting: "🟣";
10
10
  };
11
+ /**
12
+ * One parsed finding suitable for the SPEC §1.8.1 Resolved / Still-open
13
+ * blocks. Mirrors `ParsedFinding` from `./diff-findings.ts`; declared
14
+ * here as a structural interface (not re-imported) so callers without
15
+ * `diff-findings` in scope can still construct the input.
16
+ */
17
+ export interface RenderedFindingRef {
18
+ file: string;
19
+ line: number;
20
+ severity: 'critical' | 'minor' | 'preexisting';
21
+ skillName: string;
22
+ summary: string;
23
+ }
24
+ /**
25
+ * SPEC §6.7.3 cache telemetry — per-invocation token counts from the
26
+ * Anthropic-family `usage` block. When present, the renderer emits a
27
+ * `<!-- cache: ... -->` HTML comment immediately below the
28
+ * `<!-- review-sha: ... -->` metadata line so downstream cost analysis
29
+ * tooling can audit cache behaviour from the committed doc-file.
30
+ */
31
+ export interface CacheStats {
32
+ /** `cache_read_input_tokens` from the Anthropic usage block. */
33
+ cachedInputTokens: number;
34
+ /** `cache_creation_input_tokens` from the Anthropic usage block. */
35
+ cacheCreationInputTokens: number;
36
+ }
11
37
  export interface RenderReviewFileInput {
12
38
  review: Review;
13
39
  prNumber: number;
@@ -15,6 +41,34 @@ export interface RenderReviewFileInput {
15
41
  headSha: string;
16
42
  /** GitHub PR URL — appended verbatim to the trailing rule. */
17
43
  prUrl: string;
44
+ /**
45
+ * Findings present in the prior `docs/reviews/PR-<n>.md` that are NOT
46
+ * present in this round's `review`. When non-empty, emits the SPEC
47
+ * §1.8.1 `**Resolved this round:**` block AFTER the severity buckets
48
+ * and BEFORE the trailing `---` separator. Omitted entirely when
49
+ * empty/undefined (SPEC §1.8.1: blocks MUST be omitted when empty).
50
+ *
51
+ * Produced by `diffFindings()` from `./diff-findings.ts`.
52
+ */
53
+ resolvedFindings?: RenderedFindingRef[];
54
+ /**
55
+ * Findings present in BOTH the prior `docs/reviews/PR-<n>.md` AND
56
+ * this round's `review` — the persistent ones. When non-empty, emits
57
+ * the SPEC §1.8.1 `**Still open:**` block AFTER `**Resolved this
58
+ * round:**` and before the trailing `---` separator. Omitted entirely
59
+ * when empty/undefined.
60
+ *
61
+ * Produced by `diffFindings()` from `./diff-findings.ts`.
62
+ */
63
+ stillOpenFindings?: RenderedFindingRef[];
64
+ /**
65
+ * SPEC §6.7.3 cache telemetry. When present, emits a
66
+ * `<!-- cache: <read> read · <created> created -->` HTML comment
67
+ * immediately below the `<!-- review-sha: ... -->` metadata line.
68
+ * Omitted entirely when undefined (this comment is OPTIONAL per the
69
+ * SPEC, but SHOULD be implemented for cost-analysis tooling).
70
+ */
71
+ cacheStats?: CacheStats;
18
72
  }
19
73
  /**
20
74
  * Renders the review object to the SPEC §1.8.1 markdown template.
@@ -1 +1 @@
1
- {"version":3,"file":"review-writeback.d.ts","sourceRoot":"","sources":["../../src/core/review-writeback.ts"],"names":[],"mappings":"AAwBA,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,MAAM,EACZ,MAAM,wBAAwB,CAAC;AAEhC,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,0DAA0D;AAC1D,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAM1C,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAsErE;AAkCD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,MAAM,EAAE,UAAU,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,qFAAqF;IACrF,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG,aAAa,GAAG,OAAO,CAAC;AAE3E,iCAAiC;AACjC,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,aAAa,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACvC,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzC,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,cAAc,CAAC;IACrB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,2EAA2E;IAC3E,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,MAAM,CAmER;AA0FD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D"}
1
+ {"version":3,"file":"review-writeback.d.ts","sourceRoot":"","sources":["../../src/core/review-writeback.ts"],"names":[],"mappings":"AAwBA,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,MAAM,EACZ,MAAM,wBAAwB,CAAC;AAEhC,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,0DAA0D;AAC1D,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAM1C,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACxC;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACzC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CA6GrE;AA0DD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,MAAM,EAAE,UAAU,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,qFAAqF;IACrF,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG,aAAa,GAAG,OAAO,CAAC;AAE3E,iCAAiC;AACjC,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,aAAa,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACvC,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzC,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,cAAc,CAAC;IACrB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,2EAA2E;IAC3E,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,MAAM,CAmER;AA0FD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D"}