mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
docs(wiki): mark M7/M8/M9 issues fixed; add Post-pass-1 feature work
+38
-18
@@ -72,36 +72,56 @@ All tracked from the 2026-04-24 pass-1 smoke test. This list is the truth about
|
|||||||
|
|
||||||
| # | Summary | Scope | Status |
|
| # | Summary | Scope | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | **Primary navigation hidden below Dashboard fold.** Chat / Memory / Cron / Skills / Settings links live as the 4th section in a `List` — users can't reach any feature without scrolling. Needs `TabView` root (see M8). | ScarfGo | Open (deferred to M8) |
|
| 1 | **Primary navigation hidden below Dashboard fold.** Chat / Memory / Cron / Skills / Settings links lived as the 4th section in a `List`. Replaced with `.tabViewStyle(.sidebarAdaptable)` root: 4 primary tabs (Chat / Dashboard / Memory / More) + collapse to sidebar on iPadOS later with zero UI code change. | ScarfGo | ✅ Fixed |
|
||||||
| 2 | **Non-retryable provider errors → perpetual spinner.** If Hermes returns HTTP 404 from the LLM provider, the iOS UI shows "Agent is working…" forever. The error is flowing through ACP stderr but isn't surfaced to the user. Needs a banner with retry / settings affordance. | ScarfGo | Open |
|
| 2 | **Non-retryable provider errors → perpetual spinner.** ACP error triplet (`acpError`, `acpErrorHint`, `acpErrorDetails`) promoted to ScarfCore so Mac + ScarfGo share state; ChatView renders an inline banner with Copy Details / Expand. `handlePromptComplete` now calls `recordPromptStopFailureUsingProvider(stopReason:)` on non-`end_turn` stops with the stderr tail appended. | Cross-platform | ✅ Fixed |
|
||||||
| 3 | **No connecting feedback when entering Chat.** 1-second load is silent — no spinner, no "Connecting to agent…". | ScarfGo | Open |
|
| 3 | **No connecting feedback when entering Chat.** ChatController's existing `.connecting` state now drives a `.regularMaterial` overlay with "Connecting to <nickname>…" + ProgressView. | ScarfGo | ✅ Fixed |
|
||||||
| 4 | **`isAgentWorking` doesn't clear after primary response.** Spinner flips off only on the ACP `promptComplete` event; auxiliary post-work (title generation) delays it. Main response is long since done. Fix: clear on assistant-stream-end, keep a subtle secondary indicator for post-work. | ScarfGo | Open |
|
| 4 | **`isAgentWorking` doesn't clear after primary response.** Split into computed `isGenerating` (agent still producing text) + `isPostProcessing` (agent done producing; ACP `promptComplete` not yet fired). Prominent spinner drops as soon as the reply is visible; subtle "Finishing up…" pill covers auxiliary post-work. Applied cross-platform. | Cross-platform | ✅ Fixed |
|
||||||
| 5 | **ACP command missing PATH prefix.** SSH exec runs a non-interactive shell whose PATH is `/usr/bin:/bin:/usr/sbin:/sbin`. `~/.local/bin/hermes` (pipx default) wasn't on PATH; ACP died with `command not found: hermes`. Fixed by prepending common install locations to PATH. | ScarfGo | ✅ Fixed |
|
| 5 | **ACP command missing PATH prefix.** SSH exec runs a non-interactive shell whose PATH is `/usr/bin:/bin:/usr/sbin:/sbin`. Fixed by prepending common install locations (`~/.local/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `~/.hermes/bin`) to PATH inline in the exec command. Mirrors `HermesPathSet.hermesBinaryCandidates`. | ScarfGo | ✅ Fixed |
|
||||||
| 6 | **SFTP `~` tilde not expanded.** Every Memory / Cron / Skills / Settings read used paths like `~/.hermes/memories/MEMORY.md`. SFTP treats `~` as a literal character, not a home-dir alias, so every read silently returned nil. Fixed: added cached `resolveHome()` on `ConnectionHolder` + a `resolveSFTPPath()` helper applied to every SFTP entry point. This was the root cause of multiple "empty content" bugs. | ScarfGo | ✅ Fixed |
|
| 6 | **SFTP `~` tilde not expanded.** Per-connection cached `resolveHome()` on `ConnectionHolder` + `resolveSFTPPath()` helper applied to every SFTP entry point (`readFile` / `writeFile` / `fileExists` / `stat` / `listDirectory` / `createDirectory` / `removeFile`). | ScarfGo | ✅ Fixed |
|
||||||
| 7 | **No loading state on Memory editor.** The editor _has_ a ProgressView code path, but since SFTP silently returned nil, `isLoading` flipped false instantly with empty content — no spinner, no error, just blank. Exposed by #6. | ScarfGo | Needs follow-up after #6 |
|
| 7 | **No loading state on Memory editor.** Switched to throwing read (#8) so `lastError` populates on real failures instead of silently showing "empty" — the existing error banner now renders. | ScarfGo | ✅ Fixed |
|
||||||
| 8 | **`ServerContext.readText` swallows errors with `try?`.** Masks transport failures as "empty file." Refactor to throw or return `Result`. | Cross-platform | Open |
|
| 8 | **`ServerContext.readText` swallows errors.** New `readTextThrowing(_:)` distinguishes "file absent" from "transport error"; old nil-returning `readText` stays as a `try?` shim for callers that really don't care. Memory editor uses the throwing variant. | Cross-platform | ✅ Fixed |
|
||||||
| 9 | **TextEditor keyboard obscures cursor on bottom edit.** Tapping near the bottom of Memory editor scrolls content under the keyboard; cursor invisible. Needs `ScrollView` + `ScrollViewReader` + keyboard-aware padding. | ScarfGo | Open |
|
| 9 | **TextEditor keyboard obscures cursor.** `.scrollDismissesKeyboard(.interactively)` on the TextEditor, error pill + Saved pill moved into `.safeAreaInset(edge: .bottom)` so SwiftUI draws them above the keyboard. | ScarfGo | ✅ Fixed |
|
||||||
| 10 | **Save confirmation not visible.** The "Saved" pill is coded but positioned at `.bottom` with only 16pt padding — keyboard covers it, auto-dismisses after 1.5s. Reposition or raise above keyboard via `.safeAreaInset(edge: .bottom)`. | ScarfGo | Open |
|
| 10 | **Save confirmation not visible.** Saved pill is now a full-width material strip inside `.safeAreaInset`, holds 2.5s (up from 1.5s), and cancels any in-flight hide task on subsequent saves so rapid saves don't drop the pill mid-fade. | ScarfGo | ✅ Fixed |
|
||||||
| 11 | **Cron schedule + next-run shown as machine formats.** List shows `0 */6 * * *` and `2026-04-24T18:00:00+02:00`. Needs a `CronScheduleFormatter` in ScarfCore (common patterns → human readable) + `.relative(presentation: .numeric)` for next-run. Fix both macOS and ScarfGo simultaneously. | Cross-platform | Open |
|
| 11 | **Cron schedule + next-run shown as machine formats.** New `CronScheduleFormatter` in ScarfCore translates the common cron shapes (every N minutes / hourly / daily at H / weekdays at H / weekends / specific weekday / monthly on day D + @-macros) into English phrases and falls back to raw expression on unrecognised shapes. Sibling `formatNextRun(iso:)` parses Hermes's ISO-8601 next-run and renders `"in 4 hours"` etc. 17 unit tests. Applied Mac + ScarfGo. | Cross-platform | ✅ Fixed |
|
||||||
| 12 | **"Disconnect" is factory reset.** Button wipes Keychain key + config, forcing re-onboarding including key regeneration + re-append to `authorized_keys`. Interim fix: rename to "Forget this server" with destructive red styling + confirmation alert. Proper fix: soft Disconnect vs. hard Forget (M9 multi-server). | ScarfGo | Open (rename pending) |
|
| 12 | **"Disconnect" is factory reset.** Split properly into **Disconnect** (soft — keeps Keychain key + config, returns to ServerListView, next tap reconnects with no re-onboarding) and **Forget** (hard — removes that server's key + config, returns to list or onboarding if list becomes empty). Lives on the More tab. | ScarfGo | ✅ Fixed |
|
||||||
|
|
||||||
### Cross-platform (fix on Mac too)
|
### Cross-platform (fix on Mac too)
|
||||||
|
|
||||||
- **Model picker accepts unknown models for a given provider.** Configured `nous` provider + `claude-haiku-4-5-20251001` — Nous doesn't serve that model, runtime 404. Validate model ID against the provider's catalog at save time. Affects all providers with finite catalogs (Nous Portal, OpenAI Codex, Qwen OAuth). This is the underlying cause of bug #2.
|
- **Model picker accepts unknown models.** `ModelCatalogService.validateModel(_:for:)` returns `.valid` / `.unknownProvider(id)` / `.invalid(providerName, suggestions)`. Overlay-only providers (Nous / OpenAI Codex / Qwen OAuth) short-circuit to `.valid` because their catalogs aren't in models.dev. Mac `ModelPickerSheet.submitSelection` routes through the validator and raises an alert with suggestions on `.invalid`. 5 unit tests. — ✅ Fixed
|
||||||
|
|
||||||
### Hermes-side (upstream, not ours)
|
### Hermes-side (upstream, not ours)
|
||||||
|
|
||||||
- Auxiliary `title_generation` appears to hang when the main provider returns a 404 — likely retries the failed call. Worth filing upstream; causes bug #4 to manifest.
|
- Auxiliary `title_generation` appears to hang when the main provider returns a 404 — likely retries the failed call. Worth filing upstream; causes bug #4 to manifest. — still open upstream.
|
||||||
|
|
||||||
|
## Post-pass-1 feature work
|
||||||
|
|
||||||
|
The pass-1 session also surfaced the user-facing roadmap we delivered through M8 (UX density) and M9 (on-the-go features):
|
||||||
|
|
||||||
|
### M8 — UX density (all shipped)
|
||||||
|
|
||||||
|
- Dynamic Type clamped at scene root to `.xSmall ... .accessibility2`.
|
||||||
|
- TabView root nav replacing Dashboard-as-hub (see fix #1 above).
|
||||||
|
- `.scarfGoCompactListRow()` + `.scarfGoListDensity()` tokens applied to Memory / Cron / Skills / Dashboard / MoreTab / ServerList for ~48pt rows that still meet the 44pt tap-target invariant.
|
||||||
|
- Chat: fenced code blocks render in a horizontally-scrollable `CodeBlockView` (240pt collapsed, Expand to full) instead of soft-wrapping into unreadable columns; message bubbles gained `.contextMenu` (Copy + Share); iOS 17+ `.defaultScrollAnchor(.bottom)` + iOS 18's `.defaultScrollAnchor(.bottom, for: .sizeChanges)` replace the manual `scrollTo` dance.
|
||||||
|
- Custom `.presentationDetents` per sheet — `[.height(220), .large]` for permission sheet, `[.large]` for cron editor; never the misleading `.medium`.
|
||||||
|
|
||||||
|
### M9 — Multi-server + on-the-go (all shipped)
|
||||||
|
|
||||||
|
- **Multi-server** — storage layer (UserDefaults + Keychain) now keys by `ServerID`, with one-shot v1 → v2 migration so updating the app doesn't re-onboard anyone. New `ServerListView` root shows every configured server with nickname / user@host:port / tap-to-connect / swipe-to-forget. "+" button re-enters onboarding for a fresh server. ScarfGoTabRoot splits the old factory-reset "Disconnect" into soft Disconnect + destructive Forget rows in the More tab.
|
||||||
|
- **Session resume** — Dashboard Recent Sessions rows are now tappable; `ScarfGoCoordinator` routes the tap to the Chat tab with a `pendingResumeSessionID`; ChatController.startResuming calls `session/resume` (or falls back to `session/load` on older Hermes) with the full transcript preserved.
|
||||||
|
- **Project-scoped chat** — "+" in Chat opens a picker: Quick chat vs. In project…. Project list loads from `~/.hermes/scarf/projects.json` over SFTP. On project select, ScarfGo SFTP-writes the Scarf-managed block into `<project>/AGENTS.md` via the shared `ProjectContextBlock` service (same byte-for-byte markers as the Mac app — projects round-trip cleanly), spawns `hermes acp` with `cwd = project.path`, and records the session attribution in `session_project_map.json`. `SessionAttributionService` moved from Mac target into ScarfCore so both apps use the same store.
|
||||||
|
- **Scoped Settings editor** — curated list of 7 editable keys (model.default, model.provider, approvals.mode, agent.max_turns, display.show_cost / show_reasoning / streaming) as a Quick Edits section at the top of Settings. Save routes through `hermes config set <key> <value>` on the remote (Hermes owns the YAML round-trip); Scarf just picks the value. Inline error banner on sheet if the remote command fails.
|
||||||
|
- **APNs push skeleton** — `APNSTokenStore` + `NotificationRouter` ship ready for a future Hermes-side push sender. Lock-screen "Approve" / "Deny" action category is registered. Capability stays OFF in Xcode until Hermes gains a sender + we have an APNs auth key; flipping the capability on is a ~5-line follow-up.
|
||||||
|
|
||||||
## Reporting issues
|
## Reporting issues
|
||||||
|
|
||||||
- **Bugs:** <https://github.com/awizemann/scarf/issues> — tag with `component: ios` / `component: scarfgo`.
|
- **Bugs:** <https://github.com/awizemann/scarf/issues> — tag with `component: scarfgo`.
|
||||||
- **Feature requests:** same, tag with `feature: ios`.
|
- **Feature requests:** same, tag with `feature: scarfgo`.
|
||||||
- **Security / credential handling concerns:** use the repo's security policy.
|
- **Security / credential handling concerns:** use the repo's security policy.
|
||||||
|
|
||||||
## For contributors
|
## For contributors
|
||||||
|
|
||||||
See [Architecture Overview](Architecture-Overview) for how ScarfCore + ScarfIOS fit together. The `scarf-mobile-development` branch is the working branch; pass-1 and all M7 fixes land there first, merge to `main` once M7 closes.
|
See [Architecture Overview](Architecture-Overview) for how ScarfCore + ScarfIOS fit together. The `scarf-mobile-development` branch is the working branch; the M7/M8/M9 changes above live there pending a pass-2 smoke test before merging to `main`.
|
||||||
|
|
||||||
---
|
---
|
||||||
_Last updated: 2026-04-24 — post pass-1 smoke test_
|
_Last updated: 2026-04-24 — post M7/M8/M9 implementation, pass-2 pending_
|
||||||
|
|||||||
Reference in New Issue
Block a user