Skip to content

Commit 3a08594

Browse files
authored
Merge pull request #499 from atomantic/main
Release v2.10.0
2 parents ddab459 + 29c0240 commit 3a08594

144 files changed

Lines changed: 8489 additions & 617 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changelog/NEXT.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Released: TBD
66

77
TBD
88

9+
## Added
10+
911
## Changed
1012

11-
- **Universes table** — each row now shows a 48×48 thumbnail of the latest image from the universe's auto-managed media collection (the `Universe: <name>` bucket linked by `collection.universeId`). Rows without media fall back to a Globe placeholder; a broken file ref also degrades to the placeholder via `<img onError>`. Applies to both the desktop table and the mobile card layout.
13+
## Fixed
14+
15+
## Removed

.changelog/v2.10.0.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Release v2.10.0
2+
3+
Released: 2026-05-25
4+
5+
## Overview
6+
7+
A sync-hygiene and authoring release. Federated installs gain **duplicate
8+
resolution** and a **non-blocking edit-conflict journal** so two peers that
9+
independently create or edit the same Universe/Series converge cleanly without
10+
losing data, and **Series now belong to exactly one Universe** (enforced, with a
11+
migration for legacy orphans). The Writers Room adds a **length-and-craft Guide
12+
page**, and the **Review Loop reviewer chain is now configurable** — including
13+
local LM Studio / Ollama reviewers with no CLI in the loop. Worktree cleanup is
14+
re-enabled behind merge-verified safety gates, the autofixer and calendar sync
15+
now route through your configured AI provider, and the importer ceiling jumps
16+
from 200K to 5M characters. Plus a batch of mobile media-playback and layout
17+
fixes.
18+
19+
## Added
20+
21+
- **Writers Room Guide.** A new deep-linkable documentation page at `/writers-room/guide` (reachable via a "Guide" link in the Writers Room header and from `⌘K` / voice). It documents the literary length ladder — Microfiction, Flash Fiction, Standard Short Story, Novelette, plus Novella/Novel for context — with both word and character bands, a page-based book-length estimate table (200/300 pages), and craft principles grouped by structure, character, prose, and revision. The length targets and principles live in a single canonical data module (`client/src/lib/writingGuide.js`, with a `classifyByWordCount()` helper) so forthcoming editor analyses — including the planned **emotional-roadmap evaluator** that charts the reader's emotional journey beat by beat — read the same source the docs render from.
22+
- **Sync hygiene: duplicate resolution + a non-blocking edit-conflict journal.** Because two PortOS installs (or two machines federated as peers, or two copies of a `data/` folder synced through Google Drive/Dropbox) can each independently create a Universe or Series with the same name — and records are keyed by random id, not name — sync used to leave silent duplicates (e.g. two "Clandestiny" universes) and silently discard the loser whenever both sides edited the same record. Two new tabs under **Sharing** fix this:
23+
- **Duplicates** — finds same-named-but-different-id Universes (and Series, scoped within their universe) and offers to **Merge** (smart field-union: canon, categories, variations, influences, and render history are unioned; genuinely-conflicting fields are resolved field-by-field via a side-by-side diff; child series/issues and media collections are re-pointed to the survivor; the loser is tombstoned and propagated to peers) or **Rename** one to keep both. The same detection + merge is also surfaced **inline on the Universes page** — when two universes share a name a warning banner appears at the top of the list with the same Merge / Rename / Keep-both controls, so a duplicate that slipped past sync time can be resolved without leaving the page.
24+
- **Conflicts** — when a cross-install last-write-wins merge would overwrite a record both sides edited, the losing version is now archived to a local conflict journal instead of vanishing. Review the diff and **Restore mine**, **Merge** specific fields back, or **Discard**. Convergence is never blocked — peers still agree on the LWW winner; no edit is lost.
25+
26+
Both surfaces are deep-linkable (`/sharing/duplicates`, `/sharing/conflicts`) and reachable from ⌘K / voice.
27+
- **Series now belong to exactly one Universe (enforced).** Creating a series requires a universe, a linked series can no longer be un-linked (only moved to another universe), and deleting a universe that still has live series is blocked until they're moved or deleted. A migration adopts any legacy orphan series into a synthesized universe so existing installs satisfy the rule.
28+
- **Configurable Review Loop reviewer chain (with local-LLM reviewer support).** The Review Loop's default reviewer chain used to be hardcoded to GitHub Copilot; the new **AI Providers → Code Review Defaults** panel lets you set an ordered chain (any combination of `copilot`, `claude`, `gemini`, `codex`, `lmstudio`, `ollama`), the stop mode, and whether reviewers may apply fixes. The configured defaults seed ad-hoc tasks (Tasks page) and per-task-type schedules (CoS → Schedule) when nothing's explicitly pinned, and flow through to follow-up Review Loop agents that didn't carry their own reviewer metadata. Two new reviewer kinds — `lmstudio` and `ollama` — route the PR diff through a new `POST /api/code-review/local` endpoint that runs the model selected on the panel against the backend's OpenAI-compatible chat-completions API, so a local LM Studio or Ollama install can act as your code reviewer with no CLI in the loop.
29+
- **Use a different Chrome variant for the PortOS-managed browser.** PortOS now reads a `chromePath` (and `macAppBundle` on macOS) from `data/browser-config.json`, so you can point it at Chrome Canary, Chromium, Brave, Edge, or any Chromium-based browser — separating the automation surface from your daily-driver Chrome. Setup (`./setup.sh` / `setup.ps1`) and update (`./update.sh` / `./update.ps1`) now offer to install and configure Chrome Canary automatically: on macOS via `brew install --cask google-chrome@canary`, on Windows via `winget install Google.Chrome.Canary`. The prompt is interactive-only (CI / non-TTY runs skip silently), idempotent (won't re-prompt once configured), and supports `PORTOS_USE_CANARY=1` for headless opt-in. The Browser page's Config panel exposes both fields for after-the-fact edits.
30+
31+
## Changed
32+
33+
- **Merge-verified worktree cleanup — reaps merged branches/worktrees without ever risking in-progress work.** Periodic worktree cleanup was previously disabled because it could obliterate worktrees still in use by other agents or outside the CoS system. The cleanup now reaps a worktree **only** when both gates pass: the working tree is clean (auto-generated lockfile churn ignored) **and** the branch is fully merged into the default branch. Merge detection (`isBranchMergedInto`) covers both normal merges (ancestor check) and squash/rebase merges (synthesized-tree `git cherry` patch-id), so it can't be fooled by a squashed history. The daily `agent-data-cleanup` job now runs this safe reap across both CoS worktrees (`data/cos/worktrees/`) and the `/work` / `/claim` / superpowers trees under `.claude/worktrees/` — skipping locked or in-progress trees — then falls back to the existing orphan-integration pass for dead agents whose commits never landed. Because the two gates together mean "nothing pending and nothing to lose," the previous data-loss failure mode is structurally impossible. To make detection bulletproof going forward, the automated review-loop and `/do:pr` merge commands now prefer a true merge commit (`gh pr merge --merge`) over `--squash`, keeping each branch tip in the default branch's history.
34+
35+
- **Autofixer and Google Calendar sync now run through your configured AI provider.** Both previously hardcoded `claude -p`, ignoring whichever provider/model the rest of PortOS uses. They now resolve a configured CLI provider/model and route through a shared, dependency-light invocation path (`server/lib/cliProviderRun.js`) — the same per-CLI argv conventions (Codex `exec -`, Gemini stdin, Claude Code `-p -`) the main runner uses. New UI controls let you pick the provider/model per feature: a **Settings › Autofixer** tab, and a picker on the **Calendar › Config** tab (for Google MCP-sync accounts). Both default to Claude Code when unset, so existing installs are unchanged. The picker is restricted to enabled **CLI** providers — the autofixer must edit files and run pm2, and the calendar sync must call MCP tools, neither of which an API chat provider can do. *Migration note:* calendar MCP sync no longer honors the legacy `CLAUDE_PATH` env var — point the provider's command at your binary under **AI Providers** instead. The Claude-specific `--allowedTools` MCP flag is now passed only to Claude-family providers.
36+
37+
- **Importer source ceiling raised from 200K → 5M characters.** The Create › Importer page previously rejected pasted sources over 200,000 characters — far short of a real novel (a 250-page book runs ~400K chars; long novels reach several million). That cap was an over-conservative "v1" product limit, not a fundamental constraint: the schema layer already allowed 5MB, and modern large-context providers (Claude, Gemini) ingest a whole novel in a single call. The importer now matches the 5MB abuse-guard ceiling, so the only remaining limit is dynamic — the active provider's context window (the entire corpus still goes into one extraction call until chunked extraction lands). Sources that fit the ceiling but overflow the chosen model's window surface a provider error; pick a large-context provider or trim in the meantime. The pipeline's per-issue canon-extract truncation budget was decoupled (stays 200K) since a single issue's stage output is already hard-bounded at 400K and operates at a fundamentally smaller scale than whole-book ingest.
38+
39+
- **Dashboard Quick Image widget** — now exposes **resolution** and **negative prompt** options (collapsible) alongside the prompt, and renders the result inline: async backends stream the diffusion loading animation (spinner / step counter / latent preview) and resolve to the final image, mirroring the Universe asset slots via the shared `MediaJobThumb`. Sync (external) backends show the completed image directly. The "Edit" hand-off now carries the chosen size + negative prompt into the full Image Gen page.
40+
41+
- **Universes table** — each row now shows a 48×48 thumbnail of the latest image from the universe's auto-managed media collection (the `Universe: <name>` bucket linked by `collection.universeId`). Rows without media fall back to a Globe placeholder; a broken file ref also degrades to the placeholder via `<img onError>`. Applies to both the desktop table and the mobile card layout.
42+
43+
- **Writers Room auto-collapses the header + library when you open a work.** Tapping a library item now collapses both the page header and the library so the editor gets the full screen — most useful on mobile, where the library is an inline block stacked above the editor. The header shrinks to a slim bar that hosts the "show library" control (reachable on mobile, where there was previously no expand affordance), and the library always re-appears whenever no work is selected so you're never stranded.
44+
45+
- **AI Providers: billing-change warning on the headless Claude Code CLI provider.** The AI Providers page now shows an inline warning on the **Claude Code CLI** (`claude --print`) provider card: starting **June 15, 2026**, Anthropic clocks this non-interactive usage under API billing — consuming extra API credits instead of your Claude Code plan — so it should be avoided in favor of the interactive **Claude Code TUI** provider, which stays on the plan. The warning is scoped to plan-billed `claude` CLI providers and intentionally excludes Bedrock/Vertex-routed variants (those bill via the cloud account, not the plan).
46+
47+
## Fixed
48+
49+
- **Universe / Series merge modal: opaque background + AI-assisted field merge.** The merge dialog (Sharing → Duplicates and the inline Universes-page resolver) had a transparent panel that bled into the underlying list, making the conflict editor hard to read. It now uses the standard `port-card` card chrome with the same `max-h-[85vh] overflow-y-auto` scroll envelope as the rest of the app's modals. Alongside that, conflicting *text* fields (e.g. two divergent `starterPrompt`s on a duplicate "Clandestiny") gain a **Merge with AI** button: the configured AI provider receives both values plus the survivor's style context (logline / style notes / embrace influences) and returns one unified value per field, which the modal renders in an editable textarea and ships as `fieldOverrides`. Each row keeps the original Keep-survivor / Use-folded picker — switching back discards the AI value for that row. Non-text conflicts (numeric, object-shaped) skip AI and stay on the manual picker. New routes: `POST /api/universe-builder/merge/ai-resolve` and `POST /api/pipeline/series/merge/ai-resolve`.
50+
- **Ollama model installs no longer die on a transient "EOF".** Pulling a model from the Local LLMs page could fail with a bare `EOF` status and leave the model uninstalled. Ollama streams pull progress as NDJSON, and a transient network hiccup between Ollama and the model registry/CDN surfaces mid-stream as an `{"error":"EOF"}` frame (or the response read dropping outright). PortOS gave up on the first occurrence — even though the `ollama` CLI silently retries these and the pull is resumable (partial blobs are kept). `pullModel` now retries the transient/EOF class up to 3 attempts with a linear backoff, continuing from the partial download rather than restarting; non-transient errors (bad model name, missing manifest) still fail fast. The install banner shows a "retrying after network error" notice during the backoff instead of stalling.
51+
- **TUI agents no longer try (and fail) to `/quit` at the end of a run.** The spawned-agent completion workflow used to tell the agent to write its `.agent-done` sentinel and then run `/quit`. In current Claude Code, `/quit` is a UI command that can't be invoked programmatically — so the agent attempted `Skill(quit)`, hit an error, and printed a confusing "please run /quit yourself" message before idling. The prompt now treats the sentinel as the sole done signal (PortOS finalizes the run within 2s of seeing it and closes the session itself) and explicitly tells the agent not to run `/quit`.
52+
- **Tabbed sub-nav strips no longer drift vertically on mobile.** The horizontally-scrolling sub-nav tabs (Media Gen and every other page that uses the shared `TabPills` primitive) could be dragged diagonally/vertically on iOS, producing an uneven, wonky scroll. Both `TabPills` scroll containers now set `touch-action: pan-x` so touch gestures pan the strip horizontally while vertical drags pass through to normal page scrolling.
53+
- **Chief of Staff pane overflow** — content panel collapses to a single `flex-1 min-h-0 min-w-0 overflow-y-auto overflow-x-hidden` div so tall tab content scrolls inside the panel instead of expanding it. Event log rows in both `EventLog` and `TerminalCoSPanel` get `break-all` so long unbreakable tokens (URLs, hashes, paths) wrap inside the 320px sidebar instead of pushing the column wider visually.
54+
- **Videos now load in the Media Gen preview on mobile.** Both the preview-modal (lightbox) `<video>` and the Video Gen page's inline result preview autoplayed unmuted, which iOS/Android block outside a direct user gesture — so the clip never started and the area showed only black ("not loading"). They now play `muted` (autoplay-eligible everywhere; controls let you unmute) and render the thumbnail as a `poster`, so the frame is visible immediately even while the clip buffers.
55+
- **Video previews now play with sound on mobile.** Opening a video from Media Gen history previously stayed silent because the muted-autoplay baseline (needed so the clip loads at all on iOS/Android) was never upgraded to audible. The lightbox now unmutes and re-plays on open — the opening tap's user activation lets iOS grant audible playback — and falls back to muted if the browser blocks it, so the clip always runs and the controls can unmute it manually.
56+
57+
## Full Changelog
58+
59+
**Full Diff**: https://github.com/atomantic/PortOS/compare/v2.9.0...v2.10.0

0 commit comments

Comments
 (0)