mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
iOS port M0c: extract portable Services to ScarfCore
Third of four M0 sub-PRs. Moves the four Services that have no dependency
on Mac-target code or AppKit into ScarfCore, so the Mac + (future) iOS
targets can share them.
Files moved (4):
scarf/Core/Services/HermesDataService.swift (658 lines, SQLite reader + SnapshotCoordinator actor)
scarf/Core/Services/HermesLogService.swift (log tail + parse, LogEntry + LogLevel)
scarf/Core/Services/ModelCatalogService.swift (models.dev JSON reader, HermesModelInfo + HermesProviderInfo)
scarf/Core/Services/ProjectDashboardService.swift (per-project dashboard I/O)
Not moved, with reason:
HermesFileService.swift — carries the big shell-enrichment logic; a
later phase can port once iOS has a clearer env story for ACP spawns.
HermesEnvService.swift — depends on HermesFileService.
HermesFileWatcher.swift — depends on HermesFileService.
ACPClient.swift — M1's job (the ACPChannel refactor).
UpdaterService.swift — wraps Sparkle, stays Mac-only forever.
Platform guards:
HermesDataService.swift is wrapped in `#if canImport(SQLite3) ... #endif`
for the whole file. SQLite3 isn't a system module on Linux
swift-corelibs-foundation. Apple platforms compile unchanged. Linux
builds skip the file entirely; nothing in ScarfCore references
HermesDataService from outside the file, so there's no downstream
fallout.
ModelCatalogService `import os` / Logger definition / call site all
guarded with `#if canImport(os)`. Linux gets silent logging.
HermesLogService + ProjectDashboardService use only Foundation —
no guards needed.
Other fixes:
- Features/Settings/Views/Components/ModelPickerSheet.swift (the one
remaining consumer) gains `import ScarfCore`.
- Self-referential `import ScarfCore` stripped from each moved file.
Test coverage: 8 new tests in ScarfCoreTests/M0cServicesTests.swift:
- HermesLogService.parseLine exercised via readLastLines on a real
tmp file with three formats — v0.9.0+ with session tag, older
without, and garbage fallback. Pins CLAUDE.md's optional-session-tag
invariant.
- LogLevel SwiftUI colour strings pinned.
- HermesModelInfo.contextDisplay across 1M / 200K / 500 / nil cases;
costDisplay with and without costs.
- ModelCatalogService load path end-to-end against a synthetic
models_dev_cache.json lookalike — providers sorted, models
filtered, provider(for:) resolves both full-scan and slash-prefixed
IDs.
- Malformed + missing catalog files return empty, no crash.
- ProjectDashboardService round-trips ProjectRegistry + reads a
synthetic .scarf/dashboard.json.
Running `docker run --rm -v $PWD/scarf/Packages/ScarfCore:/work -w /work
swift:6.0 swift test` now reports 42 / 42 passing (M0a 16 + M0b 18 +
M0c 8).
Updated scarf/docs/IOS_PORT_PLAN.md progress log with the shipped M0c
state and the SQLite3-gating pattern future phases should reuse.
https://claude.ai/code/session_019yMRP6mwZWfzVrPTqevx2y
This commit is contained in:
@@ -303,7 +303,72 @@ stderr patterns, and round-trip an actual local file through
|
||||
iOS's Citadel-based transport lands (M4), it will provide its own env
|
||||
story — the existing macOS helper stays untouched.
|
||||
|
||||
### M0c — pending
|
||||
### M0c — shipped
|
||||
|
||||
**Shipped:**
|
||||
|
||||
- 4 portable Services moved to `Packages/ScarfCore/Sources/ScarfCore/Services/`:
|
||||
- `HermesDataService.swift` (658 lines, SQLite3-backed session/message/activity reader + `SnapshotCoordinator` actor)
|
||||
- `HermesLogService.swift` (log tailing + parsing, `LogEntry` + `LogLevel`)
|
||||
- `ModelCatalogService.swift` (models.dev cache reader, `HermesModelInfo` + `HermesProviderInfo`)
|
||||
- `ProjectDashboardService.swift` (per-project dashboard JSON I/O)
|
||||
- `HermesFileService.swift`, `HermesEnvService.swift`, `HermesFileWatcher.swift`,
|
||||
`ACPClient.swift`, and `UpdaterService.swift` stay in the Mac target.
|
||||
`HermesFileService` holds the big shell-enrichment logic and is the only
|
||||
non-portable heavyweight — a later phase can port it once iOS has a
|
||||
clearer story for shell-env-less ACP spawning. `ACPClient` is M1's job
|
||||
(the `ACPChannel` refactor). `UpdaterService` wraps Sparkle and stays
|
||||
Mac-only forever.
|
||||
- The one remaining external consumer that wasn't already importing
|
||||
ScarfCore (`Features/Settings/Views/Components/ModelPickerSheet.swift`)
|
||||
now has `import ScarfCore` added.
|
||||
|
||||
**Platform guards:**
|
||||
|
||||
- **`HermesDataService.swift` is wrapped in `#if canImport(SQLite3)` /
|
||||
`#endif`** — the whole file. SQLite3 isn't a system module on Linux
|
||||
swift-corelibs-foundation, and the service is unusable without it.
|
||||
Apple platforms (the real runtime targets) compile it unchanged. Linux
|
||||
builds just skip it. Nothing in ScarfCore references
|
||||
`HermesDataService` from outside that file, so there's no downstream
|
||||
fallout.
|
||||
- `ModelCatalogService.swift` — `import os` / logger definition / logger
|
||||
call sites all guarded with `#if canImport(os)`. Linux gets silent
|
||||
logging.
|
||||
|
||||
**Test coverage (`M0cServicesTests`):** 8 new tests.
|
||||
|
||||
- `HermesLogService.parseLine` exercised via `readLastLines` against a
|
||||
real local log file with three lines (v0.9.0+ format with session tag,
|
||||
older format without, and a garbage fallback line). Verifies the
|
||||
optional session tag handling called out in CLAUDE.md.
|
||||
- `LogEntry.LogLevel` colour strings pinned (SwiftUI views depend on
|
||||
them matching colour names).
|
||||
- `HermesModelInfo.contextDisplay` tested across `1M`, `200K`, `500`,
|
||||
and `nil` cases; `costDisplay` tested with and without costs.
|
||||
- `ModelCatalogService` load path exercised end-to-end against a
|
||||
synthetic `models_dev_cache.json` lookalike — providers sorted
|
||||
alphabetically, models filtered by provider, `provider(for:)` finds
|
||||
models both by full scan AND via `provider/model` slash-prefix
|
||||
fallback.
|
||||
- Malformed + missing file paths return empty results, no crash.
|
||||
- `ProjectDashboardService` round-trips a `ProjectRegistry` to disk and
|
||||
reads back a synthetic `.scarf/dashboard.json`.
|
||||
|
||||
**Rules next phases can rely on:**
|
||||
|
||||
- The `#if canImport(SQLite3)` gate pattern is established — any future
|
||||
ScarfCore code that touches SQLite3 directly should use the same
|
||||
whole-file or whole-block guard rather than trying to abstract SQLite
|
||||
behind a protocol (overkill; SQLite is reliably available on every
|
||||
target that can run Hermes client code).
|
||||
- Services take `ServerContext` in their init and construct their own
|
||||
transport via `context.makeTransport()`. M0d ViewModels should follow
|
||||
the same convention when they move to ScarfCore.
|
||||
- `LocalTransport()` (no-arg init) is the fast path for tests — uses
|
||||
`ServerContext.local.id`. Test helpers in ScarfCoreTests lean on this
|
||||
heavily.
|
||||
|
||||
### M0d — pending
|
||||
### M1 — pending
|
||||
### M2 — pending
|
||||
|
||||
Reference in New Issue
Block a user