Kitchen Report
Not reviewed by AI (1)
These files are too big or are one very long line, so the AI review skipped them. The rest of the report is complete. If a file matters to you, look at it yourself.
- 1 file is one very long line (often minified or generated)
Scores
Score details and supporting evidence for each check.
All promised features found
Features found in source
31.5 of 32 checked features (98%); 0 couldn't be verified; partial features count half.
The AI read 36 of 54 source files.
Features found in the code
Found: 31; Partly: 1; Missing: 0; Couldn't verify: 0
App specification source review: 31.5 of 32 features found in source (98.44%)
Show the 32 features
- ◐ Download and prepare the optional Kokoro voice package with visible setup progress. — partly
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
performInstall downloads runtime/model with stage text shown in settings; progress is text-only, no numeric progress.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#performInstall
- ✓ Open an unencrypted EPUB and display its readable chapter text, title, and author. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/EPUBImporter.swift, Sources/Leaf/DemoBook.swift
EPUBImporter.load parses title/author/chapters and rejects encrypted EPUBs; ReaderModel.load displays imported book.
Evidence: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/EPUBImporter.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#openBook, Sources/ReaderCore/EPUBImporter.swift#load
- ✓ Navigate between pages using the reader’s page controls. — found
Files checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/PageText.swift
Footer prev/next buttons call turnPage; turnPage computes destination page and repaginates.
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#turnPage
- ✓ Jump to a chapter from the contents sidebar. — found
Files checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ListeningExperience.swift
Contents sidebar buttons call selectChapter, which sets chapterIndex and repaginates.
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#selectChapter
- ✓ Adjust reading text size while preserving the current passage during page reflow. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/PageText.swift
Font size slider triggers repaginate; repaginate anchors on readingOffset/session offset.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#repaginate
- ✓ Reopen a previously opened book from the library. — found
Files checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/BookLibrary.swift
Library list buttons call openLibraryBook, which resolves URL and loads with saved position.
Evidence: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift#openLibraryBook
- ✓ Filter the library by book title or author. — found
Files checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/BookLibrary.swift, Sources/Leaf/DemoBook.swift
Search field filters library by title or author case-insensitively.
Evidence: Sources/Leaf/BookLibraryView.swift
Files from your spec: Sources/Leaf/BookLibraryView.swift
- ✓ Remove a library entry while keeping the original EPUB and saved bookmarks. — found
Files checked: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/BookLibrary.swift
Remove button calls removeLibraryBook, which only drops the library entry; EPUB and bookmark stores untouched
Evidence: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/BookLibraryView.swift, Sources/Leaf/ReaderModel.swift#removeLibraryBook
- ✓ Restore saved reading and narration positions when returning to a book. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swift
applyRestoredPosition restores chapter, text and speech offsets; ReadingPosition and LibraryBook persist them
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#applyRestoredPosition, Sources/Leaf/ReadingPosition.swift, Sources/Leaf/BookLibrary.swift
- ✓ Save selected text as a bookmark for the current book. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderAnnotations.swift, Sources/Leaf/BookLibrary.swift
bookmarkSelectedText appends a TextBookmark from the selection and saves it via TextBookmarkStore
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderAnnotations.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#bookmarkSelectedText, Sources/Leaf/ReaderAnnotations.swift
- ✓ Jump to a saved bookmark’s passage. — found
Files checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift
Sidebar bookmark button calls openBookmark, which jumps to the chapter and page containing the passage
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#openBookmark
- ✓ Delete a saved bookmark. — found
Files checked: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingPosition.swift
Sidebar xmark and in-page remove button both call removeBookmark, which deletes and saves
Evidence: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/ReaderView.swift, Sources/Leaf/ReaderModel.swift#removeBookmark
- ✓ Read book text aloud using an installed Mac voice. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/DemoBook.swift
speak uses AVSpeechSynthesizer with a chosen installed voice; settings list Mac voices
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#speak, Sources/Leaf/ReadingSettingsView.swift
- ✓ Read book text aloud offline using an installed Kokoro voice. — found
Files checked: Sources/Leaf/SpeechSettings.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/LocalKokoroWorker.swift
Local provider config, CloudNarrator local branch, and Kokoro worker generate found in source
Evidence: Sources/Leaf/SpeechSettings.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/LocalKokoroWorker.swift
Files from your spec: Sources/Leaf/SpeechSettings.swift#configuration, Sources/Leaf/CloudNarrator.swift#prepare, Sources/Leaf/LocalKokoroWorker.swift#generate
- ✓ Read book text aloud using an OpenRouter speech model. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift
speak uses cloud config; prepare calls client.speech; OpenRouter speech POST found in source
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#speak, Sources/Leaf/CloudNarrator.swift#prepare, Sources/ReaderCore/CloudSpeech.swift#speech
- ✓ Pause and resume narration. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/ReaderView.swift
toggleReading pause/resume and CloudNarrator pause/resume found in source
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#toggleReading, Sources/Leaf/CloudNarrator.swift
- ✓ Change narration pace. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift
Pace picker, updateRate, and cloud setRate found in source
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#updateRate
- ✓ Move backward or forward by sentence during an active or paused listening session. — found
Files checked: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/SentenceNavigation.swift
Arrow commands, navigateReading, and SentenceNavigation.destination found in source
Evidence: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/SentenceNavigation.swift
Files from your spec: Sources/Leaf/LeafApp.swift, Sources/Leaf/ReaderModel.swift#navigateReading, Sources/ReaderCore/SentenceNavigation.swift#destination
- ✓ Start narration from a clicked word or sentence. — found
Files checked: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swift
PageText mouseUp onSeek and ReaderModel.seek found in source
Evidence: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swift
Files from your spec: Sources/Leaf/PageText.swift, Sources/Leaf/ReaderModel.swift#seek
- ✓ Follow narration with word or sentence highlighting. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/PageText.swift
Word/sentence highlight found in source: CloudNarrator tick emits ranges, ReaderModel sets highlight, PageText paints it.
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift, Sources/Leaf/PageText.swift
Files from your spec: Sources/Leaf/ReaderModel.swift, Sources/Leaf/CloudNarrator.swift#tick, Sources/Leaf/PageText.swift
- ✓ Open a fullscreen listening view during a listening session. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderView.swift, Sources/Leaf/ListeningExperience.swift
toggleListeningView, fullscreen sync, and ListeningExperience overlay found in source.
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReaderView.swift, Sources/Leaf/ListeningExperience.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#toggleListeningView, Sources/Leaf/ReaderView.swift#syncListeningFullscreen, Sources/Leaf/ListeningExperience.swift
- ✓ Preview a local or cloud voice using fixed sample text. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift, Sources/ReaderCore/CloudSpeech.swift
previewCloudVoice uses fixed sample text; settings buttons for local and cloud preview found in source.
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/ReadingSettingsView.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/ReaderModel.swift#previewCloudVoice
- ✓ Cancel an ongoing Kokoro setup. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalCommand.swift
cancelInstall cancels task; LocalCommand terminates subprocess and throws CancellationError, shown in source.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalCommand.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#cancelInstall
- ✓ Retry or repair Kokoro setup. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
install() and Repair/check setup button re-run performInstall, found in source.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#install
- ✓ Remove downloaded Kokoro files. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift, Sources/Leaf/LocalKokoroWorker.swift
Remove download button calls local.remove(), which deletes the Kokoro root directory and clears state.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/LocalKokoro.swift#remove
- ✓ Save an OpenRouter API key securely in macOS Keychain. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/Leaf/ReaderModel.swift
Save key button calls saveKey(), which stores the key via Keychain SecItemAdd/Update.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#saveKey
- ✓ Remove the saved OpenRouter API key. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/Leaf/ReaderModel.swift
Remove key button calls removeKey(), which deletes the Keychain item via SecItemDelete.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#removeKey
- ✓ Refresh the available OpenRouter speech-model list. — found
Files checked: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/ReaderCore/CloudSpeech.swift
Refresh models button awaits refreshModels(), which fetches the speech model catalog from OpenRouter.
Evidence: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift, Sources/ReaderCore/CloudSpeech.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/SpeechSettings.swift#refreshModels
- ✓ Request audio transcription timestamps for cloud highlighting, with a visible fallback to estimated timing when alignment fails. — found
Files checked: Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift, Sources/Leaf/ReadingSettingsView.swift
prepare() requests timings when transcribed mode; on failure falls back to estimated timings with a warning.
Evidence: Sources/Leaf/CloudNarrator.swift, Sources/ReaderCore/CloudSpeech.swift
Files from your spec: Sources/Leaf/ReadingSettingsView.swift, Sources/Leaf/CloudNarrator.swift#prepare, Sources/ReaderCore/CloudSpeech.swift#timings
- ✓ Look up selected text in macOS Dictionary. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/DictionaryLookup.swift, Sources/Leaf/DictionaryLookupView.swift
lookUpSelectedText uses LocalDictionary DCSCopyTextDefinition and shows result in DictionaryLookupView.
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/DictionaryLookup.swift, Sources/Leaf/DictionaryLookupView.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#lookUpSelectedText, Sources/Leaf/DictionaryLookup.swift#definition, Sources/Leaf/DictionaryLookupView.swift
- ✓ Request an online definition through OpenRouter when the local dictionary has no result and a key is saved. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swift, Sources/Leaf/ReadingSettingsView.swift
Local dictionary first, then OpenRouter dictionaryDefinition with saved key; found in source
Evidence: Sources/Leaf/ReaderModel.swift, Sources/ReaderCore/CloudSpeech.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#lookUpSelectedText, Sources/ReaderCore/CloudSpeech.swift#dictionaryDefinition
- ✓ Prevent automatic display and system idle sleep while narration or a voice preview is active. — found
Files checked: Sources/Leaf/ReaderModel.swift, Sources/Leaf/PlaybackActivity.swift, Sources/Leaf/ReaderView.swift
setActive disables idle system and display sleep; called for speaking or preview; found in source
Evidence: Sources/Leaf/ReaderModel.swift, Sources/Leaf/PlaybackActivity.swift
Files from your spec: Sources/Leaf/ReaderModel.swift#updatePlaybackActivity, Sources/Leaf/PlaybackActivity.swift#setActive
Static AI review of the source. Partial features count half. Actual run verification: Not tested; source presence and a responding demo do not prove the features work.
Good, small improvements suggested
Security screening
3 security issue(s) found; serious issues reduce this score most. No scan guarantees safety.
- medium · Archive entry paths passed to unzip without full sanitization · Sources/ReaderCore/EPUBImporter.swift
What to do: Validate the resolved path stays within the archive root and reject absolute or traversal paths before invoking unzip; consider a native ZIP reader instead of shelling out.
- medium · Unpinned third-party Python dependencies installed during local voice setup · Sources/Leaf/Resources/LocalKokoro.requirements.txt
What to do: Pin and verify the requirements file hash or use a lockfile with hashes; document the trust boundary and consider sandboxing the worker process.
- low · Model refresh network call lacks explicit timeout handling · Sources/Leaf/SpeechSettings.swift
What to do: Add a timeout and cancellation to the model refresh request and surface failures without blocking the settings UI.
Some setup steps missing
Setup files
Present: README, run entry point, App specification or AGENTS. Missing: environment example, known install command. Static check in .; code was not executed. Checks README, environment example, install and run scripts, app specification or AGENTS, confirmed How to run it (confirmed) and run manifest (absent). The app was not executed.
- ✓ README — README.md
- ✕ Environment example
- ✕ Install command
- ✓ Run entry point
- ✓ App specification or AGENTS — Saved App specification
- ✓ Confirmed How to run it
- ✕ Run manifest
Not checked
Not checked
Dependency & license health
Dependency and license evidence has not been checked.
Source coverage: Sampled — some eligible source files were omitted
Dependency vulnerability check: Inconclusive — dependency coverage is incomplete
Build not run by CookThisApp.
Suggestions (4)
Most serious first · Page 1
Archive entry paths passed to unzip without full sanitization
Medium — could affect users in some situations; plan a fix.
A crafted EPUB could cause unzip to read entries outside the intended archive path or trigger unexpected unzip behavior, potentially exposing local files or crashing the importer.
Review finding — does not by itself block publishing.
Fix hint: Validate the resolved path stays within the archive root and reject absolute or traversal paths before invoking unzip; consider a native ZIP reader instead of shelling out.
Open
ai-security · Sources/ReaderCore/EPUBImporter.swiftUnpinned third-party Python dependencies installed during local voice setup
Medium — could affect users in some situations; plan a fix.
Users who run Kokoro setup download and execute a large Python dependency tree from the network; a compromised package or index could run arbitrary code on the user's Mac.
Review finding — does not by itself block publishing.
Fix hint: Pin and verify the requirements file hash or use a lockfile with hashes; document the trust boundary and consider sandboxing the worker process.
Open
ai-security · Sources/Leaf/Resources/LocalKokoro.requirements.txtModel refresh network call lacks explicit timeout handling
Low — limited impact; fix during routine maintenance.
If OpenRouter is slow or unreachable, the settings UI can remain in a loading state and the user cannot easily recover.
Review finding — does not by itself block publishing.
Fix hint: Add a timeout and cancellation to the model refresh request and surface failures without blocking the settings UI.
Open
ai-security · Sources/Leaf/SpeechSettings.swiftPlatform check: macOS selected but no macOS project found
Low — an improvement hint for the developer, with no confirmed user harm.
Review finding — does not by itself block publishing.
Fix hint: Review the platforms in your listing Basics. This information does not affect the score or publication.
Open
platform-check ·
Kitchen Report
Current run: generation 2
Leaf · Version 01a1c524-8990-7e18-ac12-42284f15d3df
Report complete.
Live provider analysis
Ordinary dependency advisories are disclosed warnings and allow publication. Harmful source, malicious packages and unresolved rights or license blockers prevent publication.
Features found in source are not proof the app works. Actual run verification: Not tested. License and rights eligibility requires the seller’s declarations; dependency license compatibility: Not checked. Checks do not guarantee safety or production readiness.
Download reportView findings and fixesOur estimate: $7–12 in AI usage, plus your own time.
How we estimate
We estimate AI usage for repeated edits and context reads, priced at Claude Sonnet 5.5 rates. Repeated reads use the provider's cached-input price.
The developer sets the price; this estimate is guidance only.
Formula v4 · AI usage estimate · 5 rework iterations · 16× context · claude-sonnet-5-5 · Price table 2
Source checks
Source check ✓ 10/11/2026
- 1. Unpacking · ✓ Done
- 2. Secret scan · ✓ Done
- 3. Safety scan (S1) · ✓ Done
- 4. Dependency scan (S2) · ✓ Done
For developers: code quality
Improvement hints only; these do not contribute to buyer headline scores.
Architecture / structure: 79 / 100 (AI structure review)
Code cleanliness: 77 / 100 (AI maintainability review)
Test presence: 0 test files found (presence signal only). Coverage: Not checked.
Source size: 6734 LOC (metric, not a scored check).
- ReaderModel is a large god-object mixing import, speech, bookmarks, lookup and persistence; splitting would aid review. Sources/Leaf/ReaderModel.swift
- CloudNarrator cache and chunk queueing are subtle; a brief comment on eviction and lookahead policy would help. Sources/Leaf/CloudNarrator.swift
- EPUBImporter shells out to /usr/bin/unzip; document the sandbox/availability assumption for packaged builds. Sources/ReaderCore/EPUBImporter.swift