Skip to content

Release v2.26.0#2084

Merged
atomantic merged 101 commits into
releasefrom
main
Jul 3, 2026
Merged

Release v2.26.0#2084
atomantic merged 101 commits into
releasefrom
main

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Release v2.26.0mainrelease.

98 commits since v2.25.0 (8 feat, 33 fix, no breaking) → minor bump.

Highlights

  • Sketch & Annotation Canvas (phase 1) — draw/erase over any generated image, save vector strokes, export a flattened PNG; reachable from ⌘K and voice.
  • Tribe relationship care — auto-logged touchpoints from synced calendar/messages, a "who needs care" Proactive Alert + dashboard widget, single-sourced cadence rules.
  • Two new dashboard widgets + an interactive Integration Flows architecture map.
  • Catalog ingredient media now accepts file uploads and voice memos (transcribed, federated).
  • Workspace switching from ⌘K/voice, with CoS auto-snapshotting the workspace it leaves.
  • Security — closed two DNS-rebinding TOCTOU gaps (RSS feed fetches; catalog URL ingest via Chrome/CDP) by pinning to the SSRF-vetted IP.
  • Deep-linkable selections across authors, music, sharing buckets, prompt stages/vars, JIRA reports; explicit inline delete confirmations.
  • Accessibility sweep — ~260 config-form label/input pairs across ~80 files now properly associated.
  • Reliability — CoS --swarm orchestrators no longer reaped mid-merge; POST reminder timezone false-nudge fixed; v2.25.0 review follow-ups.

Full details: .changelog/v2.26.0.md

Full Diff: v2.25.0...v2.26.0

Release-gate review

Deep local review of the full diff (5 parallel domain reviewers) surfaced 4 findings; 3 fixed in this PR (PromptManager edit-loss, message-touchpoint local-day dedupe, tribe-care migration seed), 1 deferred as fail-safe over-refusal (#2083). CI (Postgres-backed test suites + build + lint) is the merge gate.

https://claude.ai/code/session_01CXDnRojmjgdxYwwMsbMNaM

atomantic and others added 30 commits July 2, 2026 00:44
…#2042)

Migration 148 backfilled cd-treatment.md via anchor-based inserts. Installs
seeded before the imageStrength scene knob (older pre-#1808 template) got the
Cast list but missed the per-scene cast field, and since 148 is already applied
it never re-runs. Migration 155 finishes the job with the canonical hash-driven
prompt-replace, upgrading the strand (md5 95b76856) and both pristine pre-#1808
shipped versions to the current shipped reference while leaving customized
copies untouched. Folds cd-treatment.md into buildPromptDriftTables so
setup-data.js drift classification stays correct.

Claude-Session: https://claude.ai/code/session_01GmWqYTkinJmpC94zkpMQ8b
Replace the non-discoverable same-button "click again to confirm" delete
pattern (and the toast-armed replace-extract buttons) with explicit inline
confirm/cancel affordances across Rounds, Universes, Pipeline, Sharing,
ArcCanvas, Writers Room LibraryPane, Ask, Universe Builder, and the Audio /
Storyboards / Comic Pages pipeline stages. Enlarge LibraryPane delete/add
hit targets to >=44px for touch.

Claude-Session: https://claude.ai/code/session_01GmWqYTkinJmpC94zkpMQ8b
Address codex review: the confirm path no longer re-checked whether the
target conversation was mid-stream, so a row armed while idle could delete
an active streaming conversation once confirmed. Restore the guard in the
confirm callback.

Claude-Session: https://claude.ai/code/session_01GmWqYTkinJmpC94zkpMQ8b
fix: migrate two-click-arm delete confirms to InlineConfirmRow (#2024)
fix: add stage-prompt migration for cd-treatment.md #1808 cast section (#2042)
…2027)

Add client/src/components/ui/FormField.jsx — an accessible config-form field
wrapper that generates a stable id via useId() and wires <label htmlFor> to
the first child input/select/textarea's id (the repo's htmlFor/id-pairing
convention). Styling stays caller-owned (className/labelClassName) so migrating
an existing field is a lossless swap.

Migrate the confirmed sibling-label config forms onto it: AI Providers, DataDog,
feature-agent config, message & calendar account setup, the scheduled-task
global/pipeline-stage provider-model pickers, the agent world/schedule tabs, and
the MeatSpace nicotine + POST drill config. Wrapping labels and radio/toggle/
group labels left untouched.

Remaining forms tracked in #2051.

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
…mpt Manager) (#2025)

Move the open record into the URL for the master-detail views that kept it in
local state, so every selection is shareable, bookmarkable, reload-safe, and
reachable from ⌘K / voice / the back button:

- Authors: /authors/:authorId (:authorId also captures the `new` create sentinel)
- Music tabs: /music/:tab/:id (artists, albums, tracks)
- Sharing buckets: /sharing/:section/:bucketId
- Prompt Manager: ?stage= / ?var= search params
- JIRA Reports: ?reportApp=&reportDate= (appId+date is the stable report id)

Selection handlers navigate to the id'd route; deletes/clears navigate back to
the index; a stale/deleted id renders a not-found fallback. OpenClaw's session
picker is documented as an intentional exemption (ephemeral runtime state).

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
…holds

Review follow-up (#2027): when a caller passed a child with its own id,
the label's htmlFor still used the generated id, pointing at nothing and
breaking the very association the wrapper exists to make. Reuse the child's
explicit id for the label when present; inject the generated id otherwise.

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
…ds (#2025)

Review-gate follow-ups:
- Sharing: distinguish "no bucket selected" from a stale/removed bucketId with an
  explicit "could not be found" fallback (matches the other master-detail views).
- JiraReports: clear selectedReport when the URL-named report fails to resolve
  (deleted / bad deep link) instead of stranding the previously-open report.

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
fix: associate config form labels with inputs (#2027)
#2025)

Codex review: record selection in Prompt Manager (?stage/?var) and JIRA Reports
(?reportApp/?reportDate) used history-replace, so the back button skipped past
prior selections — inconsistent with Authors/Music/Sharing (which push). Selection
is now a push; the tab toggle and auto-select canonicalization keep replace.

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
Claude review: the hydration effect's idle/not-found branch skipped
clearGeneration()/setConfirmDelete() (and resetTrackViewState for tracks), so
navigating from an editing record to the index or a stale id could let a stray
render land on the prior record. Restructured all four effects (Authors, Artists,
Albums, Tracks) to run the resets for every selection change via a normalized
selectionKey guard.

Claude-Session: https://claude.ai/code/session_016N3afFZfp7gFiXLM4LEjkp
refactor: consolidate duplicate duration formatters onto utils/formatters (#2028)
fix: replace full-screen Loading text with skeletons on DataDog/Jira/GitHub pages (#2029)
…2026)

Add { silent: true } at ~50 call sites that already show their own error
toast, and add trailing options={} passthrough to the ~40 api wrappers they
call so the built-in apiCore toast can actually be suppressed. Update
BucketBoard test assertion for the new createBrainBucket signature.

Claude-Session: https://claude.ai/code/session_01M6xoZk7UnKz8e9PnPWoLRZ
fix: two-column Story Builder index layout above lg breakpoint (#2030)
fix: deep-link master-detail selections (#2025)
fix: eliminate double error toasts (silent passthrough) (#2026)
Add a workspace_switch voice tool (server/services/voice/tools/workspace.js)
that snapshots the workspace being left, then restores a named project's saved
context (git branch, in-repo shell sessions, scoped tasks) via the existing
workspaceContext service. Whitelisted in PALETTE_ACTIONS so it surfaces in ⌘K.

Add snapshotOnRepoSwitch() to workspaceContext.js — a shared "current workspace"
tracker that saves the previous app's context when work moves to a different
repo. Wired into agentLifecycle.spawnAgentForTask so a CoS coding-agent dispatch
against a different app auto-snapshots silently (snapshot-only, no restore, no
LLM calls), defensively so a missing current context is a no-op.

Covered by palette.test.js (manifest hydration + dispatch + unknown-workspace)
and workspaceContext.test.js (switch/no-op/same-repo/null-normalization).

Claude-Session: https://claude.ai/code/session_01DKGYJ2LdM91DRvNKj8Y31B
…ify tracker atomicity (#2035)

Codex review: the description advertised "take me back to PortOS" which
WORKSPACE_INTENT_RE (requires workspace/project/context) wouldn't surface to the
voice LLM — reworded to "take me back to the PortOS workspace". Added a comment
noting the tracker's read→update is synchronous (atomic per call), so concurrent
spawns can't tear it.

Claude-Session: https://claude.ai/code/session_01DKGYJ2LdM91DRvNKj8Y31B
atomantic and others added 28 commits July 2, 2026 11:48
A JSON-driven visual map of PortOS integrations: client/public/flows.html
renders ~60 components in layered columns and traces 10 end-to-end flows
(create image, start a story, voice command, CoS agent run, PM2, shell,
backup, peer sync, self-update, scheduled tasks) with per-hop transport
and payload annotations. Driven entirely by client/public/flows.json;
an integrity test keeps node references and file paths honest. Hosted
at /devtools/flows (iframe wrapper, deep-linkable ?flow= param) with an
open-full-screen escape to the standalone doc.
…ocket event names in flows doc

Same-column hops (e.g. Start a Story → Story Builder) anchored source-left/
target-right and swept across the column's node boxes; they now loop out the
right side. Two flows.json hops named events that never cross the wire
(jobs:executed, sync:applied, peers:updated, tasks:changed) — relabeled to
the real client-facing events (cos:tasks:*:changed, sharing:manifest-processed,
sharing:inbox-updated) so grepping a hop label always finds real code.
Client:
- Shell: guard the restart button's deferred startSession() with a
  generation counter so a session switch within the 1s window aborts it.
- MusicVideo: snapshot the board before optimistic auto-arrange and revert
  on a mid-loop PATCH failure.
- VideoTimelineEditor: re-derive selection from the stable clipId after
  refresh so a CONFLICT reload doesn't collapse the inspector.
- MemoryPractice: move chunk-/sequence-exhausted setState + async POST out
  of the render body into a terminal-transition effect.

Server:
- meatspacePostDrillCache: guard triggerReplenish's fire-and-forget
  replenishType promise (unhandled-rejection on disk-write failure).
- postValidation: accept memoryItemId in drillTypeConfigSchema so /post/drill
  threads it to generateMemoryDrill instead of stripping it.
- musicVideo/projectsLogic: regress status to draft on track change since the
  cleared audioAnalysis must be redone.
- sharing/peerSyncReceive: remove ~20 dead imports from the #1830 split.

Closes #2047

Claude-Session: https://claude.ai/code/session_01M1aQgtbUZZ9WmE9V6XyT8Q
…#2038)

Replace the two "coming soon" stubs on the CatalogIngredient Media panel with
working file-upload and voice-memo capture.

- New POST /catalog/ingredients/:id/media/upload persists a dropped/picked file
  into the matching federating library dir (image->data/images as PNG via the
  existing gallery saver, audio->data/audio, video->data/videos) and attaches it
  via the existing media-refs seam. Server picks the media kind from the MIME;
  documents are rejected (no federating dir -> would break peer references).
- New POST /catalog/ingredients/:id/media/voice transcribes a recorded WAV via
  the existing Whisper pipeline and attaches a kind:'audio' row with the
  transcript in caption (persist after transcription; no orphan on failure).
- Both routes Zod-validated (catalogMediaFileUploadSchema / catalogMediaVoiceMemoSchema).
- Client: drag-drop of real files, Upload-file picker, Record-memo button
  (reusing startMemoRecording), inline audio/video players in MediaTile, and the
  two "coming soon" toasts removed. Federation is automatic (catalogSync ships
  the reference, peerMediaLibrarySync ships the bytes).
- Unit tests for the new service (mime classification + upload/voice orchestration
  with injected deps).

Claude-Session: https://claude.ai/code/session_01M1aQgtbUZZ9WmE9V6XyT8Q
fix: v2.25.0 release-review robustness follow-ups (#2047)
fix: prevent CoS swarm agents being reaped mid-merge-queue (#2074)
The doc never declared color-scheme, so browsers painted default light
chunky scrollbars over the dark layout. Declare color-scheme: dark and
style scroll containers with thin border-colored thumbs on transparent
tracks (standard scrollbar-* props plus ::-webkit-scrollbar for Safari).
feat: implement catalog ingredient file upload and voice-memo capture (#2038)
Follow-up to #2027. Migrate ~130 sibling <label>+input config-form fields
across ~40 client files onto the shared FormField accessibility wrapper so
each label's htmlFor is wired to its control id (click-to-focus + screen
reader association). Accessibility only — Tailwind classes preserved verbatim;
wrapping labels, radio/toggle group labels, and custom controls that don't
forward id to a DOM input were deliberately left alone.

Closes #2051

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
docs([issue-2073]): rewrite four shipped feature docs from plan-tense to verified current-state
New GET/PUT /api/media/sketches/:key persists freehand annotation strokes
(JSON vectors + flattened PNG) as a per-image file sidecar under
data/media-sketches/, Zod-validated and image-only. Phase 1 of #2036.

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
Pure stroke model/renderer (client/src/lib/sketchCanvas.js), a pointer-event
<canvas> overlay component (mouse/pen/touch, draw/erase/undo, color+size), and
the deep-linkable /media/annotate/:mediaKey page with save + client-side PNG
export. Phase 1 of #2036.

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
Adds the /media/annotate route (+ bare index), NAV_COMMANDS entry, MediaCard
"Annotate" action wired from Media History and Collection detail, the client
API wrappers, and a changelog entry. Phase 1 of #2036.

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
…2036)

Review fixes: (1) track a single active pointerId so a second finger/palm
can't hijack the in-progress stroke on touch; (2) reset strokes/dims/imageError
synchronously when the :mediaKey route param changes, since React Router reuses
the component instance across param changes (no remount).

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
Review fix (codex): a re-save carrying strokes but no PNG left the prior
<id>.png on disk while the JSON recorded hasPng:false, so getSketchPng/`/png`
kept streaming the stale export. Now unlink the PNG in that branch; regression
test covers save-with-PNG then save-without.

Claude-Session: https://claude.ai/code/session_01KSyAbxEjURcLPhugG8MZCw
refactor: sweep remaining config forms onto shared FormField (#2051)
feat: Sketch & Annotation Canvas — phase 1 (annotate media images) (#2036)
The daysAgo() helper built its YYYY-MM-DD string via toISOString() (UTC),
but daysSinceDate() parses that string in local time — so in the evening
of a UTC-behind timezone the date rolled forward a day, making the
elapsed-day math off-by-one and failing the overdue/soon boundary
assertions. Build the string from local calendar fields so the suite is
genuinely timezone-independent (it already passed in CI's UTC runner).

Claude-Session: https://claude.ai/code/session_01CXDnRojmjgdxYwwMsbMNaM
…int local day, tribe-care migration seed

Release-gate review findings on the v2.26.0 diff:

- PromptManager variable editor re-hydrated from the server copy on any
  `variables` refresh (Reload, or deleting a different variable), silently
  discarding in-progress edits for the open key. Added a `varHydratedRef`
  guard so a list refresh only hydrates a newly-selected key — matching the
  hydratedRef pattern the Authors/Artists editors already use.

- Message-touchpoint dedupe derived its per-thread "calendar day" from the
  UTC day while the rest of Tribe cadence uses the local day, so a thread
  straddling UTC midnight in the evening double-logged. Derive the day in the
  user's local timezone (getUserTimezone + getLocalParts) and add a
  regression test.

- Migration 156 seeded `feeds` + `meatspace-streak` but not `tribe-care`,
  which this release also added to the built-in Everything layout — so
  upgraders who use Tribe would never receive the Tribe Care widget. Added
  the third seed + test coverage.

Deferred to #2083: catalog URL-ingest over-refuses public pages embedding
slow/cached iframes (over-refusal, fails safe; SSRF-gate change needs the
live CDP path to verify).

Claude-Session: https://claude.ai/code/session_01CXDnRojmjgdxYwwMsbMNaM
@atomantic atomantic merged commit 81e2340 into release Jul 3, 2026
4 checks passed
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