Bento, the office suite that fits in a file
This report summarizes scanner evidence for nyblnet/bento, a TypeScript project with 5,430 GitHub stars. SynthScan examined 410,140 lines of code across 898 source files, recording 1639 pattern matches distributed across 9 syntactic categories. The legacy adjusted pattern signal is 5.3 (Moderate 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.
Can maintainers explain intent, assumptions, and trade-offs? 1342 finding(s).
Are imports declared and package/API names verified? 34 finding(s).
Are wrappers, guards, and repeated structures necessary? 267 finding(s).
Which changes need focused human review? 1639 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 1639 distinct pattern matches across 9 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 |
|---|---|---|---|---|
| LOW | home/bridge.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/bridge.js | 21 | // ONE FILE, EVERY HOST. This used to live in home/ios/Resources/ and iOS was | COMMENT |
| LOW | home/bridge.js | 81 | COMMENT | |
| LOW | home/bridge.js | 101 | const begin = (want, launch) => { | COMMENT |
| LOW | home/bridge.js | 121 | // document — already handed out, or (Android) a read-only grant — must answer | COMMENT |
| LOW | home/webext/src/route.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/assistant.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/assistant.js | 21 | // otherwise spend the key and read the replies. So the first request from | COMMENT |
| LOW | home/webext/src/filegrant-window.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/page-bridge.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/page-bridge.js | 221 | // name against the sender's own file at that point. | COMMENT |
| LOW | home/webext/src/filegrant.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/background.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/background.js | 21 | // machine. So every message re-resolves from the grant; nothing is cached. | COMMENT |
| LOW | home/webext/src/background.js | 161 | // The candidate shares the sender's FILE NAME. That is not the same as being | COMMENT |
| LOW | home/webext/src/saveas.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/saveas.js | 21 | // page showSaveFilePicker({id:'bento-copy', suggestedName}) | COMMENT |
| LOW | home/webext/src/web.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/db.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/db.js | 21 | // cache derived, disposable: one entry per document, holding what was read | COMMENT |
| LOW | home/webext/src/place.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/place.js | 21 | // | COMMENT |
| LOW | home/webext/src/store.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/store.js | 21 | // names, delete(name). The names are exactly NAMES; values are opaque — | COMMENT |
| LOW | home/webext/src/store.js | 41 | // · the old path is GONE, as seen through the extension's own folder grant | COMMENT |
| LOW | home/webext/src/update.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/theme.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/library.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/library.js | 181 | if (hit) return hit | COMMENT |
| LOW | home/webext/src/library.js | 461 | // EQUAL is not a downgrade. Re-fetching the version already seen is the | COMMENT |
| LOW | home/webext/src/library.js | 481 | // This was the bug, and it is worth naming because it was invisible. The code | COMMENT |
| LOW | home/webext/src/library.js | 501 | // NO DOWNGRADES. Everything above passes for a replayed OLD release: it is | COMMENT |
| LOW | home/webext/src/home.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/home.js | 941 | if (upd?.version) { | COMMENT |
| LOW | home/webext/src/release.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/release.js | 21 | // party that serves the bytes. | COMMENT |
| LOW | home/webext/src/release.js | 61 | // The 359-byte difference is a Cloudflare Web Analytics beacon the edge injects | COMMENT |
| LOW | home/webext/src/prompt.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/prompt.js | 21 | // of its deck and passes through its own gate. The deck is read whole and | COMMENT |
| LOW | home/webext/src/consent.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/relay.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/grantflow.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/providers.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/providers.js | 441 | yield dec.decode(value, { stream: true }) | COMMENT |
| LOW | home/webext/src/i18n.js | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/webext/src/i18n.js | 21 | // | COMMENT |
| LOW | home/ios/SearchViewController.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/ios/BentoDocument.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/ios/Releases.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/ios/Releases.swift | 21 | /// THIS IS THE ONE THING THE HOST FETCHES. The app makes no other network | COMMENT |
| LOW | home/ios/Releases.swift | 101 | guard let url = payload["url"] as? String, !url.isEmpty else { | COMMENT |
| LOW | home/ios/Releases.swift | 161 | /// | COMMENT |
| LOW | home/ios/Releases.swift | 241 | /// The signature covers the EXACT payload string bytes — no canonicalisation, | COMMENT |
| LOW | home/ios/Releases.swift | 301 | timeoutInterval: limit) | COMMENT |
| LOW | home/ios/FolderGrants.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/ios/project.yml | 21 | buildPhase: resources | COMMENT |
| LOW | home/ios/project.yml | 41 | # committed. Unset, it expands to empty and simulator builds are | COMMENT |
| LOW | home/ios/ExportSessions.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| LOW | home/ios/ExportSessions.swift | 21 | /// is visibly for another file. | COMMENT |
| LOW | home/ios/BentoIndex.swift | 1 | // SPDX-License-Identifier: MIT | COMMENT |
| 1273 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | spaces/src/gantt.ts | 6 | // comes after inherits it ────────────────────────────────────────────── | COMMENT |
| MEDIUM | spaces/src/gantt.ts | 43 | // ── WHAT IS DELIBERATELY NOT HERE ──────────────────────────────────────── | COMMENT |
| MEDIUM | spaces/src/gantt.ts | 58 | // ── DATES ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 360 | // ── 1 · TITLE ────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 452 | // ── 2 · ONE FILE ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 497 | // ── 3 · NO MOVING PARTS (staggers + count-ups, arriving by morph) ───── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 540 | // ── 4 · MORPH MANIFESTO ──────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 774 | // ── 6 · CHARTS ALIVE (+ hidden pie state) ────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 885 | // ── 7 · TABLES (real HTML table) ─────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 971 | // ── 8 · MOMENTUM (line-chart hero) ───────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 998 | // ── 9 · MOTION & LINES ───────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 1043 | // ── 10 · CHOREOGRAPHY (base + two hidden states) ────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 1133 | // ── 11 · HOVER FOCUS ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 1183 | // ── 12 · MARKDOWN ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | slides/src/starterdeck.ts | 1216 | // ── 13 · CLOSE ───────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | type/src/inline.ts | 307 | // ─────────────────────────────────────────────────────────── model → HTML | COMMENT |
| MEDIUM | type/src/inline.ts | 484 | // ─────────────────────────────────────────────────────────── HTML → model | COMMENT |
| MEDIUM | type/src/comments.ts | 12 | // ─────────────────────────────────────────────────────────── the four choices | COMMENT |
| MEDIUM | type/src/comments.ts | 40 | // ─────────────────────────────────────────────── how comments stay out of print | COMMENT |
| MEDIUM | type/src/comments.ts | 54 | // ───────────────────────────────────────────────────────── NEEDS FROM THE CORE | COMMENT |
| MEDIUM | type/src/comments.ts | 145 | // ───────────────────────────────────────────────────────────────── the shape | COMMENT |
| MEDIUM | type/src/comments.ts | 222 | // ─────────────────────────────────────────────────── reading and writing them | COMMENT |
| MEDIUM | type/src/comments.ts | 297 | // ───────────────────────────────────────────────────────── anchor maintenance | COMMENT |
| MEDIUM | type/src/comments.ts | 436 | // ───────────────────────────────────────────────────────────── thread logic | COMMENT |
| MEDIUM | type/src/comments.ts | 526 | // ────────────────────────────────────── the hooks the core calls (see (B)) | COMMENT |
| MEDIUM | type/src/comments.ts | 561 | // ═══════════════════════════════════════════════════════════════════════ UI | COMMENT |
| MEDIUM | type/src/comments.ts | 931 | // ─────────────────────────────────────────────────────────────── the feature | COMMENT |
| MEDIUM | type/src/comments.ts | 1072 | // ─────────────────────────────────────────────────────── showing and hiding | COMMENT |
| MEDIUM | type/src/docstyles.ts | 52 | // ═══════════════════════════════════════════════════════════ resolution (pure) | COMMENT |
| MEDIUM | type/src/docstyles.ts | 298 | // ═══════════════════════════════════════════════════════════════════ the UI | COMMENT |
| MEDIUM | type/src/main.ts | 46 | // ───────────────────────────────────────────────────────────── the document | COMMENT |
| MEDIUM | type/src/main.ts | 96 | // ───────────────────────────────────────────────────────────────── chrome | COMMENT |
| MEDIUM | type/src/main.ts | 181 | // ───────────────────────────────────────────────────── chrome: labels & icons | COMMENT |
| MEDIUM | type/src/main.ts | 295 | // ─────────────────────────────────────────────── the feature registry, rendered | COMMENT |
| MEDIUM | type/src/main.ts | 695 | // ────────────────────────────────────────────────────────── sidebar | COMMENT |
| MEDIUM | type/src/main.ts | 778 | // ───────────────────────────────────────────── revisions and redlining | COMMENT |
| MEDIUM | type/src/main.ts | 789 | // ─────────────────────────────────────────────────────────── signatures | COMMENT |
| MEDIUM | type/src/main.ts | 888 | // ─────────────────────────────────────────────────────────── a small toast | COMMENT |
| MEDIUM | type/src/main.ts | 904 | // ─────────────────────────────────────────────────────────────── saving | COMMENT |
| MEDIUM | type/src/main.ts | 956 | // ─────────────────────────────────────────────────────────────── printing | COMMENT |
| MEDIUM | type/src/main.ts | 1038 | // ─────────────────────────────────────────────────────────────────── ready | COMMENT |
| MEDIUM | type/src/editor.ts | 60 | // ───────────────────────────────────────────────────────── caret ↔ model | COMMENT |
| MEDIUM | type/src/editor.ts | 145 | // ─────────────────────────────────────────────────────────── input | COMMENT |
| MEDIUM | type/src/editor.ts | 288 | // ────────────────────────────────────────────────────────── formatting | COMMENT |
| MEDIUM | type/src/editor.ts | 495 | // ───────────────────────────────────────────────────────────── keys | COMMENT |
| MEDIUM | type/src/props.ts | 38 | // ─────────────────────────────────────────────────────────────── small parts | COMMENT |
| MEDIUM | type/src/props.ts | 109 | // ────────────────────────────────────────────────────────────────── sections | COMMENT |
| MEDIUM | type/src/props.ts | 477 | // ──────────────────────────────────────────────────────────────── the panel | COMMENT |
| MEDIUM⚡ | type/src/math.ts | 4 | // ───────────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | type/src/math.ts | 6 | // ───────────────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM⚡ | type/src/math.ts | 15 | // ═══ WHAT IS SUPPORTED ══════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | type/src/math.ts | 42 | // ═══ WHAT IS NOT ════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | type/src/math.ts | 65 | // ═══ HOW MATH FITS THE MODEL, AND WHY ═══════════════════════════════════════ | COMMENT |
| MEDIUM | type/src/math.ts | 126 | // ═══ NEEDS FROM THE CORE ════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | type/src/math.ts | 239 | // ───────────────────────────────────────────────────────────── the renderers | COMMENT |
| MEDIUM | type/src/math.ts | 277 | // ───────────────────────────────────────────────────────────── editing | COMMENT |
| MEDIUM | type/src/math.ts | 365 | // ───────────────────────────────────────────────────────────── the chrome | COMMENT |
| MEDIUM | type/src/find.ts | 53 | // ───────────────────────────────────────────────────────────── the search | COMMENT |
| MEDIUM | type/src/find.ts | 178 | // ─────────────────────────────────────────────────────────── decoration | COMMENT |
| MEDIUM | type/src/find.ts | 224 | // ────────────────────────────────────────────────────────────── the panel | COMMENT |
| 203 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | spaces/src/editor.ts | 6254 | print() | CODE |
| HIGH | scripts/test-spaces-model.ts | 1561 | ['py', 'def f():\n return None', 'def', 'k'], | CODE |
| HIGH⚡ | scripts/test-home-bridge.ts | 206 | print("ok " + b64(out) + " " + (inside ? "1" : "0")) | CODE |
| HIGH⚡ | scripts/test-home-bridge.ts | 209 | print("ok " + b64(exportName(arg(2)))) | CODE |
| HIGH⚡ | scripts/test-home-bridge.ts | 213 | print("ok " + b64(targetsOpenDocument(arg(2)) ? "1" : "0")) | CODE |
| HIGH⚡ | scripts/test-home-bridge.ts | 215 | print("ok " + b64(jsString(arg(1)))) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | home/android/app/src/main/res/values-night/colors.xml | 19 | <color name="placeholder">#24313F</color> | CODE |
| LOW | home/android/app/src/main/res/values/colors.xml | 19 | <color name="placeholder">#E4E9EF</color> | CODE |
| LOW | slides/src/modelkeys.generated.ts | 17 | "text": ["align", "backdropFilter", "blend", "blur", "color", "colorGradient", "fontFamily", "fontSize", "fontWeig | CODE |
| LOW | schema/slides-1.2.1.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.0.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.7.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.6.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.1.0.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.5.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.4.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.3.json | 367 | "placeholder": { | CODE |
| LOW | schema/slides-1.2.2.json | 367 | "placeholder": { | CODE |
| LOW | scripts/test-spaces-undo.ts | 64 | html: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod ' | CODE |
| LOW | scripts/test-spaces-undo.ts | 64 | html: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod ' | CODE |
| LOW | scripts/test-type-link.ts | 253 | const long = 'lorem ipsum dolor sit amet '.repeat(4000) + 'x'; | CODE |
| LOW | scripts/test-type-link.ts | 253 | const long = 'lorem ipsum dolor sit amet '.repeat(4000) + 'x'; | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 82 | ok(matchText('Acme Ltd', q({})), 'a lowercase query matches mixed-case text by default') | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 83 | ok(!matchText('Acme Ltd', q({ caseSensitive: true })), '…and does not when case matters') | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 84 | ok(matchText('Acme Ltd', q({ query: 'Ac', caseSensitive: true })), 'case-sensitive matches the right case') | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 85 | ok(!matchText('Acme Ltd', q({ query: 'acme', wholeCell: true })), 'whole-cell refuses a prefix') | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 86 | ok(matchText('Acme Ltd', q({ query: 'acme ltd', wholeCell: true })), 'whole-cell matches the whole cell, case-folded') | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 87 | ok(matchText(' Acme Ltd ', q({ query: 'Acme Ltd', wholeCell: true })), | CODE |
| LOW⚡ | scripts/test-dash-find.ts | 89 | ok(!matchText('Acme Ltd', q({ query: 'me L' })) === false, | CODE |
| LOW | scripts/test-dash-find.ts | 143 | const NAMES = ['Acme Ltd', 'Beta Co', 'acme holdings', 'Delta'] | CODE |
| LOW | scripts/test-convert/pptx.ts | 144 | reportIs(r, ['text-needs-refit', 'font-substituted'], 'placeholder') | CODE |
| LOW | scripts/test-convert/_fixtures.ts | 398 | { name: 'placeholder', build: fxPlaceholder }, | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | scripts/test-webext-assistant.ts | 822 | ok(picked.ok === true && picked.store.providers.gemini.pinned.join() === 'gemini-1.5-pro,gemini-2.5-pro,gemini-3-pro', | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | docs/agents.md | 397 | ## Layout guardrails | COMMENT |
| MEDIUM | scripts/test-autosave.ts | 18 | // writing into bento/slides' database since its scaffold landed. | COMMENT |
| MEDIUM | scripts/test-spaces-model.ts | 12 | // space" for a document it could not read. The scaffold did — it returned | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dash/src/panels.ts | 594 | function buildCondFmtColumnSection(sheet: TableSheet): void { | CODE |
| LOW | dash/src/panels.ts | 622 | function buildDatasetValidationSection(sheet: TableSheet): void { | CODE |
| LOW | dash/src/panels.ts | 655 | function buildCanvasValidationSection(sheet: CanvasSheet): void { | CODE |
| LOW | dash/src/cellfmt.ts | 295 | export function describeOverrideSelection(keys: readonly string[], cursor: string): string { | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | scripts/loader-csp-server.py | 21 | # header, including harness.html, so the sandboxed iframes it hosts inherit | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | .github/workflows/ci.yml | 783 | # Creating a document downloads an app shell and writes it to the user's | COMMENT |