Penpot: The open-source design platform for Product teams that need scalable collaboration.
This report summarizes scanner evidence for penpot/penpot, a Clojure project with 61,905 GitHub stars. SynthScan examined 443,457 lines of code across 1897 source files, recording 554 pattern matches distributed across 16 syntactic categories. The legacy adjusted pattern signal is 2.2 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 269 finding(s).
Are imports declared and package/API names verified? 187 finding(s).
Are wrappers, guards, and repeated structures necessary? 196 finding(s).
Which changes need focused human review? 554 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 554 distinct pattern matches across 16 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | manage.sh | 82 | # ---------------------------------------------------------------------------- | COMMENT |
| MEDIUM | manage.sh | 119 | # ---------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | frontend/src/app/main/ui/dashboard/grid.scss | 14 | // ─── VARIABLES ───────────────────────────────── | COMMENT |
| MEDIUM⚡ | frontend/src/app/main/ui/dashboard/grid.scss | 20 | // ─── DASHBOARD GRID ──────────────────────────── | COMMENT |
| MEDIUM⚡ | frontend/src/app/main/ui/dashboard/grid.scss | 30 | // ─── GRID ROW ────────────────────────────────── | COMMENT |
| MEDIUM | frontend/src/app/main/ui/dashboard/grid.scss | 50 | // ─── CARD VIEW: GRID ITEM ───────────────────── | COMMENT |
| MEDIUM | frontend/src/app/main/ui/dashboard/grid.scss | 157 | // ─── CARD VIEW: PROJECT THUMBNAIL ────────────── | COMMENT |
| MEDIUM | frontend/src/app/main/ui/dashboard/grid.scss | 203 | // ─── LIST VIEW ───────────────────────────────── | COMMENT |
| MEDIUM | frontend/src/app/main/ui/dashboard/grid.scss | 290 | // ─── LIBRARY ─────────────────────────────────── | COMMENT |
| MEDIUM | frontend/src/app/main/ui/dashboard/grid.scss | 391 | // ─── MISC ────────────────────────────────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 8 | // ── theme (plugin-styles colour variables are gated on [data-theme]) ─────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 14 | // ── messages exchanged with the sandbox ──────────────────────────────────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 35 | // ── a single test row ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 163 | // ── a group of rows (one test case) ───────────────────────────────────────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 269 | // ── controller: builds the tree, wires selection and the run buttons ──────────── | COMMENT |
| MEDIUM | plugins/apps/composable-test-suite/src/main.ts | 359 | // ── wire up ───────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | mcp/packages/server/src/PenpotMcpServer.test.ts | 33 | // ── Pure function tests ──────────────────────────────────────── | COMMENT |
| MEDIUM | mcp/packages/server/src/PenpotMcpServer.test.ts | 47 | // ── Pure function tests: isReplEnabled ────────────────────────── | COMMENT |
| MEDIUM | mcp/packages/server/src/PenpotMcpServer.test.ts | 69 | // ── Integration tests: constructor gating ────────────────────── | COMMENT |
| MEDIUM | mcp/packages/server/src/PenpotMcpServer.test.ts | 217 | // ── Helpers ──────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 150 | // ── Newline ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 158 | // ── Escape: skip next character ────────────────────────────────── | COMMENT |
| MEDIUM⚡ | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 164 | // ── Inside comment: skip until newline ─────────────────────────── | COMMENT |
| MEDIUM⚡ | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 167 | // ── Inside string literal ──────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 174 | // ── Outside string / comment ───────────────────────────────────── | COMMENT |
| MEDIUM | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 188 | // ── Opening delimiter ──────────────────────────────────────────── | COMMENT |
| MEDIUM | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 222 | // ── Closing delimiter ──────────────────────────────────────────── | COMMENT |
| MEDIUM | …p/packages/server/src/tools/dev/CljCheckParentheses.ts | 234 | // ── EOF: no mismatch was found, but the stack is not empty ────────── | COMMENT |
| MEDIUM | scripts/gh.py | 98 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 100 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 131 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 133 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 215 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 217 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 253 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 255 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 488 | # ── No-milestone path ────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 494 | # ── Milestone path ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 545 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 547 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 706 | # ── Milestone path ────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 722 | # ── Number-based path ─────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 742 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 744 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 891 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 893 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 960 | # ── Single advisory detail ────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 970 | # ── List all advisories ───────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 991 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/gh.py | 993 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/taiga.py | 41 | # ── URL Parsing ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | scripts/taiga.py | 57 | # ── API call ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | scripts/taiga.py | 81 | # ── Output formatting ──────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/taiga.py | 184 | # ── CLI ─────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 38 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 40 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 128 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 130 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 158 | # ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/changelog.py | 160 | # ───────────────────────────────────────────── | COMMENT |
| 32 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 75 | // "Lorem ipsum" as a paragraph with a text span. | COMMENT |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 77 | const paragraph = createParagraphWith("Lorem ipsum"); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 83 | expect(paragraph.firstChild.firstChild.textContent).toBe("Lorem ipsum"); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 85 | // ["Lorem ipsum"] as a paragraph with a text span. | COMMENT |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 87 | const paragraph = createParagraphWith(["Lorem ipsum"]); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 93 | expect(paragraph.firstChild.firstChild.textContent).toBe("Lorem ipsum"); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 95 | // ["Lorem ipsum","\n","dolor sit amet"] as a paragraph with multiple text spans. | COMMENT |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 95 | // ["Lorem ipsum","\n","dolor sit amet"] as a paragraph with multiple text spans. | COMMENT |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 97 | const paragraph = createParagraphWith(["Lorem ipsum", "\n", "dolor sit amet"]); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 97 | const paragraph = createParagraphWith(["Lorem ipsum", "\n", "dolor sit amet"]); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 103 | expect(paragraph.children.item(0).firstChild.textContent).toBe("Lorem ipsum"); | CODE |
| LOW⚡ | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 108 | expect(paragraph.children.item(2).firstChild.textContent).toBe("dolor sit amet"); | CODE |
| LOW | …d/text-editor/src/editor/content/dom/Paragraph.test.js | 186 | createTextSpan(new Text("Lorem ipsum sit")), | CODE |
| LOW⚡ | …end/text-editor/src/editor/content/dom/Content.test.js | 49 | "Lorem ipsum", | CODE |
| LOW⚡ | …end/text-editor/src/editor/content/dom/Content.test.js | 50 | "Dolor sit amet", | CODE |
| LOW⚡ | …end/text-editor/src/editor/content/dom/Content.test.js | 55 | "<div>Lorem ipsum</div><div>Dolor sit amet</div><div>Sed iaculis blandit odio ornare sagittis.</div>", | CODE |
| LOW⚡ | …end/text-editor/src/editor/content/dom/Content.test.js | 55 | "<div>Lorem ipsum</div><div>Dolor sit amet</div><div>Sed iaculis blandit odio ornare sagittis.</div>", | CODE |
| LOW | …end/text-editor/src/editor/content/dom/Content.test.js | 75 | "Lorem ipsumDolor sit ametSed iaculis blandit odio ornare sagittis.", | CODE |
| LOW | …end/text-editor/src/editor/content/dom/Content.test.js | 75 | "Lorem ipsumDolor sit ametSed iaculis blandit odio ornare sagittis.", | CODE |
| LOW | …tor/src/editor/controllers/SelectionController.test.js | 339 | expect(textEditorMock.root.textContent).toBe("Lorem ipsum dolor"); | CODE |
| LOW | …tor/src/editor/controllers/SelectionController.test.js | 464 | expect(textEditorMock.root.textContent).toBe("Lorem ipsum dolor"); | CODE |
| LOW | …tor/src/editor/controllers/SelectionController.test.js | 1251 | ["Lorem ipsum"], | CODE |
| LOW | …tor/src/editor/controllers/SelectionController.test.js | 1252 | ["dolor sit amet"], | CODE |
| LOW | frontend/playwright/ui/specs/numeric-input.spec.js | 281 | await expect(widthInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW | …ontend/playwright/ui/specs/dashboard-libraries.spec.js | 22 | const libraryItem = page.getByTitle(/Lorem Ipsum/); | CODE |
| LOW | frontend/playwright/ui/specs/workspace.spec.js | 536 | await page.evaluate(() => navigator.clipboard.writeText("Lorem ipsum dolor")); | CODE |
| LOW | frontend/playwright/ui/specs/workspace.spec.js | 541 | .getByText("Lorem ipsum dolor"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 385 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 394 | expect(copiedText).toBe("Lorem ipsum"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 498 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 550 | await expect.poll(() => readClipboard(page)).toBe("Lorem ipsum"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 559 | await expect.poll(() => readClipboard(page)).toBe("Lorem ipsum"); | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 635 | await expect.poll(() => copiedText(page)).toBe("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/multiseleccion.spec.js | 72 | await expect(widthInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/multiseleccion.spec.js | 73 | await expect(heightInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/multiseleccion.spec.js | 74 | await expect(xPosInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/multiseleccion.spec.js | 75 | await expect(yPosInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW | frontend/playwright/ui/specs/multiseleccion.spec.js | 201 | await expect(rotationInput).toHaveAttribute("placeholder", "Mixed"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 79 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 82 | await page.keyboard.type(" dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 84 | await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 84 | await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 96 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 99 | await page.keyboard.type("Dolor sit amet "); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 101 | await workspace.waitForSelectedShapeName("Dolor sit amet Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 101 | await workspace.waitForSelectedShapeName("Dolor sit amet Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 113 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 116 | await page.keyboard.type(" dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 118 | await workspace.waitForSelectedShapeName("Lorem dolor sit amet ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 124 | const textToPaste = " dolor sit amet"; | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 134 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 139 | await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 139 | await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 145 | const textToPaste = "Dolor sit amet "; | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 155 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 168 | const textToPaste = "Dolor sit amet"; | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 175 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 184 | await workspace.waitForSelectedShapeName("Dolor sit amet ipsum"); | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 190 | const textToPaste = "dolor sit amet"; | CODE |
| LOW⚡ | frontend/playwright/ui/specs/text-editor-v2.spec.js | 197 | await workspace.clickLeafLayer("Lorem ipsum"); | CODE |
| 117 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | frontend/text-editor/src/editor/content/dom/Style.js | 120 | function getStyleDefaultsDeclaration() { | CODE |
| LOW | frontend/text-editor/src/editor/content/dom/Content.js | 29 | function isContentFragmentFromDocumentTextSpan(document) { | CODE |
| LOW | frontend/text-editor/src/editor/content/dom/Content.js | 56 | export function mapContentFragmentFromDocument(document, root, styleDefaults) { | CODE |
| LOW | frontend/text-editor/src/editor/content/dom/Content.js | 201 | export function mapContentFragmentFromHTML( | CODE |
| LOW | frontend/text-editor/src/editor/content/dom/Content.js | 228 | export function mapContentFragmentFromString(string, styleDefaults) { | CODE |
| LOW | frontend/text-editor/src/editor/clipboard/paste.js | 21 | function getFormattedFragmentFromClipboardData( | CODE |
| LOW | frontend/text-editor/src/editor/clipboard/paste.js | 37 | function getPlainFragmentFromClipboardData(selectionController, clipboardData) { | CODE |
| LOW | frontend/text-editor/src/editor/clipboard/paste.js | 50 | function getFormattedOrPlainFragmentFromClipboardData( | CODE |
| LOW | frontend/text-editor/src/editor/clipboard/paste.test.js | 14 | function createPlainTextClipboardEvent(text) { | CODE |
| LOW | …/playwright/ui/specs/text-options-missing-font.spec.js | 17 | function convertToTypographyButton(workspace) { | CODE |
| LOW | frontend/playwright/ui/specs/text-editor-v3.spec.js | 709 | async function expectBoardShrinksWhileTyping(workspace, text, boardY) { | CODE |
| LOW | frontend/scripts/find-unused-translations.js | 51 | async function findExecutionTimeTranslations() { | CODE |
| LOW | frontend/scripts/_helpers.js | 596 | export async function compileFontsPreviewSprite() { | CODE |
| LOW | frontend/scripts/_worker.js | 42 | function configureModulesProcessor(options) { | CODE |
| LOW | frontend/packages/draft-js/index.js | 205 | export function removeBlurSelectionEntity(state) { | CODE |
| LOW | …/plugins-runtime/src/lib/load-plugin-real-path.spec.ts | 96 | function lastCompartmentGlobalThis(): Record<string, unknown> { | CODE |
| LOW | …ns/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts | 8 | function generateMockPluginManager() { | CODE |
| LOW | plugins/apps/composable-test-suite/ci/run-ci.ts | 125 | async function openNotificationsWebSocket(page: Page, created: Set<string>): Promise<void> { | CODE |
| LOW | …posable-tests/cases/caseMainChildMovedOutLeavesCopy.ts | 26 | export function createTestCaseMainChildMovedOutLeavesCopy(): TestCase { | CODE |
| LOW | …omposable-tests/cases/caseMainReorderKeepsCopySlots.ts | 16 | export function createTestCaseMainReorderKeepsCopySlots(): TestCase { | CODE |
| LOW | …able-tests/cases/caseCopyOverrideSurvivesMainChange.ts | 21 | export function createTestCaseCopyOverrideSurvivesMainChange(): TestCase { | CODE |
| LOW | …composable-tests/cases/caseRemoteMainCopySyncNested.ts | 34 | export function createTestCaseRemoteMainCopySyncNested(): TestCase { | CODE |
| LOW | …/composable-tests/cases/caseVariantSwitchPropagates.ts | 26 | export function createTestCaseVariantSwitchPropagates(): TestCase { | CODE |
| LOW | …-suite/src/composable-tests/cases/caseMainEditSyncs.ts | 27 | export function createTestCaseMainEditSyncs(): TestCase { | CODE |
| LOW | …ble-tests/cases/caseCopySubheadDeletePreservesSlots.ts | 22 | export function createTestCaseCopySubheadDeletePreservesSlots(): TestCase { | CODE |
| LOW | plugins/apps/plugin-api-test-suite/ci/run-ci.ts | 283 | async function openNotificationsWebSocket(page: Page): Promise<void> { | CODE |
| LOW | …ins/apps/plugin-api-test-suite/src/framework/runner.ts | 55 | function assertNoNewIntegrityErrors(before: Set<string>): void { | CODE |
| LOW | …pps/plugin-api-test-suite/src/tests/components.test.ts | 36 | function boardComponentMainWithChildren(ctx: TestContext): Board { | CODE |
| LOW | …pps/plugin-api-test-suite/src/tests/components.test.ts | 48 | function componentMainFromClonedNestedMain(ctx: TestContext): Board { | CODE |
| LOW | …ns/apps/plugin-api-test-suite/src/tests/tokens.test.ts | 36 | async function rectangleWithStrokeTokens(ctx: TestContext) { | CODE |
| LOW | plugins/apps/poc-state-plugin/src/plugin.ts | 604 | function combineSelectedAsVariants() { | CODE |
| LOW | mcp/types-generator/test_prepare_api_docs.py | 7 | def test_preserves_line_breaks_in_code_examples(self): | CODE |
| LOW | mcp/types-generator/prepare_api_docs.py | 136 | def _convert_strings_to_block(self, obj): | CODE |
| LOW | mcp/packages/plugin/src/ReconnectPolicy.ts | 16 | export function shouldReconnectAfterClose(code: number): boolean { | CODE |
| LOW | mcp/packages/server/src/PenpotMcpServer.ts | 50 | export function shouldRegisterDeveloperTools(isDevEnv: boolean, isMultiUserMode: boolean): boolean { | CODE |
| LOW | scripts/gh.py | 355 | def fetch_no_milestone_issues(states: str, labels: str | None = None) -> list[dict]: | CODE |
| LOW | scripts/gh.py | 470 | def load_existing_issue_numbers(filepath: str) -> set[int]: | CODE |
| LOW⚡ | scripts/test_ci.py | 63 | def test_build_all_has_one_command_per_module_without_common(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 72 | def test_build_is_not_part_of_the_default_tasks(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 80 | def test_build_flag_runs_build_only(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 88 | def test_no_build_flag_is_accepted(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 95 | def test_dry_run_executes_nothing(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 104 | def test_module_is_accepted_and_lint_is_skipped(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 115 | def test_module_is_included_in_all(self): | CODE |
| LOW⚡ | scripts/test_ci.py | 122 | def test_build_runs_types_check_before_esbuild(self): | CODE |
| LOW | scripts/test_ci.py | 142 | def test_help_documents_the_build_flags(self): | CODE |
| LOW | scripts/test_ci.py | 150 | def test_help_lists_every_module(self): | CODE |
| LOW | scripts/test_gh.py | 54 | def test_link_issue_to_pr_adds_reference(self, run_graphql): | CODE |
| LOW | scripts/test_gh.py | 72 | def test_link_issue_to_pr_fails_when_mutation_did_not_link(self, run_graphql): | CODE |
| LOW | scripts/test_gh.py | 82 | def test_cmd_link_issue_outputs_result(self, link_issue): | CODE |
| LOW | scripts/test_gh.py | 101 | def test_cmd_link_issue_exits_on_error(self, _link_issue): | CODE |
| LOW | scripts/test_sync_pnpm_version.py | 93 | def test_stamp_updates_stale_inserts_missing_keeps_synced(self): | CODE |
| LOW | scripts/test_sync_pnpm_version.py | 114 | def test_check_passes_once_synced(self): | CODE |
| LOW | scripts/test_sync_pnpm_version.py | 119 | def test_check_fails_on_drift_without_writing(self): | CODE |
| LOW | scripts/test_sync_pnpm_version.py | 132 | def test_check_reports_missing_field(self): | CODE |
| LOW | scripts/test_sync_pnpm_version.py | 137 | def test_target_dirs_are_never_stamped(self): | CODE |
| LOW⚡ | scripts/test_check_commit.py | 57 | def test_line_at_limit_passes(self): | CODE |
| LOW⚡ | scripts/test_check_commit.py | 62 | def test_line_one_over_limit_fails(self): | CODE |
| LOW⚡ | scripts/test_check_commit.py | 68 | def test_long_body_line_fails(self): | CODE |
| LOW⚡ | scripts/test_check_commit.py | 74 | def test_subject_is_not_checked(self): | CODE |
| 38 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | manage.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | manage.sh | 81 | COMMENT | |
| LOW | manage.sh | 101 | # devenv-main-running true if 'main' is up | COMMENT |
| LOW | manage.sh | 321 | COMMENT | |
| LOW | manage.sh | 421 | [[ "$instance" =~ ^ws([0-9]+)$ ]] && n="${BASH_REMATCH[1]}" | COMMENT |
| LOW | manage.sh | 481 | # `-c` overrides built at launch time -- see start-coding-agent. | COMMENT |
| LOW | manage.sh | 501 | # <workspace>/.vscode/mcp.json auto-loaded by VS Code Copilot; | COMMENT |
| LOW | manage.sh | 1081 | # replaces the developer's global client config; see .devenv/README.md for | COMMENT |
| LOW | docker/devenv/docker-compose.opencode.yml | 1 | # Optional compose overlay, included by manage.sh's instance-compose ONLY | COMMENT |
| LOW | docker/devenv/files/serena_config.yml | 1 | language_backend: LSP | COMMENT |
| LOW | docker/devenv/files/serena_config.yml | 21 | # window being updated. Since we can't control how agno or Claude Desktop start Serena, | COMMENT |
| LOW | docker/devenv/files/serena_config.yml | 41 | COMMENT | |
| LOW | docker/devenv/files/serena_config.yml | 81 | # list of regex patterns which, when matched, mark a memory entry as read‑only. | COMMENT |
| LOW | docker/devenv/files/serena_config.yml | 121 | COMMENT | |
| LOW | docker/images/docker-compose.yaml | 1 | ## Common flags: | COMMENT |
| LOW | docker/images/docker-compose.yaml | 61 | volumes: | COMMENT |
| LOW | docker/images/docker-compose.yaml | 161 | COMMENT | |
| LOW | docker/images/docker-compose.yaml | 241 | # Don't touch it; this uses an internal docker network to | COMMENT |
| LOW | docker/images/files/nginx-entrypoint.sh | 101 | envsubst "\$PENPOT_INTERNAL_RESOLVER" \ | COMMENT |
| LOW | docker/images/files/config.js | 1 | // Frontend configuration | COMMENT |
| LOW | frontend/vitest.shims.d.ts | 1 | /// <reference types="@vitest/browser/providers/playwright" /> | COMMENT |
| LOW | frontend/resources/styles/main-default.scss | 21 | // Layouts | COMMENT |
| LOW | frontend/resources/styles/common/refactor/themes.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | …ht/ui/specs/workspace-texts-typography-persist.spec.js | 1 | import { test, expect } from "@playwright/test"; | COMMENT |
| LOW | …/playwright/ui/specs/text-options-missing-font.spec.js | 1 | import { test, expect } from "@playwright/test"; | COMMENT |
| LOW | frontend/playwright/scripts/MockWebSocket.js | 21 | } | COMMENT |
| LOW | frontend/scripts/build-fonts-preview.js | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/scripts/check-translations/words.ru.txt | 1 | # Word catalog for check-translations.js (Russian). | COMMENT |
| LOW | frontend/packages/ui/src/lib/menu/Menu.tsx | 61 | // closing both the root and the submenu popovers at once (rather than just | COMMENT |
| LOW | frontend/packages/ui/src/lib/menu/Menu.tsx | 101 | // between Menu and ContextMenu, which each keep their own stack (a | COMMENT |
| LOW | frontend/packages/ui/src/lib/menu/Menu.tsx | 241 | className?: string; | COMMENT |
| LOW | frontend/packages/ui/src/lib/menu/Menu.tsx | 321 | triggerRef={triggerRef} | COMMENT |
| LOW | frontend/packages/mousetrap/index.js | 741 | // loop through matching callbacks for this key event | COMMENT |
| LOW | frontend/packages/mousetrap/index.js | 781 | // if the key you pressed matches the type of sequence without | COMMENT |
| LOW | frontend/src/app/util/path/arc_to_curve.js | 41 | COMMENT | |
| LOW | frontend/src/app/main/ui/releases/v2_8.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_12.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_4.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_5.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_13.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_9.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_2.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_18.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_14.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_15.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_19.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_3.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_0.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_16.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_17.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_1.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_10.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_6.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_7.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/releases/v2_11.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/auth/recovery.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | frontend/src/app/main/ui/auth/recovery_request.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | …nd/src/app/main/ui/workspace/sidebar/options/page.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | …/src/app/main/ui/workspace/sidebar/common/sidebar.scss | 1 | // This Source Code Form is subject to the terms of the Mozilla Public | COMMENT |
| LOW | …/src/app/main/ui/workspace/sidebar/common/sidebar.scss | 61 | grid-template-columns: repeat(#{$column-number}, #{$column-width}); | COMMENT |
| 56 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | …pps/plugin-api-test-suite/src/tests/components.test.ts | 216 | ctx.penpot.library.local.components.some((c) => c.id === comp.id), | CODE |
| CRITICAL | …pps/plugin-api-test-suite/src/tests/components.test.ts | 238 | ctx.penpot.library.local.components.some((c) => c.id === comp.id), | CODE |
| CRITICAL | …pps/plugin-api-test-suite/src/tests/components.test.ts | 243 | ctx.penpot.library.local.components.some((c) => c.id === comp.id), | CODE |
| CRITICAL⚡ | …pps/plugin-api-test-suite/src/tests/components.test.ts | 431 | ctx.penpot.library.local.components.some((c) => c.id === comp.id), | CODE |
| CRITICAL⚡ | …pps/plugin-api-test-suite/src/tests/components.test.ts | 434 | const deleted = ctx.penpot.library.local.deletedComponents.find( | CODE |
| CRITICAL⚡ | …pps/plugin-api-test-suite/src/tests/components.test.ts | 444 | ctx.penpot.library.local.components.some((c) => c.id === comp.id), | CODE |
| CRITICAL⚡ | …pps/plugin-api-test-suite/src/tests/components.test.ts | 447 | ctx.penpot.library.local.deletedComponents.some((c) => c.id === comp.id), | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | plugins/docs/create-angular-plugin.md | 11 | ### Step 1: Initialize the Plugin | COMMENT |
| LOW | plugins/docs/create-angular-plugin.md | 24 | ### Step 2: Configure the Manifest | COMMENT |
| LOW | plugins/docs/create-angular-plugin.md | 44 | ### Step 3: Update Project Configuration | COMMENT |
| LOW | plugins/docs/create-angular-plugin.md | 81 | ### Step 4: Modify TypeScript Configuration | COMMENT |
| LOW | plugins/docs/create-angular-plugin.md | 106 | ### Step 5: Add TS parser to eslint | COMMENT |
| LOW⚡ | plugins/docs/create-angular-plugin.md | 129 | ### Step 7: Run the plugin | COMMENT |
| LOW⚡ | plugins/docs/create-angular-plugin.md | 139 | ### Step 8: Load the Plugin in Penpot | COMMENT |
| LOW⚡ | plugins/docs/create-angular-plugin.md | 149 | ### Step 9: Build plugin | COMMENT |
| LOW | plugins/docs/create-plugin.md | 11 | ### Step 1: Initialize the Plugin | COMMENT |
| LOW | plugins/docs/create-plugin.md | 36 | ### Step 2: Create ESLint Config | COMMENT |
| LOW | plugins/docs/create-plugin.md | 58 | ### Step 3: Configure the Manifest | COMMENT |
| LOW | plugins/docs/create-plugin.md | 78 | ### Step 4: Update Vite Configuration | COMMENT |
| LOW | plugins/docs/create-plugin.md | 96 | ### Step 5: Modify TypeScript Configuration | COMMENT |
| LOW | plugins/docs/create-plugin.md | 106 | ### Step 6: Run a Static Server | COMMENT |
| LOW | plugins/docs/create-plugin.md | 120 | ### Step 7: Load the Plugin in Penpot | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 143 | ### Step 1: Understand the Goal | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 153 | ### Step 2: Check Completeness First | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 166 | ### Step 3: Review Task Quality | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 179 | ### Step 4: Validate Sequencing | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 191 | ### Step 5: Assess Actionability | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 202 | ### Step 6: Verify the Verification Story | COMMENT |
| LOW | .agents/skills/plan-review-criteria/SKILL.md | 213 | ### Step 7: Evaluate Proposed Code Quality *(if applicable)* | COMMENT |
| LOW⚡ | render-wasm/docs/visual_regression_tests.md | 23 | ### Step 1: Initialize the page | COMMENT |
| LOW⚡ | render-wasm/docs/visual_regression_tests.md | 33 | ### Step 2: Intercept requests | COMMENT |
| LOW⚡ | render-wasm/docs/visual_regression_tests.md | 41 | ### Step 3: Go to workspace and take a screenshot | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | manage.sh | 105 | # --attach, --agentic (enables MCP + Serena), -e, | COMMENT |
| MEDIUM | manage.sh | 592 | # gitignored, so git ls-files would not list it, yet the agentic devenv | COMMENT |
| MEDIUM | manage.sh | 793 | # Bring a single instance up: compose up + detached tmux start. When agentic | COMMENT |
| MEDIUM | manage.sh | 795 | # it is a plain non-agentic workspace (no MCP, no Serena). Workspace sync and | COMMENT |
| MEDIUM | manage.sh | 892 | # Bring a single workspace up. Without --agentic it's non-agentic (no MCP, no | COMMENT |
| MEDIUM | manage.sh | 893 | # Serena); with --agentic it enables MCP + Serena for AI-driven development. | COMMENT |
| MEDIUM | manage.sh | 958 | # Pre-flight: config.js must exist for agentic mode. The file is gitignored; | COMMENT |
| MEDIUM | manage.sh | 1127 | # ws1+; ws0 is the default target so 'run-devenv --agentic' is the right hint. | COMMENT |
| MEDIUM | docker/devenv/docker-compose.main.yml | 37 | # Serena MCP server (agentic mode only). Internal ports fixed by Serena. | COMMENT |
| MEDIUM | docker/devenv/files/serena_config.yml | 50 | # this may result in too many icons being displayed when using multi-agent setups. | COMMENT |
| MEDIUM | .serena/memories/devenv/core.md | 57 | ## Workspace orchestration (ws1+) | COMMENT |
| MEDIUM | .agents/README.md | 325 | ## 6. Common agentic flows | COMMENT |
| MEDIUM | docs/technical-guide/developer/devenv.md | 51 | ./manage.sh run-devenv --agentic --attach # bring up main devenv instance in agentic mode and attach tmux | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | backend/scripts/manage.py | 70 | except Exception as cause: | CODE |
| MEDIUM | backend/scripts/manage.py | 62 | def run_cmd(params): | CODE |
| MEDIUM⚡ | scripts/taiga.py | 66 | print(f"Error: HTTP {e.code} — {e.reason}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/taiga.py | 74 | print(f"Error: {e.reason}", file=sys.stderr) | CODE |
| MEDIUM⚡ | scripts/taiga.py | 77 | print(f"Error: invalid JSON response — {e}", file=sys.stderr) | CODE |
| MEDIUM | scripts/taiga.py | 242 | print(f"Error: ref must be a number, got '{ref_str}'", file=sys.stderr) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | scripts/gh.py | 828 | Unlike PRs (which resolve to null), a dead issue number fails the whole | STRING |
| HIGH⚡ | scripts/test_ci.py | 127 | "pnpm run types:check && pnpm run build", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | docker/devenv/files/serena_config.yml | 77 | # Same syntax as gitignore, so you can use * and **. | COMMENT |
| LOW | plugins/libs/plugins-runtime/vite.config.ts | 51 | // Don't forget to update your package.json as well. | COMMENT |
| MEDIUM | .serena/project.yml | 79 | # Same syntax as gitignore, so you can use * and **. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | backend/scripts/manage.py | 38 | CODE | |
| LOW | mcp/types-generator/prepare_api_docs.py | 23 | CODE | |
| LOW | mcp/types-generator/prepare_api_docs.py | 207 | CODE | |
| LOW | scripts/gh.py | 850 | CODE | |
| LOW | scripts/changelog.py | 53 | CODE | |
| LOW | scripts/changelog.py | 230 | CODE | |
| LOW | scripts/project-anomalies.py | 224 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | manage.sh | 329 | # Create the backend-test database for <instance> on the shared Postgres | COMMENT |
| MEDIUM | docker/devenv/files/start-tmux.sh | 26 | # Create the tmux session first so attach-devenv can connect immediately | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | frontend/src/app/main/ui/ds/layout/menu.stories.jsx | 177 | // Absolutely-positioned + transform centering, rather than flex | COMMENT |
| LOW | mcp/types-generator/prepare_api_docs.py | 50 | # for links, just return the text | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .serena/project.yml | 114 | # Set this to [] to disable base modes for this project. | COMMENT |
| LOW | .serena/project.yml | 115 | # Set this to a list of mode names to always include the respective modes for this project. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | scripts/replace-copyright.sh | 5 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .devenv/scripts/merge-mcp-config.py | 53 | CODE |