rehuco — Implementation Plan
[[[implementation-plan]]]
Note
Tracer-Bullet First Slices
Each milestone opens with a tracer bullet — the thinnest real, kept, production-grade path through every layer, proving they connect end-to-end. Later iterations thicken one part at a time without breaking the working spine. This counters the failure mode of building layers deeply in isolation before anything works as a whole. LocalEdit1, CacheDB1, WatchTutorial1, and Borrowing1 are the tracer bullets; everything else thickens them.
Concept from The Pragmatic Programmer (Thomas & Hunt); further discussion at wiki.c2.com.
Companion to architecture-design.md. This plan covers the near-term build scoped to the stated personal
priorities:
- View/edit local
.rehufor basic tutorials and reference images. - Scan folders into a cached catalog and search it — close to the original tutcatalog.
- Watch a tutorial from a tablet/local browser.
- (later) Borrow a local copy for offline viewing on leave.
Before you create the monorepo (one-time, cheap-now/costly-later)
These are settled; listed so nothing is forgotten at uv init:
- Name:
rehuco(decided). It propagates into package names (rehuco-core,rehuco-node,rehuco-agent), PyPI namespace, import paths, file extensions (.rehu/.rehuco/.rehudb/.rehusw), config dirs, and the swarm-ID scheme — which is exactly why it's chosen before init. - Reserve PyPI names via throwaway
0.0.0stub repos (the same move used forpyside6-scintilla/-lexilla): claimrehuco,rehuco-core,rehuco-node,rehuco-agentbefore anyone squats them. - Root
pyproject.tomlis a virtual workspace — no[project]table, only[tool.uv.workspace]([[packaging-deployment#three-packages]]). Decided. rehuco-noderequires-pythonfloor — set at creation; no constraint from the TS-230 since the node runs on capable hardware ([[packaging-deployment#ts230-as-nas]]).- Fresh git history — start clean, old repos kept read-only as archive ([[packaging-deployment#migrating-repos]]). Decided.
Methodology: agile cadence, tracer-bullet spines
[[[implementation-plan#methodology]]]
Agile and tracer bullets are not alternatives — agile is the cadence (short iterations, something usable each cycle, adjust as you learn), tracer bullets are a technical strategy used within it (build a thin, real, kept end-to-end skeleton through every layer first, then thicken). This plan uses agile iterations whose first slice of each milestone is a tracer bullet.
Why tracer bullets specifically fit here:
- The risk is integration, not features — the architecture doc shows the features are understood; what's unproven is that the layers connect (agent↔node client split, the field/block model rendering end-to-end). UI approach: both QML and QtWidgets, each where it's strongest — QWidgets (with pyqtads docking) for dense sortable/filterable tables and trees (the browsers, [[plugins#browsers]]) and the app shell; QML for the image grid and animated lightbox. This needs the pyqtads-hosts-QML integration to hold (the spike below). Note: the "NVIDIA overlay" prompt seen on app start is the GeForce/NVIDIA App in-game overlay reacting to the GPU context — a per-machine NVIDIA-software setting to toggle off, not an app concern and not a rendering problem; it imposes no architectural constraint.
- It counters the prior failure mode — earlier versions built layers deeply (a full field/type system in TutCatalog5) without reaching a usable end-to-end whole. A tracer bullet keeps a usable-if-minimal thing alive from iteration one.
- Tracer bullets are kept production code, not throwaway prototypes — exactly the discipline wanted after three discarded attempts.
Rule for every milestone: the first iteration is the thinnest end-to-end path that works and is kept. Later iterations thicken one part without breaking the working spine.
The plan is a guide, not a contract — life beats any planning. Slices grow, split, and gain ad-hoc features as implementation teaches (LocalEdit2 already did; see its sub-slices on the GH milestone). Introducing a feature ad-hoc when it's needed is expected, not a deviation: the GH milestones/issues are the live record of what a milestone actually contains, and this plan catches up after the fact.
Spikes vs. tracer bullets (a related but different distinction)
A third term, spike (from XP), is often confused with a tracer bullet. They're distinguished by what you keep, which follows from what question each answers:
- Spike — answers "I don't know how/whether X works — let me find out." Time-boxed, quick-and-dirty, throwaway by intent. Its product is knowledge; the code is deleted afterward (keep the lesson, not the toy).
- Tracer bullet — answers "do my layers connect end-to-end?" Minimal but real, production-grade, and kept. Its product is a working skeleton you build on.
- Prototype — like a spike (throwaway), but broader/UX-exploratory rather than one sharp technical question.
The trap: letting a spike quietly become load-bearing — building on quick-and-dirty exploration code written before you understood the problem. Decide up front which you're writing and honor it. Quick test: "if this works, do I keep the code or just the lesson?" Keep the code → tracer bullet (write it properly). Keep the lesson → spike (write it fast and delete it).
They sequence: for a layer with genuine unknowns → spike (learn, discard) → tracer bullet (build the thin real spine, now informed) → thicken (iterations). Skip the spike where there are no real unknowns.
In this plan, LocalEdit1/CacheDB1/WatchTutorial1/Borrowing1 are tracer bullets (kept spines). The items flagged (spike) below are throwaway — keep the lesson, delete the code.
Model strategy (Claude Code): opusplan backbone, manual escalation for the hard cores
[[[implementation-plan#model-strategy]]]
Note
Use opusplan as the default (set /model opusplan at the start of each session). It ties model choice to mode:
Opus in plan mode (architecture, edge cases, tradeoffs), then automatically switches to Sonnet in execution
mode for code generation. For ~90% of rehuco — field widgets, UI wiring, REST endpoints, web templates, migration
tooling — this gives Opus-quality planning and Sonnet-speed execution with no micromanagement, and it's better than
hand-switching for routine work.
Its one blind spot matters for this app: opusplan switches on mode, not on how hard the code is. It assumes
"execution = mechanical." But rehuco has a few cores where the implementation itself is reasoning-dense and a subtle
error silently corrupts data. For those, manually switch to Opus (/model opus) even while implementing — context
carries over, so you can drop back to opusplan/Sonnet after. These cores are marked with > [!NOTE] blocks in
architecture-design.md; they are:
- Sync engine — version vector + activity log, conflict/merge, tombstones ([[sync#overview]]).
- Plugin block save invariant — the active/inactive/claim-then-abandon rule ([[plugins#plugin-blocks]]).
- Registry resolution & serve-after-resync — preferred-authority/chatter, version-marker comparison ([[discovery-trust-access#registry-home]], [[discovery-trust-access#serve-after-resync]]).
- Cross-filesystem safe move — checksum-gated, data-loss-sensitive ([[mounts-and-storage#safe-move-rename]]).
Don't micro-manage beyond that — constant hand-switching for ordinary work wastes effort; the point of opusplan is
to handle the common case so attention goes only to these few exceptions. Feeding the relevant § section into
context makes even Sonnet reliable on the routine parts and Opus more reliable on the hard ones. (Model names/aliases
shift over time and by plan/provider — verify the current /model list in Claude Code; the strategy is stable
regardless of version numbers.)
Pre-work (do before/around the first slice)
These unblock or de-risk everything after; small but high-leverage.
| Task | Kind | Why now | Notes |
|---|---|---|---|
| Stand up the monorepo (uv workspaces) | setup (kept) | The dev environment for everything; fixes the venv-confusion immediately ([[packaging-deployment#overview]]) | Root virtual workspace + packages/rehuco-core, packages/rehuco-agent; add rehuco-node later when the WatchTutorial milestone starts |
| Decide the tutorial and reference-image field lists | decision | Specific-field rendering is the one thing blocked on schema ([[appendices.open-questions#still-open]]) | The generic editor does not need this; only the rich per-type view does. Decide enough to start, refine later |
| Decide access-rule grammar | decision | Not needed for LocalEdit–WatchTutorial (single user, local or trusted-LAN) | Can defer to the Borrowing milestone or past it; noted so it's not forgotten |
| pyqtads + QML integration (regression check) | spike | Confirms the "both QML and QtWidgets" approach still holds on current Qt/PySide6/pyqtads versions | QML-in-pyqtads already worked previously; this re-verifies it on current versions, focused on the parts the app will depend on: a QQuickWidget dock that detaches to a floating window and re-docks without glitches (the classic QML cross-window trouble spot), a QWidgets dock and QML dock coexisting in one layout, and layout save/restore with a QML dock present — including whether closed/hidden docks restore their size (a known soft spot across all Qt docking; likely needs stashing dock size on close keyed by object name and restoring on show, rather than relying solely on the layout blob). Keep a tiny reference snippet of the working wiring; discard the rest. If a QML dock's detach glitches, the response is to keep QML surfaces in non-detachable docks or reduce the QML footprint — not switch to KDDockWidgets, which is foreclosed by its GPL license (architecture [[packaging-deployment#licensing-policy]]). pyqtads stays (LGPL, prebuilt PySide6 bindings). Result: approach holds on PySide6 6.11.1 + pyside6-qtads 5.0.0; one caveat — a closed dock's size needs a splitterSizes stash/reapply workaround — carried forward — it landed with the LocalEdit2.0 document-dock shell (#20). Recorded in [[packaging-deployment#qml-regression]] |
| QNAP/glibc dependency canary | spike | De-risks WatchTutorial's node deps early | Build a glibc-2.23 container; confirm FastAPI/uvicorn/zeroconf/pydantic-core/cryptography wheels load. Keep the lesson (a pinned compatible-versions list); the container/script is throwaway |
| File association + app identity | spike | De-risks LocalEdit1's "double-click → opens" before LocalEdit1 depends on it ([[packaging-deployment#app-identity]]) | macOS: a minimal .app (built via Briefcase) that's the default opener for .rehu and delivers the path as a QFileOpenEvent into a single running PySide6 instance ([[nodes#single-instance]]) — macOS does not pass it as argv. Windows: an HKCU ProgID for default double-click + an explicit AUMID (SetCurrentProcessExplicitAppUserModelID) so a pinned taskbar button shows the app's icon and lights up as running (the gap resource-hub only papered over with a PyInstaller exe), with DefaultIcon from a shipped .ico. Second double-click routes to the existing instance, not a new process. Keep the bundle/ProgID recipe + AUMID line; discard the toy GUI. Confirms Briefcase as the end-user packager as a side effect |
Milestone shape at a glance
The four detailed near-term milestones map onto the personal priorities above, each introducing exactly one new architectural spine — isolating integration risk (the architecture's thesis is that the risk is integration, not features). LocalEdit and CacheDB stay on a single machine (LocalEdit edits one file; CacheDB adds the cached catalog); WatchTutorial and Borrowing then climb the distribution ladder one rung at a time — WatchTutorial a single serving node, Borrowing a second party. Swarm and Daz3D are further milestones, detailed later.
| LocalEdit | CacheDB | WatchTutorial | Borrowing | |
|---|---|---|---|---|
| Use-case | View/edit a local .rehu |
Scan, cache, and search the catalog | Watch a tutorial from a browser | Borrow a copy, watch offline |
| Topology | 1 machine, no network | 1 machine, no network | 1 node + thin browser clients (LAN) | 2 parties (home node ↔ laptop) |
| New spine | Field/block/plugin rendering + local file I/O | SQLite cache (.rehudb) + incremental scan ([[data-model#scan-and-staleness]]) + browsers |
The node + agent-as-node-client refactor ([[nodes#two-roles]]) + web stack | Version-vector + activity-log sync ([[sync#overview]]), two-party |
| Deliberately absent | No node, swarm, or login | No node, network, or login | No swarm, pairing, multi-node sync, or auth-propagation | No general swarm — just two-party reconcile |
| New skill / risk | Rendering the data model end-to-end | Scan/cache correctness + table/search UI | Web stack (FastAPI/HTMX/Pico), video serving | Conflict/merge machinery |
Three principles hold across the split:
- Monotonically increasing distribution complexity. LocalEdit and CacheDB are standalone (no network) — CacheDB adds the cached catalog on the same single machine; WatchTutorial adds a single node serving thin clients; Borrowing adds two-party sync — the first real reconcile, but the minimal topology. The full N-node swarm (discovery, pairing, registry, safe-move — [[discovery-trust-access]], [[mounts-and-storage#fingerprint-map]]–[[mounts-and-storage#safe-move-rename]]) is the later Swarm milestone.
- One new integration risk per milestone. Each isolates a single unproven spine so nothing tackles two at once; within each, the first slice (LocalEdit1/CacheDB1/WatchTutorial1/Borrowing1) is a kept tracer bullet and the rest thicken it.
- Each is independently useful and shippable. LocalEdit is a standalone editor even if the rest never ship; CacheDB makes it a real searchable catalog; WatchTutorial adds tablet watching; Borrowing adds offline borrow. Value lands at every milestone boundary.
Heavier work beyond these four — acquisition/LLM tooling ([[acquisition-tooling#overview]]), reference-image richness, 3D objects, multi-user auth, native installers — is deferred (see What is deliberately deferred); the full swarm and Daz3D are further milestones (Swarm, Daz3D), not part of the near-term four.
LocalEdit — Local view/edit (no node, no swarm, no login)
Goal: a standalone, usable tool that opens, displays, and edits a local .rehu for tutorials and reference images.
This is the agent's local-file mode ([[nodes#local-vs-swarm]]), and a genuinely useful product on its own.
LocalEdit1 — Tracer bullet (the spine)
Double-click a
.rehuin File Explorer → single-instance agent opens → reads the file off disk → renders common fields + Markdown + a basic image strip → edit one field → atomic-save back.
Deliberately minimal: one resource shown via the generic field editor (not yet a rich plugin), no field toolkit depth, no block lifecycle, no cache. It only has to prove the spine: file-association → single-instance forward → read → render → edit → atomic-write. Keep this code.
Touches, thinly: [[nodes#single-instance]] (single-instance/association), [[data-model#write-integrity]] (atomic write), [[nodes#local-vs-swarm]] (local-file mode), [[plugins#fallback-editor]] (generic editor), [[plugins#tutorial-plugin]] (Markdown + image strip).
LocalEdit2–LocalEditX — Thicken the spine (iterations, each shippable)
- LocalEdit2 — Field toolkit. Real field widgets (text, switch, tag-list, date, rating, duration, size, choice, path,
image-count) with editor/viewer variants — the shared toolkit plugins compose from ([[plugins#core-vs-plugin]]); the
toolkit and viewer/editor/both surfaces are designed in [[plugins#toolkit-surfaces]]. (TutCatalog5 has prior art here
to draw on as a design reference.) In practice LocalEdit2 is wider than the field widgets alone: it opened with its own
mini-tracer — LocalEdit2.0/#20, the document-dock shell + reactive view-model + text-field spine ([[plugins#dock-shell]],
[[plugins#view-model]]) — and is broken into sub-slices LocalEdit2.0–LocalEdit2.8, tracked as issues on the GH
LocalEdit2milestone (session/close-guard persistence, the per-type field widgets, multi-source and image-selection editors, unknown-field fallback). - LocalEdit3 —
.rehuformat + versioning. JSON read/write, per-file format-version field, preserve-unknown-fields rule ([[data-model#schema-version]]). Bring.tc→.rehumigration in as the oldest source format ([[acquisition-tooling#tc-to-rehu]]) — opening a.tcoffers migration + screenshot-name normalization. (It is not "format v0": a.tcis a different file format, not an old.rehu, and the mapping emits the current.rehulayout stamp included — v0 means an unstamped.rehu, [[data-model#schema-version]].) - LocalEdit4 — Plugin block model. Keyed per-plugin blocks, single-active-type rule, the save-persistence invariant (active type or never-claimed foreign payload; claim-then-abandon drops on save), generic fallback for inactive/unknown blocks with carry-or-drop ([[plugins#plugin-blocks]]/[[plugins#fallback-editor]]). This is the genuinely new core logic.
- LocalEdit5 — Tutorial plugin (rich). Real tutorial viewer/editor: full image lightbox (click-to-maximize, prev/next, hideable strip, ESC), folder-rename-from-suggestions ([[plugins#tutorial-plugin]]).
- LocalEdit6 — Reference-images plugin (basic). Type + fields + viewer; defer redaction/slideshow/search to later (they're [[plugins#refimages-plugin]] richness, not needed for "view/edit").
- LocalEdit7 — Log dock, task queue, checksums. Three items, in dependency order — each is the one before it made useful:
- In-app log viewer — a log dock fed by a caching
logging.Handlerbridge (records logged before the GUI exists are replayed, not lost) plus a level-filterable log widget. rehuco has only colorized console logging today, while every recent predecessor shipped this; prior art, including the cache-then-replay design, is in [[pyside-ibo#log-stack]]. First — it is the simplest real dock, and it is what makes the two items below observable when they misbehave. - Task queue / dock ([[architecture-design#components]]) — the visible, app-wide queue of slow operations (checksum, sync, scans, copies, node-notify, safe moves) with pause/resume/cancel/reorder, multi-select serializing work rather than running it all at once. Specified as a component since the architecture doc but never scheduled; LocalEdit7 is where it lands, because checksums are its first real client and every later milestone (CacheDB scans, Borrowing sync/copies) assumes it exists.
- Checksums — generate/verify with algorithm-tagging, as task-queue jobs ([[data-model#checksums]]) — pairs naturally with migration.
- LocalEdit8 — Legacy catalog import. Bulk
.tc→.rehuconversion across a folder tree, run as task-queue jobs (LocalEdit7) over a dry-run plan, with backups retained by default and an in-app revert/discard surface replacing the file-manager round-trip ([[acquisition-tooling#tc-to-rehu]]). This is the one-time migration of an existing tc4 catalog, and doing it here is what keeps.tcout of CacheDB's scanner entirely. - LocalEdit9 — Migration correctness. What pointing LocalEdit8's wizard at a real catalog surfaced, and the scoping
rule it forced. The
.tcwalk halts at the first record it finds, so a template at a tree root — or any tc4 collection, which is a parent record over member directories — hides everything beneath it. Conversion backups count as a resource's own content, so a bulk import bakes each resource's.originto its first baseline and every measured size shrinks the moment the backups are discarded. And content scoping overlaps by design: a nested record's files are counted by its ancestors too, which makes a library's size unsummable from its records — the aggregation CacheDB exists to do. So content becomes exclusive — a record counts only what it covers — a verify prunes what it no longer covers and moves the claim to whichever record now does, and a legacy.sfvis retired once its claim is in the.checksuminstead of shadowing it forever ([[data-model#resource-scoping]], [[data-model#checksums]]). Carries the task dock's own polish alongside it, and is where a.tc-era resource stops being a special case anywhere but the importer. - LocalEditX — Tray + polish. Close-to-tray/explicit-quit ([[nodes#single-instance]]), preferences. Last in the milestone by construction: the catch-all polish slice, named like an audit run because it is never "next", only "after everything else".
Exit criteria: open any tutorial or ref-image .rehu (or migrate a whole .tc catalog), view it richly, edit and
atomic-save, generate/verify checksums. Standalone, no infrastructure.
Rough size: ~3–5 focused dev-weeks (front-loaded refactoring against prior-version design knowledge, not greenfield).
CacheDB — Cached database (scan, cache, search; still local, no node)
Goal: point the agent at your folders, have it scan the .rehu files into a .rehudb cache, and browse/search the
catalog on the desktop. This brings the app close to the original tutcatalog: a real, searchable library view over
everything on disk. Still one machine, no network, no node, no login — the cache is built and read by the agent
itself.
Note: the cache is a rebuildable derivative of the
.rehufiles, never a source of truth ([[data-model#local-file-trio]]). WatchTutorial later moves cache ownership to the node; here the agent owns it.
CacheDB1 — Tracer bullet (the spine)
Point the agent at one folder → it scans the
.rehufiles into.rehudb→ a table lists them → type a query → the table filters live.
Minimal: one scan pass (no incremental yet), the common columns only, a single text filter. Proves the spine: scan → cache → list → search. Keep this code.
Touches, thinly: [[data-model#scan-and-staleness]] (scan), [[data-model#local-file-trio]] (.rehudb as derived cache),
[[plugins#browsers]] (table view).
CacheDB2–CacheDB4 — Thicken
- CacheDB2 — Incremental, version-aware scan. Rescan only what changed (mtime/size/format-version), prune vanished
entries, keep
.rehudbcurrent across edits ([[data-model#scan-and-staleness]]). Scans run as task-queue jobs (LocalEdit7). - CacheDB3 — Generic + tutorial browsers. Table view with common columns + tutorial columns (duration, progress); click opens the viewer; click-to-filter on tag/author/publisher ([[plugins#browsers]]).
- CacheDB4 — Search. Query the cache by text and by field (type, author, publisher, tags, rating), with combinable filters, fast enough over a large library to feel live.
Exit criteria: point the agent at your real folders, get a searchable catalog on the desktop, and click through to view or edit any resource. Standalone, no node.
Rough size: ~2–3 focused dev-weeks (the cache/scan is the new correctness-sensitive part; the browsers reuse the field toolkit from LocalEdit).
WatchTutorial — Watch a tutorial from a tablet/local browser
Goal: from the iPad (thin browser client, [[borrowing#vacation-topology]]), browse the tutorials a node serves and watch one, with progress recorded. Single node, on the LAN — no swarm, no pairing, no multi-node sync, no auth-propagation (single-node base case, [[multiplicity#single-node-base]]).
Note: this introduces the node and the agent-as-node-client refactor ([[nodes#two-roles]]) — the first architecturally new spine beyond the single-machine milestones. Cache ownership moves from the agent (CacheDB) to the node here.
WatchTutorial1 — Tracer bullet (the spine)
A single headless node serves an HTTP page listing one configured tutorial → tap it → browser plays the video → progress is recorded server-side and survives a reload.
Minimal: one node, one hard-configured folder, no auth, plain video serving. Proves the spine: node serves → browser lists → browser plays → progress persists.
Touches, thinly: [[nodes#overview]] (REST node), [[multiplicity#single-node-base]] (single-node), web stack (FastAPI/HTMX/Pico — new skill), [[plugins#tutorial-plugin]] web/follow, progress write ([[sync#overview]]/[[mounts-and-storage#node-handoff]] minimal).
WatchTutorial2–WatchTutorial6 — Thicken
- WatchTutorial2 — Agent-as-node-client refactor. Move the agent's catalog reads to go through the node ([[nodes#two-roles]]);
the node now owns and serves the
.rehudbbuilt in CacheDB. The local-file viewer (LocalEdit) stays node-free; only catalog operations route through the node. (rehuco-nodepackage created now.) - WatchTutorial3 — Generic + tutorial web browsers. The CacheDB table view, mirrored in the browser for the tablet ([[plugins#browsers]]).
- WatchTutorial4 — Web follow mode. Sequential playback, progress/duration tracking, notes, bookmarks in the browser ([[plugins#tutorial-plugin]] web).
- WatchTutorial5 — Progress sync frequency + handoff basics. Frequent progress writes so a reload/resume is current ([[mounts-and-storage#node-handoff]]) — even single-node benefits.
- WatchTutorial6 — Minimal auth (optional this milestone). Even single-user, a login gate for the web UI ([[discovery-trust-access#user-auth]]) if you want the tablet to require it; can defer if it's just you on a trusted LAN.
Exit criteria: open the web UI on the iPad over the LAN, see your tutorials, watch one, progress is remembered across sessions and devices.
Rough size: ~4–6 focused dev-weeks, much of it the web-stack learning curve (new to you) rather than architecture. The node runs on a capable box (Mac mini or always-on Linux node); the TS-230 is accessed via its existing SMB share ([[packaging-deployment#ts230-as-nas]]).
Borrowing — Borrow a local copy for offline viewing
Goal: before leaving, borrow a tutorial onto a laptop; watch it offline (the laptop runs its own node, [[borrowing#vacation-topology]]); sync progress/notes back on return. This is a two-party sync (home node ↔ laptop), far simpler than general swarm sync.
Borrowing1 — Tracer bullet (the spine)
Mark a tutorial "borrow" → its files +
.rehucopy onto the laptop, borrow recorded in the user meta block ([[borrowing#recording-borrows]]) → laptop node serves it offline → on return, progress/notes reconcile back.
Touches, thinly: [[borrowing#another-instance-role]] (borrow as instance role), [[instances-and-dedup#instance-registry]] (instance tracking), [[sync#overview]]/[[offline-editing#overview]] (two-party reconcile), [[borrowing#recording-borrows]] (borrow-in-meta).
Borrowing2–Borrowing5 — Thicken
- Borrowing2 — Instance registry (minimal). Track where a UUID's copies live + roles; enough for borrow/return ([[instances-and-dedup#instance-registry]]).
- Borrowing3 — Version-vector + activity-log sync. The real reconcile machinery ([[sync#overview]]), scoped to two parties first.
- Borrowing4 — Return/reconcile UI. Merge progress/notes; handle the borrow-vs-changed cases ([[borrowing#borrow-vs-delete]]) if they arise.
- Borrowing5 — Scheduled archival. Borrow→archive-on-return, full or selective ([[borrowing#scheduled-archival]]).
Exit criteria: borrow → go offline → watch + take notes → return → changes reconciled.
Rough size: ~3–5 focused dev-weeks in the minimal (laptop + one home node) topology.
What is deliberately deferred past these four
Everything that isn't on the personal critical path, per the architecture doc's own scoping. (The full multi-node Swarm and Daz3D support are themselves later milestones, not part of the near-term four — see the roadmap.)
- Acquisition tooling (LLM URL extraction, image-drag, HTML→Markdown) — [[acquisition-tooling#overview]]. Explicitly deferred until after the tutorial web viewer; manual entry suffices meanwhile. (HTML→Markdown and image-drag are cheap enough to slip into LocalEdit if convenient.)
- Reference-image richness (redaction, tag/semantic search, sketch slideshow, drawing critique) — [[plugins#refimages-plugin]].
- 3D objects, dedup review UI, access-control grammar, multi-user auth propagation, web for non-tutorial types.
- Auto-update (the installers themselves landed in #206, [[appendices.briefcase-packaging#status]]) — Briefcase-built installers with declarative file association / icon /
AUMID, MSIX later, and self-update against a public release oracle
([[packaging-deployment#app-identity]]/[[packaging-deployment#auto-update]]).
uv tool installcovers the author's own machines regardless; the file-association mechanics are proven earlier by the Pre-work spike, but release delivery — a published artifact (#208), update delivery, and code-signing/notarization — still waits.
Sequencing gates (decide-before-you-start)
- Before LocalEdit2: enough of the tutorial + ref-image field lists to render them (the generic editor needs nothing).
- Before the dock manager + mixed QML/QWidgets UI is adopted: the pyqtads + QML integration spike (pre-work, issue #4) — confirms a QML dock detaches/re-docks and coexists with QWidgets docks on current versions. Done; the dock-manager half is already adopted — the QtAds document-dock shell landed with the LocalEdit2.0 tracer (#20, [[plugins#dock-shell]]) — while the first QML dock is still ahead.
- Before LocalEdit1 relies on "double-click → opens": the file-association + app-identity spike (pre-work) — macOS
.app/QFileOpenEventand Windows ProgID/AUMID, so default-double-click open and taskbar pin/running actually work; also settles Briefcase as the end-user packager. - Before WatchTutorial (serving NAS content): no glibc gate — the node runs on capable hardware with the TS-230 mounted via SMB ([[packaging-deployment#ts230-as-nas]]). The glibc canary findings ([[packaging-deployment#glibc-canary]]) are kept as a reference if direct QNAP deployment is ever reconsidered.
- Before WatchTutorial web work: a short FastAPI/HTMX/Pico spike, since it's a new stack — answer "can I build the follow-mode page the way I need?", keep the lesson, discard the toy.
- Before WatchTutorial1 promises "browser plays the video": an iPad-playback spike — serve a representative sample of the real catalog to the actual tablet: container/codec coverage (Safari plays H.264/HEVC in MP4/MOV; MKV — common in tutorial catalogs — does not play natively), the self-signed-HTTPS trust story ([[appendices.open-questions#still-open]]), and HTTP Range seeking. The outcome decides whether WatchTutorial grows a remux/transcode task-queue job.
- Before Borrowing: nothing new architecturally — it reuses [[sync#overview]]'s reconcile, scoped to two parties.
Honest caveats
- Estimates are focused dev-weeks (uninterrupted equivalent), not calendar time, and assume the design holds under implementation — building always surfaces changes (as the design conversation itself repeatedly showed). Treat them as ordering and relative-size guidance, not promises.
- The prior versions de-risk design (you know what you want, you've tried approaches) more than they supply droppable code — especially since only the oldest (TutCatalog4, C++/Qt5) reached "usable," and the Python ones are ideas/scaffolding to redesign.
- Personal-priority path to a genuinely useful standalone tool: LocalEdit + CacheDB (local edit, then a searchable cached catalog — ~5–8 focused dev-weeks), with a standalone-usable result already at the end of LocalEdit. Tablet watching (WatchTutorial) and offline borrow (Borrowing) build on top.