Skip to content

Rename to Findle, course outlines, faster Finder delivery, and Nightly separation - #24

Open
alexmodrono wants to merge 4 commits into
mainfrom
release/0.3.0
Open

Rename to Findle, course outlines, faster Finder delivery, and Nightly separation#24
alexmodrono wants to merge 4 commits into
mainfrom
release/0.3.0

Conversation

@alexmodrono

Copy link
Copy Markdown
Owner

Four independent changes, each buildable on its own.

Rename Foodle to Findle

Every remaining Foodle identifier moves to Findle: bundle prefix, App Group,
keychain service, File Provider domain prefix, framework targets, the on-disk
database path, and the CI workflows. Mechanical substitution plus six file
renames; no behaviour change.

Add course outlines and MCP catalog tools

Persists the section/module tree from core_course_get_contents in a new
course_outlines table (schema 15), so the MCP server can produce a compact
course brief and read an item's text without walking the file tree. Also scopes
the File Provider enumerators and several queries by site, and lets sign-in fall
back to the user-domain Application Support directory when the App Group
container is unavailable.

Deliver new coursework to Finder promptly

New files could take many minutes to show up, and the Moodle server was not the
cause — it answers in well under a second. The extension only reads rows the app
writes, so everything hung off the app's 30-minute timer, and the enumerators
were signalled once after the entire run, hiding finished courses behind slow
ones.

  • Signal each course as soon as its rows land
  • Sync courses through a task group capped at four instead of strictly serially
  • Download pinned items once per pass rather than once per course
  • Scope the per-course diff so idx_items_course does the filtering
  • Default cadence 30 min → 5 min
  • New Sync Now action in Finder, which wakes the app over a Darwin
    notification and launches it if it isn't running
  • Real byte-level download progress, so a large PDF no longer looks hung

Separate Nightly from the release installation

Nightly and release shared a File Provider mount name, the MCP loopback port and
the Claude config key, so a side-by-side install collided on all three. Nightly
also inherited the release appcast, meaning it would have updated itself into
the production build. All four are now derived per build, and Nightly ships with
no feed.

Release Nightly
App Findle.app Findle Nightly.app
Bundle ID es.amodrono.findle …findle.nightly
App group group.es.amodrono.findle …findle.nightly
Finder mount Findle-<site> Findle Nightly-<site>
MCP port 8765 8766
Claude config key findle findle-nightly
Sparkle feed release appcast none

Testing. Full suite passes (** TEST SUCCEEDED **), Debug and Nightly both
build, and all four commits build independently. Four new tests cover the
concurrent sync: completion fires exactly once per course, fetches overlap but
never exceed the cap, a limit of one stays serial, and a failed course reports
no completion.

Tag v0.3.0 after merge to trigger the release build.

The project ships as Findle, but the codebase still carried the original
Foodle name throughout: bundle identifiers, the App Group, the keychain
service, the File Provider domain prefix, framework and target names, the
on-disk database path, and the CI workflows.

Rename every occurrence so the identifiers match the product. The bundle
prefix becomes es.amodrono.findle, the App Group group.es.amodrono.findle,
the framework targets FindleNetworking / FindlePersistence / FindleSyncEngine,
and the state directory .FindleState/Findle/findle.db. The six Foodle-prefixed
source files are renamed with their contents otherwise unchanged.
The MCP server could list and search synced items, but had no way to answer
questions about a course's shape — which sections exist, what sits in each,
and how a given file fits in. Answering that from the items table alone meant
reconstructing the hierarchy on every call.

Persist the section/module tree returned by core_course_get_contents as a
course outline snapshot alongside the items, in a new course_outlines table
(schema 15). The catalog builds a compact course brief from it and can read an
item's extracted text directly, so an assistant can orient itself in a course
without walking the file tree.

Also scopes the File Provider enumerators and several database queries by site
so a multi-site install can't leak items between domains, and lets sign-in fall
back to the user-domain Application Support directory when the App Group
container is unavailable.
New files could take many minutes to appear in Finder, and the delay was not
the Moodle server: the File Provider extension only reads rows the app writes,
so it can never fetch anything itself. Everything hung off the app's timer,
which defaulted to 30 minutes, and the enumerators were signalled once after
the whole run finished — so the first course's files stayed invisible until the
last course had synced. Courses were also fetched strictly one at a time, the
site-wide pinned set was re-scanned once per course, and the per-course diff
loaded every item for the site and filtered in memory.

Signal each course as soon as its rows land, sync courses through a task group
capped at four, hoist the pinned-item download to run once per pass, and scope
the diff query to the course so idx_items_course does the filtering. The
default cadence drops to 5 minutes, and a "Sync Now" action in Finder asks the
app to sync immediately, launching it first if it isn't running — the extension
reaches it over a Darwin notification, which is the one channel that crosses
its sandbox without extra entitlements.

Materialization also reported no progress at all: the completion-handler form
of downloadTask stays silent until it finishes, so Finder showed an
indeterminate spinner for the whole transfer and a large lecture PDF looked
identical to a hung download. Downloads now run on a shared delegate-backed
session that reports bytes as they arrive, falling back to the file size Moodle
already reports when a response omits Content-Length.
Nightly already had its own bundle identifiers and App Group, but three things
were still shared and would have made a side-by-side install misbehave. The
File Provider root was named "Findle-<site>" unconditionally, so both builds
mounted the same folder under CloudStorage; the MCP helper's loopback port was
a hardcoded 8765, so whichever app launched second failed to bind; and the
Claude config entry was keyed "findle" for both, so installing Nightly silently
overwrote the release app's registration.

Worse, Nightly inherited the release appcast. Nightly ships as an unsigned CI
artifact and publishes no appcast of its own, so a Nightly install would have
followed the release feed and updated itself into the production build.

Derive the mount name, MCP port and config key from the bundle prefix — which
the File Provider extension resolves the same way, so the extension agrees with
the app it ships inside — and give Nightly an empty feed. UpdateController
leaves Sparkle unstarted when there is no feed rather than letting it raise a
configuration error, and Settings shows a manual-update note in place of
controls that could never work. ClaudeIntegration gains an uninstall path so a
Nightly registration can be removed without touching the release entry.
@github-actions

Copy link
Copy Markdown

Nightly Build

Download Findle Nightly (unsigned)

Built from a5767fa.

Important

This build is unsigned. macOS will block it on first launch. To open it:

  1. Try to open the app normally — macOS will show a warning and refuse.
  2. Go to System Settings → Privacy & Security, scroll down, and click Open Anyway.
  3. The File Provider extension requires code signing and won't work in this build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant