Skip to content

Move Where onto Periscope; retire LogKit + LogViewerUI#94

Draft
kyleve wants to merge 8 commits into
mainfrom
cursor/migrate-where-to-periscope
Draft

Move Where onto Periscope; retire LogKit + LogViewerUI#94
kyleve wants to merge 8 commits into
mainfrom
cursor/migrate-where-to-periscope

Conversation

@kyleve

@kyleve kyleve commented Jul 16, 2026

Copy link
Copy Markdown
Owner

What & why

Migrates the Where app and every one of its modules from the old LogKit / LogViewerUI logging stack onto Periscope, then removes LogKit and LogViewerUI entirely. This is the full structured redesign (plan option B): every logging call site is now a typed LogEvent on a Log<Event> scope tree, a single process-global PeriscopeStore replaces the two old LogStore ring buffers, and the DEBUG developer surface adopts the PeriscopeTools UI.

Landed as one commit per plan step, keeping the tree green at each step (LogKit survived until its last consumer moved over).

Logging design

  • Scope tree — a WhereLog facade vends a "Where" root Log with grouping scopes (location, reminders, backup, widgets, session, evidence, recentActivity); each collaborator derives its own typed LogEvent leaf (WhereLog.<group>(SomeLog.self) / WhereLog.root(SomeLog.self)). RegionKit keeps its own "RegionKit" root scope but emits into the same Periscope.shared.
  • Typed events — one LogEvent enum per collaborator, carrying the fields the old messages interpolated; catch-path events attach LogAttachment.error(_:). Severity conventions (info/warning/error/fault) and the PII-free .public contract are preserved; hot paths stay quiet.
  • One store — a single on-disk PeriscopeStore is attached to Periscope.shared at launch (AppDelegateWhereLaunch.bootstrapLogging), with retention pruning, exposed to the UI via an optional WhereModel.logStore. Widgets / share extension / intents run in their own processes and stay OSLog-only.
  • Developer UI (DEBUG)PeriscopeViewer replaces the old two-buffer LogViewer; adds an OpenSpansView, a Log View Mode toggle bound to a PeriscopeInspector, .logInspectable(_:) on high-value rows, .logContext(_:) at the root, and a PeriscopeAlerter (threshold .warning) wired to an in-app toast handler.

Build & module wiring

  • Package.swift / Project.swift: dropped the LogKit + LogViewerUI products, targets, test bundles, and schemes; swapped their deps for PeriscopeCore (RegionKit, WhereCore, WhereIntents, WhereWidgets, WhereShareExtension) and PeriscopeCore/PeriscopeUI/PeriscopeTools on WhereUI; unlisted them from the Where app, RegionViewer, and the Stuff-iOS-Tests scheme.
  • Deleted Shared/LogKit/ and Shared/LogViewerUI/.
  • Double-link caution respected: WhereUI (dynamic framework) statically embeds the Periscope products, so WhereUITests / WhereIntentsTests get them transitively and keep them out of extraPackageProducts.

Docs & dogfooding

  • Updated the root + Where/module AGENTS.md / README.md logging and dependency sections; regenerated the gitignored CLAUDE.md mirrors via ./sync-agents.
  • Appended the rough edges hit while dogfooding the API to Shared/Periscope/TODOs.md: late-attached sinks don't backfill the pre-attach recent buffer; inspect-by-object is scope- not instance-granular; and there's no eager (non-async) store handle. (The LogContextProviding parent-hierarchy, multi-process/App Group store sharing, and PeriscopeTools-on-Broadway gaps were already tracked.)

Testing

  • ./swiftformat --lint — clean.
  • tuist test Stuff-iOS-Tests (the full multi-bundle scheme, where the double-link regression reproduces) — 198 suites, all green.
  • Rewrote WhereLogTests against the new scope tree / event payloads and updated ScreenHostingTests (LogViewerPeriscopeViewer, inspector wiring). The double-link regression guard WhereStylesheetTests.resolvesTraitAwareTokensFromTheBroadwayRoot passes.

Commits

  • Migrate RegionKit logging to Periscope
  • Migrate WhereCore logging to Periscope
  • Migrate WhereUI view models to Periscope
  • Bootstrap a single PeriscopeStore at launch
  • Adopt PeriscopeTools in the DEBUG developer surface
  • Migrate Where extensions to Periscope
  • Remove LogKit and LogViewerUI
  • Update docs for the Periscope migration; log dogfood findings
Open in Web Open in Cursor 

kyleve added 8 commits July 15, 2026 18:48
Rebuild RegionLog as a Periscope facade: one "RegionKit" root scope with a
typed LogEvent per collaborator (RegionAttributor / RegionCatalog /
RegionGeometryCatalog), emitting into the process-wide Periscope.shared system
instead of a private LogKit LogStore. Errors ride as LogAttachments and region
ids as externalIDs. Swap RegionKit's LogKit dependency for PeriscopeCore and
add PeriscopeCore/UI/Tools to WhereUI (RegionMapView's one RegionLog call site
moves over now to keep the tree green). Closes plan step: Migrate RegionKit.
Rebuild WhereLog as a Periscope facade rooted at a "Where" scope with grouping
scopes (location, reminders, backup, widgets, session, evidence,
recentActivity) and derive a typed LogEvent per collaborator. Convert every
WhereCore call site to structured events, add measure() spans for store open,
backup archive read/write, and recent-activity generation, attach errors via
LogAttachment.error, and stamp day/sample/evidence externalIDs. Drop the dead
FoundationModelSummaryGenerator logger and the unused appDelegate category.

The legacy LogKit-backed WhereLog API (subsystem/store/Category/channel) is kept
alongside the new tree so WhereUI and the extensions stay green until their own
migration commits; WhereCore gains a PeriscopeCore dependency. Closes plan step:
Migrate WhereCore.
Convert every WhereUI view-model/view logging site to typed Periscope
LogEvents under the right scope: WhereModel/WhereLaunch/LoggedDaysModel as
"Where" leaves; WhereSession/YearReport/Backup/Resolve/CalendarView under the
session group; the evidence models under the evidence group; RecentActivityModel
under recentActivity. Add a per-collaborator LogEvent file for each, drop the
unused RemindersSettingsModel logger, and point DeveloperToolsView's legacy
LogViewer at the single WhereLog buffer (RegionKit no longer feeds a
LogViewerUI store) pending the PeriscopeTools rework. Closes plan step: Migrate
WhereUI view models.
Wire the Where app's durable logging at process launch: open one on-disk
PeriscopeStore, attach it to Periscope.shared as the durable sink, start
the built-in ambient sources, and prune history past a two-week retention
window. Runs off the launch critical path (the OSLog sink covers the
pre-attach window), and hands the opened store to WhereModel so the DEBUG
developer surface can browse persisted history.

Closes the store-bootstrap plan step.
Rework the Where app's developer tools onto Periscope's UI:

- DeveloperToolsView pushes PeriscopeViewer over the process-global store
  and adds an OpenSpansView monitor, replacing the LogViewerUI viewer.
- RootView builds a PeriscopeInspector once the launch bootstrap opens the
  store, injects it via .periscopeInspector, and adds a "Log View Mode"
  toggle bound to it; debugLogInspectable(_:) badges are sprinkled on the
  evidence rows and the location status row (no-ops in release).
- A DEBUG PeriscopeAlerter (threshold .warning) feeds an in-app toast
  surface (DeveloperToastCenter/Overlay) mounted above the app.
- RootView seeds the root \.logContext so view-level freeform logging lands
  under the "Where" scope.
- New DEBUG-only Strings for the open-spans and Log View Mode rows; hosting
  + Strings tests updated.

Closes the dev-ui plan step.
Convert the WhereIntents, WhereShareExtension, and WhereWidgets logging
call sites from the legacy LogKit facade (WhereLog.channel(_:)) to typed
Periscope LogEvents emitted on WhereLog.root(_:) leaf scopes. Each
extension runs in its own process, so Periscope.shared stays OSLog-only
(no persistent store).

Adds one LogEvent enum per extension (WhereIntentsLog, ShareExtensionLog,
WhereWidgetsLog) carrying the fields the old messages interpolated, with
error attachments on catch paths. Swaps the LogKit dependency for
PeriscopeCore on WhereIntents (Package.swift) and WhereWidgets /
WhereShareExtension (Project.swift).

Closes the extensions plan step.
Every consumer now emits through Periscope, so retire the legacy logging
stack entirely. Drops the LogKit-backed facade from WhereLog (the
subsystem/store/Category/channel API and its tests), removes the LogKit /
LogViewerUI products, targets, test bundles, and schemes from
Package.swift and Project.swift, unlists them from the Where app and
RegionViewer deps and the Stuff-iOS-Tests scheme, and deletes
Shared/LogKit and Shared/LogViewerUI.

Verified: project regenerates, ./swiftformat --lint clean, WhereCoreTests
green, and the full Stuff-iOS-Tests scheme builds and links.

Closes the remove-logkit plan step.
Rewrites the Where feature logging section (WhereLog as a Periscope scope
tree, typed LogEvent leaves, one PeriscopeStore sink, OSLog-only
extensions), swaps the LogKit dependency mentions for PeriscopeCore in the
extension + RegionViewer + WhereIntents docs, and refreshes the root
double-link note and never-swallow-errors rule for Periscope.

Appends the rough edges hit while dogfooding the API: late-attached
sinks don't backfill the pre-attach recent buffer, inspect-by-object is
scope- not instance-granular, and there's no eager (non-async) store
handle. Regenerates the gitignored CLAUDE.md mirrors via ./sync-agents.

Closes the docs-dogfood plan step.
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