ghagga 3.2.0 → 3.4.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 (49) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/audit.js +6 -2
  3. package/dist/commands/audit.js.map +1 -1
  4. package/dist/commands/index-cmd.d.ts +49 -0
  5. package/dist/commands/index-cmd.d.ts.map +1 -0
  6. package/dist/commands/index-cmd.js +367 -0
  7. package/dist/commands/index-cmd.js.map +1 -0
  8. package/dist/commands/index-cmd.test.d.ts +17 -0
  9. package/dist/commands/index-cmd.test.d.ts.map +1 -0
  10. package/dist/commands/index-cmd.test.js +535 -0
  11. package/dist/commands/index-cmd.test.js.map +1 -0
  12. package/dist/commands/indexer-registry.d.ts +93 -0
  13. package/dist/commands/indexer-registry.d.ts.map +1 -0
  14. package/dist/commands/indexer-registry.js +342 -0
  15. package/dist/commands/indexer-registry.js.map +1 -0
  16. package/dist/commands/indexer-registry.test.d.ts +10 -0
  17. package/dist/commands/indexer-registry.test.d.ts.map +1 -0
  18. package/dist/commands/indexer-registry.test.js +538 -0
  19. package/dist/commands/indexer-registry.test.js.map +1 -0
  20. package/dist/commands/review-blast-radius.test.d.ts +13 -0
  21. package/dist/commands/review-blast-radius.test.d.ts.map +1 -0
  22. package/dist/commands/review-blast-radius.test.js +179 -0
  23. package/dist/commands/review-blast-radius.test.js.map +1 -0
  24. package/dist/commands/review.d.ts +136 -1
  25. package/dist/commands/review.d.ts.map +1 -1
  26. package/dist/commands/review.js +357 -2
  27. package/dist/commands/review.js.map +1 -1
  28. package/dist/commands/review.test.js +527 -1
  29. package/dist/commands/review.test.js.map +1 -1
  30. package/dist/commands/triage.d.ts +14 -0
  31. package/dist/commands/triage.d.ts.map +1 -0
  32. package/dist/commands/triage.js +248 -0
  33. package/dist/commands/triage.js.map +1 -0
  34. package/dist/commands/triage.test.d.ts +8 -0
  35. package/dist/commands/triage.test.d.ts.map +1 -0
  36. package/dist/commands/triage.test.js +272 -0
  37. package/dist/commands/triage.test.js.map +1 -0
  38. package/dist/index.js +38 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/lib/cli-github-client-port.d.ts +2 -0
  41. package/dist/lib/cli-github-client-port.d.ts.map +1 -1
  42. package/dist/lib/cli-github-client-port.js +12 -0
  43. package/dist/lib/cli-github-client-port.js.map +1 -1
  44. package/dist/ui/__tests__/theme.test.js +3 -2
  45. package/dist/ui/__tests__/theme.test.js.map +1 -1
  46. package/dist/ui/theme.d.ts.map +1 -1
  47. package/dist/ui/theme.js +1 -0
  48. package/dist/ui/theme.js.map +1 -1
  49. package/package.json +11 -10
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Unit tests for the blast-radius pure seams extracted from `reviewCommand`
3
+ * (design v2 D9): `resolveBlastRadiusEnabled`, `checkGraphStaleness`,
4
+ * `computeUncoveredLanguages`.
5
+ *
6
+ * `resolveBlastRadiusEnabled` touches the real filesystem (existsSync) — a
7
+ * REAL temp dir (mkdtempSync) is used, NOT a mocked `node:fs`, so the actual
8
+ * repoPath-relative resolution is exercised (CRITICAL-2 regression guard).
9
+ * This file deliberately does NOT `vi.mock('node:fs')` (unlike review.test.ts)
10
+ * to keep that guarantee honest.
11
+ */
12
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
13
+ import { tmpdir } from 'node:os';
14
+ import { join } from 'node:path';
15
+ import { GRAPH_VERSION } from 'ghagga-core';
16
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
17
+ import { checkGraphStaleness, computeUncoveredLanguages, resolveBlastRadiusEnabled, } from './review.js';
18
+ // ─── resolveBlastRadiusEnabled ───────────────────────────────────
19
+ describe('resolveBlastRadiusEnabled', () => {
20
+ let repoPath;
21
+ let otherCwd;
22
+ beforeEach(() => {
23
+ repoPath = mkdtempSync(join(tmpdir(), 'ghagga-blast-radius-repo-'));
24
+ otherCwd = mkdtempSync(join(tmpdir(), 'ghagga-blast-radius-cwd-'));
25
+ });
26
+ afterEach(() => {
27
+ rmSync(repoPath, { recursive: true, force: true });
28
+ rmSync(otherCwd, { recursive: true, force: true });
29
+ });
30
+ function withGraph() {
31
+ mkdirSync(join(repoPath, '.ghagga'), { recursive: true });
32
+ writeFileSync(join(repoPath, '.ghagga', 'graph.json'), '{}');
33
+ }
34
+ it('auto-enables when .ghagga/graph.json exists under repoPath', () => {
35
+ withGraph();
36
+ const result = resolveBlastRadiusEnabled(repoPath, {}, {});
37
+ expect(result).toBe(true);
38
+ });
39
+ it('stays off when no graph.json exists', () => {
40
+ const result = resolveBlastRadiusEnabled(repoPath, {}, {});
41
+ expect(result).toBe(false);
42
+ });
43
+ it('resolves against repoPath, NOT process.cwd() (CRITICAL-2)', () => {
44
+ withGraph();
45
+ const originalCwd = process.cwd();
46
+ try {
47
+ process.chdir(otherCwd);
48
+ // The graph lives under repoPath, not the (different) current cwd —
49
+ // the v1 bug used resolve('.') and would have returned false here.
50
+ const result = resolveBlastRadiusEnabled(repoPath, {}, {});
51
+ expect(result).toBe(true);
52
+ }
53
+ finally {
54
+ process.chdir(originalCwd);
55
+ }
56
+ });
57
+ it('--no-blast-radius (blastRadius: false) wins over an existing graph', () => {
58
+ withGraph();
59
+ const result = resolveBlastRadiusEnabled(repoPath, { blastRadius: false }, {});
60
+ expect(result).toBe(false);
61
+ });
62
+ it('.ghagga.json enableBlastRadius: false wins over an existing graph', () => {
63
+ withGraph();
64
+ const result = resolveBlastRadiusEnabled(repoPath, {}, { enableBlastRadius: false });
65
+ expect(result).toBe(false);
66
+ });
67
+ it('.ghagga.json enableBlastRadius: true wins even without a graph', () => {
68
+ const result = resolveBlastRadiusEnabled(repoPath, {}, { enableBlastRadius: true });
69
+ expect(result).toBe(true);
70
+ });
71
+ it('explicit --no-blast-radius overrides .ghagga.json enableBlastRadius: true', () => {
72
+ const result = resolveBlastRadiusEnabled(repoPath, { blastRadius: false }, { enableBlastRadius: true });
73
+ expect(result).toBe(false);
74
+ });
75
+ });
76
+ // ─── checkGraphStaleness ──────────────────────────────────────────
77
+ describe('checkGraphStaleness', () => {
78
+ function makeMetadata(overrides = {}) {
79
+ return {
80
+ lastIndexedCommit: 'abc123',
81
+ lastIndexedAt: new Date().toISOString(),
82
+ schemaVersion: GRAPH_VERSION,
83
+ fileCount: 1,
84
+ languages: ['typescript'],
85
+ indexDurationMs: 10,
86
+ ...overrides,
87
+ };
88
+ }
89
+ it('returns no warnings when metadata is fresh and HEAD matches', () => {
90
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123' });
91
+ expect(checkGraphStaleness(metadata, 'abc123')).toEqual([]);
92
+ });
93
+ it('warns on HEAD mismatch', () => {
94
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123' });
95
+ const warnings = checkGraphStaleness(metadata, 'def456');
96
+ expect(warnings.some((w) => w.kind === 'head-mismatch')).toBe(true);
97
+ });
98
+ it('skips the HEAD-mismatch check when currentHead is empty (non-git/detached)', () => {
99
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123' });
100
+ const warnings = checkGraphStaleness(metadata, '');
101
+ expect(warnings.some((w) => w.kind === 'head-mismatch')).toBe(false);
102
+ });
103
+ it('warns on age > 7 days', () => {
104
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
105
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123', lastIndexedAt: thirtyDaysAgo });
106
+ const warnings = checkGraphStaleness(metadata, 'abc123');
107
+ expect(warnings.some((w) => w.kind === 'stale-age')).toBe(true);
108
+ });
109
+ it('warns distinctly when metadata is null (B-005)', () => {
110
+ const warnings = checkGraphStaleness(null, 'abc123');
111
+ expect(warnings).toHaveLength(1);
112
+ expect(warnings[0]?.kind).toBe('no-metadata');
113
+ });
114
+ it('warns on graphVersion mismatch (B-003)', () => {
115
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123', graphVersion: GRAPH_VERSION + 1 });
116
+ const warnings = checkGraphStaleness(metadata, 'abc123');
117
+ expect(warnings.some((w) => w.kind === 'version-mismatch')).toBe(true);
118
+ });
119
+ it('does not warn on graphVersion match', () => {
120
+ const metadata = makeMetadata({ lastIndexedCommit: 'abc123', graphVersion: GRAPH_VERSION });
121
+ const warnings = checkGraphStaleness(metadata, 'abc123');
122
+ expect(warnings.some((w) => w.kind === 'version-mismatch')).toBe(false);
123
+ });
124
+ });
125
+ // ─── computeUncoveredLanguages ────────────────────────────────────
126
+ describe('computeUncoveredLanguages', () => {
127
+ function makeGraph(nodeLanguages) {
128
+ return {
129
+ version: GRAPH_VERSION,
130
+ rootDir: '.',
131
+ nodes: Object.fromEntries(Object.entries(nodeLanguages).map(([path, language]) => [
132
+ path,
133
+ {
134
+ hash: 'h',
135
+ // biome-ignore lint/suspicious/noExplicitAny: fixture language is dynamic
136
+ language: language,
137
+ imports: [],
138
+ exports: [],
139
+ calls: [],
140
+ isTest: false,
141
+ },
142
+ ])),
143
+ };
144
+ }
145
+ it('flags a changed file whose language has zero graph coverage', () => {
146
+ const graph = makeGraph({ 'src/main.go': 'go' });
147
+ const uncovered = computeUncoveredLanguages(graph, ['app.py']);
148
+ expect(uncovered).toContain('python');
149
+ });
150
+ it('does not flag a changed file whose language IS covered', () => {
151
+ const graph = makeGraph({ 'src/main.go': 'go' });
152
+ const uncovered = computeUncoveredLanguages(graph, ['src/other.go']);
153
+ expect(uncovered).not.toContain('go');
154
+ });
155
+ it('regex-fallback graph (languages populated from real nodes) is NOT all-flagged (CRITICAL-1)', () => {
156
+ // A regex-fallback-built graph still has real per-file `language` on
157
+ // every node (unlike dispatch's `indexedLanguages`, which is empty for
158
+ // the regex path) — so coverage must be computed from graph CONTENTS.
159
+ const graph = makeGraph({ 'src/a.ts': 'typescript', 'src/b.py': 'python' });
160
+ const uncovered = computeUncoveredLanguages(graph, ['src/a.ts', 'src/b.py']);
161
+ expect(uncovered).toEqual([]);
162
+ });
163
+ it('flags a subpackage-only language not present anywhere in the graph', () => {
164
+ const graph = makeGraph({ 'src/main.go': 'go' });
165
+ const uncovered = computeUncoveredLanguages(graph, [
166
+ 'packages/api/requirements.txt',
167
+ 'src/main.go',
168
+ ]);
169
+ // requirements.txt has no detectable language extension — must not crash
170
+ // and must not be spuriously flagged as a language.
171
+ expect(uncovered).toEqual([]);
172
+ });
173
+ it('flags multiple distinct uncovered languages without duplicates', () => {
174
+ const graph = makeGraph({ 'src/main.go': 'go' });
175
+ const uncovered = computeUncoveredLanguages(graph, ['a.py', 'b.py', 'c.rs']);
176
+ expect(uncovered.sort()).toEqual(['python', 'rust']);
177
+ });
178
+ });
179
+ //# sourceMappingURL=review-blast-radius.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-blast-radius.test.js","sourceRoot":"","sources":["../../src/commands/review-blast-radius.test.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * environment, and optional .ghagga.json file, then runs the
6
6
  * core review pipeline and formats the output.
7
7
  */
8
- import type { LLMProvider, ReviewMode } from 'ghagga-core';
8
+ import type { DependencyGraph, GraphMetadata, LLMProvider, ReviewMode } from 'ghagga-core';
9
9
  export interface ReviewOptions {
10
10
  mode: ReviewMode;
11
11
  provider: LLMProvider;
@@ -53,6 +53,141 @@ export interface ReviewOptions {
53
53
  lenses?: string;
54
54
  /** Path to custom lens definitions directory (from --lens-dir flag). */
55
55
  lensDir?: string;
56
+ /**
57
+ * Fail-closed review anchor. When a hex SHA, HEAD must resolve to that
58
+ * commit before the pipeline runs. `"auto"` skips the mismatch check.
59
+ */
60
+ anchor?: string;
61
+ /**
62
+ * Explicit blast-radius override from `--no-blast-radius`. `false` forces
63
+ * blast-radius off regardless of graph presence / config; `undefined`
64
+ * defers to `.ghagga.json`/auto-detection (see `resolveBlastRadiusEnabled`).
65
+ */
66
+ blastRadius?: boolean;
67
+ }
68
+ /**
69
+ * A single entry in the `.ghagga.json` `providerChain` array — routes ONE
70
+ * multi-voice reviewer (consensus vote / fan-out lens) to a specific
71
+ * cross-engine target via mcp-llm-bridge. Mirrors `ghagga-core`'s
72
+ * `ProviderChainEntry`, minus `apiKey`: the gateway token is resolved at
73
+ * runtime (CLI `--api-key` / stored login token), never stored in plaintext
74
+ * in the config file.
75
+ */
76
+ interface GhaggaConfigProviderChainEntry {
77
+ /** Only 'gateway' entries are meaningful for cross-engine routing. */
78
+ provider: 'gateway';
79
+ /** Model identifier passed to the bridge (e.g. "claude-sonnet-4-6"). */
80
+ model: string;
81
+ /**
82
+ * Bridge-side provider id to route to (e.g. 'cli-claude', 'codex-cli').
83
+ * Short-circuits the bridge's model-based routing.
84
+ */
85
+ targetProvider?: string;
86
+ /** Gateway base URL. Defaults to the first entry's / flat gateway config when omitted. */
87
+ gatewayUrl?: string;
88
+ /** OpenCode model in `provider/model` format (cli-bridge only — unused for gateway). */
89
+ cliModel?: string;
90
+ }
91
+ interface GhaggaConfig {
92
+ mode?: string;
93
+ provider?: string;
94
+ model?: string;
95
+ enableSemgrep?: boolean;
96
+ enableTrivy?: boolean;
97
+ enableCpd?: boolean;
98
+ customRules?: string[];
99
+ ignorePatterns?: string[];
100
+ reviewLevel?: string;
101
+ disabledTools?: string[];
102
+ enabledTools?: string[];
103
+ lenses?: string[];
104
+ /**
105
+ * When true, fan-out pins every lens to generateFns[0] (the first
106
+ * providerChain / generateFn entry). Default/omit keeps round-robin.
107
+ */
108
+ pinLensesToFirst?: boolean;
109
+ /**
110
+ * Opt-in unlensed whole-diff contrarian voices in fan-out.
111
+ * When set, must be a JSON number that is an integer >= 1.
112
+ */
113
+ contrarianCount?: number;
114
+ /**
115
+ * Opt-in batched 2-of-K refuters over critical fan-out findings.
116
+ * When set, must be a JSON number that is an integer >= 2.
117
+ */
118
+ refuterCount?: number;
119
+ /**
120
+ * Per-voice cross-engine provider chain (BL-CLI-PROVIDER-CHAIN). When set,
121
+ * consensus (3-vote) and fan-out (N-lens) modes round-robin each voice
122
+ * across these gateway entries instead of hammering a single engine.
123
+ * Fan-out opt-in `pinLensesToFirst: true` pins every lens to generateFns[0]
124
+ * instead of round-robin.
125
+ */
126
+ providerChain?: GhaggaConfigProviderChainEntry[];
127
+ /**
128
+ * Enable blast-radius filtering (design v2 D7). `true`/`false` override
129
+ * auto-detection (`.ghagga/graph.json` presence); omit to auto-detect.
130
+ * `--no-blast-radius` (ReviewOptions.blastRadius === false) always wins
131
+ * over this config key.
132
+ */
133
+ enableBlastRadius?: boolean;
134
+ /** Fail-closed review anchor. Hex SHA or `"auto"`. CLI `--anchor` wins. */
135
+ anchor?: string;
56
136
  }
57
137
  export declare function reviewCommand(targetPath: string, options: ReviewOptions): Promise<void>;
138
+ /** One staleness/coverage signal for `checkGraphStaleness` — WARN, never block. */
139
+ export interface StalenessWarning {
140
+ kind: 'head-mismatch' | 'stale-age' | 'no-metadata' | 'version-mismatch';
141
+ message: string;
142
+ }
143
+ /**
144
+ * Resolve whether blast-radius should be enabled for this review run.
145
+ *
146
+ * Precedence (highest to lowest):
147
+ * 1. `--no-blast-radius` (options.blastRadius === false) → OFF
148
+ * 2. `.ghagga.json` `enableBlastRadius` (explicit true/false) → that value
149
+ * 3. Auto-ON when `<repoPath>/.ghagga/graph.json` exists
150
+ * 4. OFF (no graph, no override)
151
+ *
152
+ * CRITICAL-2 (JD B-001): the existence check is resolved against `repoPath`
153
+ * — the review target — NOT `process.cwd()`, so `ghagga review <other-dir>`
154
+ * from an unrelated cwd still auto-detects correctly.
155
+ */
156
+ export declare function resolveBlastRadiusEnabled(repoPath: string, options: Pick<ReviewOptions, 'blastRadius'>, fileConfig: Pick<GhaggaConfig, 'enableBlastRadius'>): boolean;
157
+ /**
158
+ * Detect staleness/coverage-verification signals for the loaded graph.
159
+ * ALWAYS warn-and-continue (design v2 D2) — never blocks the review.
160
+ *
161
+ * - HEAD mismatch: `metadata.lastIndexedCommit` non-empty and differs from
162
+ * `currentHead`. An EMPTY `currentHead` (non-git/detached checkout) skips
163
+ * this specific check — there's nothing meaningful to compare against.
164
+ * - Age: delegates to `isGraphStale` (>`GRAPH_STALE_DAYS` days).
165
+ * - Metadata absent (B-005): `metadata === null` — graph.json loaded but no
166
+ * sibling metadata.json (e.g. pre-feature `ghagga index` run). Returns
167
+ * EARLY with only this warning — there's nothing else to check.
168
+ * - Version mismatch (B-003): `metadata.graphVersion` set and differs from
169
+ * the current `GRAPH_VERSION` (equivalent to comparing against the loaded
170
+ * graph's `version`, which `validateGraph` already pins to `GRAPH_VERSION`).
171
+ */
172
+ export declare function checkGraphStaleness(metadata: GraphMetadata | null, currentHead: string): StalenessWarning[];
173
+ /**
174
+ * Compute the set of languages present among `changedFiles` that have ZERO
175
+ * coverage in the loaded graph — i.e. files whose blast-radius dependent
176
+ * count is unreliable (shows zero, not "no dependents").
177
+ *
178
+ * Coverage is derived from the graph's ACTUAL node contents (design v2 D8,
179
+ * JD CRITICAL-1), not from indexer-dispatch bookkeeping:
180
+ * - The regex-fallback path sets `indexedLanguages: []` even though every
181
+ * node has a real `language` — keying off dispatch state would flag
182
+ * every changed file as uncovered.
183
+ * - `detectPresentLanguages` is root-only — a language that only exists in
184
+ * a subpackage (e.g. `packages/api/requirements.txt`) is never detected
185
+ * or skipped, so dispatch state gives no signal at all.
186
+ *
187
+ * Files whose language cannot be detected (`detectLanguage` returns
188
+ * `undefined` — unknown extension) are silently excluded; there is no
189
+ * language identity to report as uncovered.
190
+ */
191
+ export declare function computeUncoveredLanguages(graph: DependencyGraph, changedFiles: string[]): string[];
192
+ export {};
58
193
  //# sourceMappingURL=review.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACV,WAAW,EAIX,UAAU,EAKX,MAAM,aAAa,CAAC;AA6CrB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IAC7C,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IAEjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,yCAAyC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAqBD,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiQ7F"}
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"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"}