<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RawCull Architecture on RawCull</title><link>https://techrawcull.netlify.app/docs/copilot/</link><description>Recent content in RawCull Architecture on RawCull</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 04 Sep 2026 20:55:01 +0200</lastBuildDate><atom:link href="https://techrawcull.netlify.app/docs/copilot/index.xml" rel="self" type="application/rss+xml"/><item><title>Concurrency Architecture</title><link>https://techrawcull.netlify.app/docs/copilot/01-concurrency-architecture/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/01-concurrency-architecture/</guid><description>&lt;h1 id="concurrency-architecture"&gt;Concurrency Architecture&lt;a class="td-heading-self-link" href="#concurrency-architecture" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull is a Swift 6 / strict-concurrency codebase. Almost every piece of
mutable shared state — caches, in-flight request tables, coordinators, view
models — is isolated to either an &lt;code&gt;actor&lt;/code&gt; or &lt;code&gt;@MainActor&lt;/code&gt;, and cross-boundary
data is required to be &lt;code&gt;Sendable&lt;/code&gt;. This document is an inventory of that
system and the patterns it relies on, so you can extend it safely.&lt;/p&gt;
&lt;h2 id="the-two-isolation-domains"&gt;The two isolation domains&lt;a class="td-heading-self-link" href="#the-two-isolation-domains" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RawCull&amp;rsquo;s concurrency model boils down to two kinds of isolated types:&lt;/p&gt;</description></item><item><title>Image Pipeline and Caching</title><link>https://techrawcull.netlify.app/docs/copilot/02-image-pipeline-and-caching/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/02-image-pipeline-and-caching/</guid><description>&lt;h1 id="image-pipeline-and-caching"&gt;Image Pipeline and Caching&lt;a class="td-heading-self-link" href="#image-pipeline-and-caching" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This is the path a RAW file takes from &amp;ldquo;user picked a folder&amp;rdquo; to &amp;ldquo;sharp
preview pixels on screen&amp;rdquo;, and the multi-layer cache system that keeps that
fast on repeat visits. Read
&lt;a href="../01-concurrency-architecture/"&gt;Concurrency Architecture&lt;/a&gt; first — this
document assumes you know the actor/TaskGroup/Sendable vocabulary from there.&lt;/p&gt;
&lt;h2 id="end-to-end-flow"&gt;End-to-end flow&lt;a class="td-heading-self-link" href="#end-to-end-flow" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;User selects folder
 │
 ▼
DiscoverFiles (actor) ──► finds candidate RAW file URLs
 │
 ▼
ScanFiles (actor) ──► reads EXIF/metadata per file → [FileItem]
 │ (feeds RawCullViewModel.files)
 ▼
ScanAndCreateThumbnails (actor, background, low priority)
 │ proactively decodes + caches grid thumbnails
 ▼
 ┌─────────────────────────────┐
 │ SharedMemoryCache (RAM) │ ← only RequestThumbnail admits here
 │ DiskCacheManager (disk) │ ← both scan-preload and requests write here
 └─────────────────────────────┘
 ▲
 │ (user scrolls the grid / opens loupe)
 │
RequestThumbnail (actor) ──► coalesces duplicate concurrent requests,
 │ checks memory cache → disk cache → decode
 ▼
 CGImage/NSImage rendered by a SwiftUI Image/ThumbnailComponents view
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For full-size zoom/export, a parallel path exists through
&lt;code&gt;ScanAndExtractJPGs&lt;/code&gt; / &lt;code&gt;ExtractAndSaveJPGs&lt;/code&gt; / &lt;code&gt;SaveJPGImage&lt;/code&gt;, backed by
&lt;code&gt;FullSizeJPGDiskCache&lt;/code&gt; instead of &lt;code&gt;DiskCacheManager&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Culling and Persistence</title><link>https://techrawcull.netlify.app/docs/copilot/03-culling-and-persistence/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/03-culling-and-persistence/</guid><description>&lt;h1 id="culling-and-persistence"&gt;Culling and Persistence&lt;a class="td-heading-self-link" href="#culling-and-persistence" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&amp;ldquo;Culling&amp;rdquo; is RawCull&amp;rsquo;s core job: attach a rating/status to each RAW file so
the photographer can filter down to keepers and export just those. This
document covers the model that owns that decision, how it&amp;rsquo;s filtered for
display, and how it&amp;rsquo;s saved to disk.&lt;/p&gt;
&lt;h2 id="the-model-cullingmodel"&gt;The model: &lt;code&gt;CullingModel&lt;/code&gt;&lt;a class="td-heading-self-link" href="#the-model-cullingmodel" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CullingModel&lt;/code&gt; (&lt;code&gt;Model/ViewModels/CullingModel.swift&lt;/code&gt;) is a
&lt;code&gt;@MainActor @Observable&lt;/code&gt; class, constructed exactly once by
&lt;code&gt;RawCullViewModel&lt;/code&gt; (&lt;code&gt;var cullingModel = CullingModel()&lt;/code&gt; — the comment above
it says explicitly &amp;ldquo;This is the only place CullingModel is initialised&amp;rdquo;).
It holds:&lt;/p&gt;</description></item><item><title>Export and Copy Pipeline</title><link>https://techrawcull.netlify.app/docs/copilot/04-export-copy-pipeline/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/04-export-copy-pipeline/</guid><description>&lt;h1 id="export--copy-pipeline-rsync"&gt;Export / Copy Pipeline (rsync)&lt;a class="td-heading-self-link" href="#export--copy-pipeline-rsync" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Once files are rated, RawCull needs to get the keepers out to another
folder (a backup drive, a delivery folder, etc.). It does this by shelling
out to the system&amp;rsquo;s &lt;code&gt;/usr/bin/rsync&lt;/code&gt;, rather than using &lt;code&gt;FileManager&lt;/code&gt; copy
APIs directly. This document explains that pipeline and, importantly, why
the code around it uses vocabulary (&amp;ldquo;synchronize&amp;rdquo;, &amp;ldquo;offsite server&amp;rdquo;, &amp;ldquo;SSH
parameters&amp;rdquo;) that has nothing to do with what RawCull actually does.&lt;/p&gt;</description></item><item><title>Intelligence and AI Subsystem</title><link>https://techrawcull.netlify.app/docs/copilot/05-intelligence-ai-subsystem/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/05-intelligence-ai-subsystem/</guid><description>&lt;h1 id="intelligence--ai-subsystem"&gt;Intelligence / AI Subsystem&lt;a class="td-heading-self-link" href="#intelligence--ai-subsystem" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;RawCull/Intelligence/&lt;/code&gt; is where RawCull&amp;rsquo;s on-device machine-learning
features live: grouping near-duplicate &amp;ldquo;burst&amp;rdquo; shots, ranking them by
sharpness/subject focus, letting the user search photos by natural-language
description, and (optionally) running a heavier &amp;ldquo;Deep Review&amp;rdquo; AI pass that
recommends a winner within a burst. All of it runs &lt;strong&gt;locally&lt;/strong&gt; — no photo
data or embeddings leave the machine, and every optional model requires an
explicit license acceptance and download before RawCull will use it.&lt;/p&gt;</description></item><item><title>The Intelligence Runtime</title><link>https://techrawcull.netlify.app/docs/copilot/runtime/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/runtime/</guid><description>&lt;h1 id="the-intelligence-runtime"&gt;The Intelligence Runtime&lt;a class="td-heading-self-link" href="#the-intelligence-runtime" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document is a deep dive into &lt;code&gt;RawCullIntelligenceRuntime&lt;/code&gt; and its
composition partner &lt;code&gt;RawCullAIIntegration&lt;/code&gt; — together &amp;ldquo;the runtime&amp;rdquo; — which
is only summarized in
&lt;a href="../05-intelligence-ai-subsystem/#composition-root-rawcullintelligenceruntime--rawcullaiintegration"&gt;Intelligence and AI Subsystem&lt;/a&gt;.
Read that doc first for the subsystem&amp;rsquo;s overall shape (folders, backends,
CoreAI attribution); this document explains &lt;strong&gt;how the runtime object itself
is built, how it reacts when a part of its configuration changes, how to
extend it safely, and why it&amp;rsquo;s built this way&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>SwiftUI View Layer</title><link>https://techrawcull.netlify.app/docs/copilot/06-swiftui-view-layer/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/06-swiftui-view-layer/</guid><description>&lt;h1 id="swiftui-view-layer"&gt;SwiftUI View Layer&lt;a class="td-heading-self-link" href="#swiftui-view-layer" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;RawCull/Views/&lt;/code&gt; (87 files across 16 subfolders, ~15k lines) is the
presentation layer. This doc covers the navigation architecture, the
grid/inspection tools that make up most of the screen time, and the SwiftUI
state-management idioms used consistently across the whole layer, so you can
match the existing style when adding a view.&lt;/p&gt;
&lt;h2 id="navigation-architecture"&gt;Navigation architecture&lt;a class="td-heading-self-link" href="#navigation-architecture" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RawCullApp&lt;/code&gt; (see &lt;a href="https://techrawcull.netlify.app/docs/"&gt;Architecture Overview&lt;/a&gt;) hosts three &lt;code&gt;Window&lt;/code&gt;
scenes; the main one wraps &lt;code&gt;RawCullMainView&lt;/code&gt;
(&lt;code&gt;Main/RawCullMainView.swift&lt;/code&gt;), which is the root of everything a user
actually browses photos through.&lt;/p&gt;</description></item><item><title>Settings and Configuration</title><link>https://techrawcull.netlify.app/docs/copilot/07-settings-and-configuration/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/07-settings-and-configuration/</guid><description>&lt;h1 id="settings-and-configuration"&gt;Settings and Configuration&lt;a class="td-heading-self-link" href="#settings-and-configuration" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull&amp;rsquo;s user-configurable state splits into two independently-persisted
models: general app preferences (&lt;code&gt;SettingsViewModel&lt;/code&gt;) and AI-feature
preferences (&lt;code&gt;RawCullAISettingsModel&lt;/code&gt;, covered in depth in
&lt;a href="../05-intelligence-ai-subsystem/"&gt;Intelligence and AI Subsystem&lt;/a&gt;). This
doc covers the general settings model, its persistence, and the memory
monitor shown alongside it.&lt;/p&gt;
&lt;h2 id="settingsviewmodel"&gt;&lt;code&gt;SettingsViewModel&lt;/code&gt;&lt;a class="td-heading-self-link" href="#settingsviewmodel" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Model/ViewModels/SettingsViewModel.swift&lt;/code&gt; is an &lt;code&gt;@MainActor @Observable&lt;/code&gt;
class exposing a flat list of plain stored properties for every tunable in
the app — no nested config struct, so SwiftUI settings controls can bind to
each property directly (&lt;code&gt;Bindable(settingsViewModel).thumbnailSizeGrid&lt;/code&gt;,
etc.). It groups into four areas via &lt;code&gt;// MARK:&lt;/code&gt; comments:&lt;/p&gt;</description></item><item><title>RawCull Features and Roadmap</title><link>https://techrawcull.netlify.app/docs/copilot/features/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/features/</guid><description>&lt;h1 id="rawcull-vs-other-culling-apps-and-where-rawcull-goes-after-320"&gt;RawCull vs. Other Culling Apps, and Where RawCull Goes After 3.2.0&lt;a class="td-heading-self-link" href="#rawcull-vs-other-culling-apps-and-where-rawcull-goes-after-320" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document is deliberately different from the rest of the &lt;code&gt;Docs/&lt;/code&gt; catalog:
docs 00–07 and &lt;code&gt;runtime.md&lt;/code&gt; explain &lt;em&gt;how the code works&lt;/em&gt;. This one is a
&lt;strong&gt;product-positioning and roadmap&lt;/strong&gt; document — it compares RawCull (current
version &lt;strong&gt;3.2.0&lt;/strong&gt;) against other well-known photo-culling tools on macOS, and
lays out a set of principles and concrete ideas for how the app should evolve
afterward. It&amp;rsquo;s written for the same reader as the rest of the catalog
(Swift/SwiftUI-literate, new to this codebase), but the goal here is product
understanding, not implementation detail.&lt;/p&gt;</description></item><item><title>Known Issues and Findings</title><link>https://techrawcull.netlify.app/docs/copilot/issues/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://techrawcull.netlify.app/docs/copilot/issues/</guid><description>&lt;h1 id="known-issues-and-findings"&gt;Known Issues and Findings&lt;a class="td-heading-self-link" href="#known-issues-and-findings" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This is a code-review pass over the main &lt;code&gt;RawCull&lt;/code&gt; app target (excluding
&lt;code&gt;RawCullTests&lt;/code&gt; and the external SPM packages). Overall the codebase is
unusually clean for its size: &lt;strong&gt;no force-unwraps (&lt;code&gt;!&lt;/code&gt;), no &lt;code&gt;try!&lt;/code&gt;, no
force-casts (&lt;code&gt;as!&lt;/code&gt;), no unguarded array &lt;code&gt;[0]&lt;/code&gt; accesses, no &lt;code&gt;DispatchSemaphore&lt;/code&gt;
or &lt;code&gt;Timer&lt;/code&gt;/&lt;code&gt;NotificationCenter&lt;/code&gt; leaks&lt;/strong&gt;, and every
&lt;code&gt;startAccessingSecurityScopedResource()&lt;/code&gt; call has a matching
&lt;code&gt;stopAccessingSecurityScopedResource()&lt;/code&gt; on all paths, including cancellation
and &lt;code&gt;deinit&lt;/code&gt;. The findings below are the exceptions to that otherwise solid
baseline. Severities: &lt;strong&gt;High&lt;/strong&gt; (real user-facing breakage or data-loss risk),
&lt;strong&gt;Medium&lt;/strong&gt; (real bug/gap, narrow trigger conditions or degraded UX), &lt;strong&gt;Low&lt;/strong&gt;
(code-quality / robustness / maintainability).&lt;/p&gt;</description></item></channel></rss>