diff --git a/apps/marketing/components/ui/carousel.tsx b/apps/marketing/components/ui/carousel.tsx
index c43b8262..cd7c9bec 100644
--- a/apps/marketing/components/ui/carousel.tsx
+++ b/apps/marketing/components/ui/carousel.tsx
@@ -102,19 +102,22 @@ function Carousel({
};
}, [api, onSelect]);
+ const contextValue = React.useMemo(
+ () => ({
+ carouselRef,
+ api: api,
+ opts,
+ orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
+ scrollPrev,
+ scrollNext,
+ canScrollPrev,
+ canScrollNext,
+ }),
+ [api, canScrollNext, canScrollPrev, carouselRef, orientation, opts, scrollNext, scrollPrev],
+ );
+
return (
-
+
({ config }), [config]);
return (
-
+
- {errors.map((error, index) => error?.message &&
{error.message})}
+ {errors
+ .filter((error): error is { message: string } => typeof error?.message === "string")
+ .map((error) => (
+
{error.message}
+ ))}
);
}, [children, errors]);
diff --git a/apps/marketing/components/ui/form.tsx b/apps/marketing/components/ui/form.tsx
index c2c8525d..4c908e57 100644
--- a/apps/marketing/components/ui/form.tsx
+++ b/apps/marketing/components/ui/form.tsx
@@ -33,8 +33,10 @@ const FormField = <
>({
...props
}: ControllerProps
) => {
+ const fieldContextValue = React.useMemo(() => ({ name: props.name }), [props.name]);
+
return (
-
+
);
@@ -71,9 +73,10 @@ const FormItemContext = React.createContext({} as FormItem
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
const id = React.useId();
+ const itemContextValue = React.useMemo(() => ({ id }), [id]);
return (
-
+
);
diff --git a/apps/marketing/components/ui/toggle-group.tsx b/apps/marketing/components/ui/toggle-group.tsx
index c93b7ae6..22476099 100644
--- a/apps/marketing/components/ui/toggle-group.tsx
+++ b/apps/marketing/components/ui/toggle-group.tsx
@@ -19,6 +19,7 @@ function ToggleGroup({
children,
...props
}: React.ComponentProps & VariantProps) {
+ const contextValue = React.useMemo(() => ({ variant, size }), [size, variant]);
return (
-
- {children}
-
+ {children}
);
}
diff --git a/apps/mobile/ios/App/CapApp-SPM/README.md b/apps/mobile/ios/App/CapApp-SPM/README.md
index 03964db9..24f5e2ff 100644
--- a/apps/mobile/ios/App/CapApp-SPM/README.md
+++ b/apps/mobile/ios/App/CapApp-SPM/README.md
@@ -1,5 +1,88 @@
-# CapApp-SPM
+# CapApp-SPM (Capacitor Swift Package)
-This package is used to host SPM dependencies for your Capacitor project
+This directory is a generated Swift Package integration point for the native mobile
+shell. It is managed by Capacitor tooling and should be treated as a reproducible
+native dependency boundary.
-Do not modify the contents of it or there may be unintended consequences.
+## 1) What this package is
+
+- Package name: `CapApp-SPM`
+- Product: `CapApp-SPM`
+- Target(s): `CapApp-SPM`
+- Platforms: iOS 15+
+
+Primary goals:
+
+- provide a stable dependency entrypoint into the native layer,
+- keep native package metadata separate from app JS source trees,
+- avoid hand-editing generated package internals.
+
+## 2) Files here
+
+- `Package.swift`
+- `Sources/` (package targets/source)
+
+The current `Package.swift` includes local package links into Bun-managed `node_modules`
+paths, which is why it is intentionally generated rather than hand-maintained.
+
+## 3) Install and wire into an Xcode project
+
+1. In Xcode, open your `.xcworkspace` / `.xcodeproj`.
+2. Go to **File → Add Packages…**.
+3. Add by local path:
+ - `apps/mobile/ios/App/CapApp-SPM`
+4. Add the `CapApp-SPM` product to the appropriate app target.
+
+### Target mapping quick reference
+
+- Package: `CapApp-SPM`
+- Product: `CapApp-SPM`
+- Usually added to the main app target and the extensions that depend on Capacitor modules.
+
+## 4) Regenerating after dependency changes
+
+When web/native dependency versions change in `apps/mobile`, regenerate this package
+using your normal Capacitor sync workflow (outside this folder) and commit generated
+updates if required.
+
+Recommended sequence:
+
+```bash
+cd apps/mobile
+bun run sync
+cd ios/App
+# regenerate native project references through your normal mobile tooling
+```
+
+Then reopen Xcode and run package resolve.
+
+## 5) Build and debug checks
+
+### Before a build
+
+- Open the workspace cleanly.
+- Remove stale derived data if dependency resolution changed.
+- Validate package resolve on local machine first.
+
+### Common failure states
+
+- **Package not found / cannot resolve product**
+ - verify path points to `CapApp-SPM/` and Xcode selected the correct package product.
+- **Build fails with module resolution issues**
+ - clean build folder and re-resolve package dependencies.
+- **Code signing breaks after package edits**
+ - re-check provisioning profile and signing identity for native targets.
+
+## 6) CI/release implications
+
+For release and package integrity:
+
+- keep native package updates deterministic,
+- avoid ad-hoc local path edits that are not reflected in source control,
+- verify any package-manager-driven changes are generated and committed with the
+ corresponding release or mobile update.
+
+## 7) Reference
+
+This package is separate from release docs in [`docs/releases/README.md`](/Users/buns/.okcode/worktrees/okcode/okcode-ddc899c0/docs/releases/README.md),
+which controls desktop artifact publication and GitHub release behavior.
diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx
index 70a23e26..98deb819 100644
--- a/apps/web/src/components/DiffPanel.tsx
+++ b/apps/web/src/components/DiffPanel.tsx
@@ -418,9 +418,11 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
() => renderableFiles.map((fileDiff) => resolveFileDiffPath(fileDiff)),
[renderableFiles],
);
- const activeReviewState = patchReviewSelectionKey
- ? (reviewStateBySelectionKey[patchReviewSelectionKey] ?? {})
- : {};
+ const activeReviewState = useMemo(() => {
+ return patchReviewSelectionKey
+ ? (reviewStateBySelectionKey[patchReviewSelectionKey] ?? {})
+ : {};
+ }, [patchReviewSelectionKey, reviewStateBySelectionKey]);
const acceptedFileCount = useMemo(
() =>
renderableFilePaths.reduce(
diff --git a/apps/web/src/components/pr-review/RawPatchViewer.tsx b/apps/web/src/components/pr-review/RawPatchViewer.tsx
index 7d6d1be8..c5aad318 100644
--- a/apps/web/src/components/pr-review/RawPatchViewer.tsx
+++ b/apps/web/src/components/pr-review/RawPatchViewer.tsx
@@ -446,14 +446,27 @@ export function RawPatchViewer({ text, reason }: { text: string; reason: string
) : (
/* Fallback: no file structure detected, render all lines with highlighting */
- {lines.map((line, index) => (
-
- ))}
+ {lines
+ .reduce
>(
+ (items, line, lineIndex) => {
+ const lineNumber = lineIndex + 1;
+ items.push({
+ line,
+ lineNumber,
+ key: `${lineNumber}-${line}`,
+ });
+ return items;
+ },
+ [],
+ )
+ .map(({ key, line, lineNumber }) => (
+
+ ))}
)}
diff --git a/apps/web/src/store.ts b/apps/web/src/store.ts
index ea105142..06f3b327 100644
--- a/apps/web/src/store.ts
+++ b/apps/web/src/store.ts
@@ -260,26 +260,28 @@ export function syncServerReadModel(state: AppState, readModel: OrchestrationRea
}
: null,
messages: thread.messages.map((message) => {
- const attachments = message.attachments?.map((attachment) => ({
- ...(attachment.type === "image"
- ? {
- type: "image" as const,
- id: attachment.id,
- name: attachment.name,
- mimeType: attachment.mimeType,
- sizeBytes: attachment.sizeBytes,
- url: toAttachmentPreviewUrl(attachmentPreviewRoutePath(attachment.id)),
- previewUrl: toAttachmentPreviewUrl(attachmentPreviewRoutePath(attachment.id)),
- }
- : {
- type: "file" as const,
- id: attachment.id,
- name: attachment.name,
- mimeType: attachment.mimeType,
- sizeBytes: attachment.sizeBytes,
- url: toAttachmentPreviewUrl(attachmentPreviewRoutePath(attachment.id)),
- }),
- }));
+ const attachments = message.attachments?.map((attachment) => {
+ const baseAttachment = {
+ id: attachment.id,
+ name: attachment.name,
+ mimeType: attachment.mimeType,
+ sizeBytes: attachment.sizeBytes,
+ url: toAttachmentPreviewUrl(attachmentPreviewRoutePath(attachment.id)),
+ };
+
+ if (attachment.type === "image") {
+ return {
+ type: "image" as const,
+ previewUrl: toAttachmentPreviewUrl(attachmentPreviewRoutePath(attachment.id)),
+ ...baseAttachment,
+ };
+ }
+
+ return {
+ type: "file" as const,
+ ...baseAttachment,
+ };
+ });
const normalizedMessage: ChatMessage = {
id: message.id,
role: message.role,
diff --git a/docs/releases/README.md b/docs/releases/README.md
index 5d00dd6f..d01fafe2 100644
--- a/docs/releases/README.md
+++ b/docs/releases/README.md
@@ -1,25 +1,180 @@
-# Release notes
-
-Human-readable notes and asset inventories for tagged releases.
-
-| Version | Notes | Assets |
-| -------------------- | ------------------------------------------------------------ | ----------------------------- |
-| [0.0.12](v0.0.12.md) | Release 0.0.12 with latest preflight fixes and Apple Silicon | [manifest](v0.0.12/assets.md) |
-| [0.0.12](v0.0.12.md) | Release 0.0.12 with latest preflight fixes and Apple Silicon | [manifest](v0.0.12/assets.md) |
-| [0.0.11](v0.0.11.md) | Release 0.0.11 with Apple Silicon-only macOS default asset m | [manifest](v0.0.11/assets.md) |
-| [0.0.11](v0.0.11.md) | Release 0.0.11 with Apple Silicon-only macOS default asset m | [manifest](v0.0.11/assets.md) |
-| [0.0.11](v0.0.11.md) | Release 0.0.11 with Apple Silicon-only macOS default asset m | [manifest](v0.0.11/assets.md) |
-| [0.0.10](v0.0.10.md) | Release 0.0.10 with desktop packaging matrix updated to Appl | [manifest](v0.0.10/assets.md) |
-| [0.0.9](v0.0.9.md) | Review fixes, skill dialog polish, and marketing refresh | [manifest](v0.0.9/assets.md) |
-| [0.0.8](v0.0.8.md) | Skills, localization, attachment flow, and UI hardening | [manifest](v0.0.8/assets.md) |
-| [0.0.7](v0.0.7.md) | Navigation, clone flow, plan checklists, preview presets | [manifest](v0.0.7/assets.md) |
-| [0.0.6](v0.0.6.md) | PR + preview polish, env persistence, search, landing page | [manifest](v0.0.6/assets.md) |
-| [0.0.5](v0.0.5.md) | Git workflows, PR review, mobile shell | [manifest](v0.0.5/assets.md) |
-| [0.0.4](v0.0.4.md) | PR review, desktop preview, release tooling | [manifest](v0.0.4/assets.md) |
-| [0.0.3](v0.0.3.md) | Onboarding, code viewer, PRs | [manifest](v0.0.3/assets.md) |
-| [0.0.2](v0.0.2.md) | Patch release | [manifest](v0.0.2/assets.md) |
-| [0.0.1](v0.0.1.md) | First public tag | [manifest](v0.0.1/assets.md) |
-
-The repository root [CHANGELOG.md](../../CHANGELOG.md) summarizes versions in Keep a Changelog form.
-
-Each release workflow also uploads **`okcode-CHANGELOG.md`**, **`okcode-RELEASE-NOTES.md`**, and **`okcode-ASSETS-MANIFEST.md`** to the GitHub Release (see [.github/workflows/release.yml](../../.github/workflows/release.yml)).
+# Release Playbook (Desktop + site assets)
+
+This document defines how to produce, validate, and publish official releases from
+`main` with the current GitHub Actions pipeline.
+
+## 1) Trigger modes
+
+### 1.1 Tag-driven release
+
+- Push a tag in the form `v` (for example `v0.0.10`).
+- `preflight` resolves `version` from the tag and runs required checks.
+
+### 1.2 Manual dispatch
+
+- Uses `workflow_dispatch` inputs:
+ - `version` (required; examples: `0.0.10` or `0.0.10-rc.1`)
+ - `mac_arm64_only` (default: `true`)
+- Useful for controlled smoke/release dry-runs.
+
+## 2) Current CI stages
+
+### 2.1 `configure`
+
+- Selects release matrix.
+- macOS default is Apple Silicon only when `mac_arm64_only=true`:
+ - `macos-14` + `mac` + `dmg` + `arm64`
+- Full matrix (default for manual off switch) includes:
+ - `macos-14` + `arm64`
+ - `ubuntu-24.04` + `x64` AppImage
+ - `windows-2022` + `x64` NSIS
+
+### 2.2 `preflight`
+
+Executed on Ubuntu and includes:
+
+- dependency install (`bun install --frozen-lockfile`)
+- `bun run lint`
+- `bun run typecheck`
+- `bun run test`
+- `bun run release:smoke`
+
+Any failure here blocks build and publish jobs.
+
+### 2.3 `build` (matrixed)
+
+For each target:
+
+1. Checkout release ref.
+2. Install dependencies.
+3. Align package versions (`scripts/update-release-package-versions.ts`).
+4. Build desktop artifact with `bun run dist:desktop:artifact`.
+5. Collect release outputs (`release/*.dmg`, `release/*.AppImage`, `release/*.exe`, blockmaps, `latest*.yml`).
+6. Upload per-platform artifact bundle.
+
+### 2.4 `release`
+
+- downloads all uploaded build artifacts,
+- requires release docs to exist:
+ - `docs/releases/v.md`
+ - `docs/releases/v/assets.md`
+- stages these as release body artifacts, then publishes release via `softprops/action-gh-release`.
+
+### 2.5 `finalize`
+
+- validates `RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY`,
+- creates GitHub App token and bot identity,
+- re-runs version bump flow and updates lockfile if needed,
+- pushes version updates back to `main`.
+
+## 2.6 Operational matrix (release commands)
+
+| Trigger | Command/target | Expected output | Failure signal | Recovery |
+| -------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------- |
+| Tag-driven release | push `v` | `configure` → `preflight` → matrix `build` → `release` → `finalize` all succeed | missing/failed preflight gate | fix local violations, update commit, re-tag if needed |
+| Manual release | workflow_dispatch with `version` | same stages as tag-driven | matrix wrong for architecture targets | set `mac_arm64_only` explicitly |
+| Format regression | `bun run fmt` | files rewritten | `bun run fmt:check` fails in `preflight` | commit formatter output |
+| Type/contract regressions | `bun run typecheck` | clean compile output | `TS` errors in preflight | update contracts and both producer/consumer sides together |
+| Release content regression | `git show` on `docs/releases/v.md` + `docs/releases/v/assets.md` | both files present and committed | `release` job fails missing files | add both files and rerun release |
+| Asset mismatch | build logs / upload-artifact | expected platform assets in `release-publish` | `if-no-files-found: error` or missing files | verify `release/` output names and rerun build |
+
+## 3) Required release assets
+
+For each release version `X.Y.Z`, commit:
+
+- `docs/releases/vX.Y.Z.md` (release notes body)
+- `docs/releases/vX.Y.Z/assets.md` (asset manifest used in release publish step)
+
+The publish step copies both into `release-assets` and uses them for release artifacts.
+
+## 4) Standard release checklist
+
+1. Start from `main`:
+
+ ```bash
+ git checkout main
+ git pull --ff-only
+ ```
+
+2. Ensure current format/lint/type/test gates are green locally:
+
+ ```bash
+ bun run fmt
+ bun run fmt:check
+ bun run lint
+ bun run typecheck
+ bun run test
+ ```
+
+3. Prepare release notes and asset manifest:
+
+ ```bash
+ mkdir -p docs/releases/v0.0.10
+ touch docs/releases/v0.0.10.md
+ touch docs/releases/v0.0.10/assets.md
+ ```
+
+4. Commit release notes updates.
+
+5. Cut and push tag:
+
+ ```bash
+ git tag -a v0.0.10 -m "Release 0.0.10"
+ git push origin v0.0.10
+ ```
+
+6. Watch workflow progress:
+ - `Release Desktop / preflight`
+ - `Release Desktop / build (...)`
+ - `Release Desktop / release`
+ - `Release Desktop / finalize`
+
+7. Verify final output includes:
+ - expected platform artifacts,
+ - release body and assets manifest,
+ - optional signing/ notarization status,
+ - version bumps committed by finalize job (if any).
+
+## 5) Failure triage
+
+### 5.1 Preflight failures
+
+- **Lint/typecheck/test failure**
+ - reproduce locally with the same command from section 4.
+ - fix at source, rerun gate, then re-run tag/release path.
+
+- **Release smoke failure**
+ - inspect `bun run release:smoke` logs and confirm expected runtime prerequisites.
+
+### 5.2 Build failures
+
+- **Wrong architecture in matrix**
+ - confirm intended architecture matrix by checking `configure` job output.
+ - for Apple Silicon-only runs, ensure `mac_arm64_only` is true.
+- **Signing/notarization failure**
+ - confirm required GitHub secrets are present in repository settings.
+- **No files found during upload**
+ - check artifact output paths (`release/*.dmg`, `release/*.AppImage`, etc.)
+ - verify build command produced files in `release/`.
+
+### 5.3 Release staging failures
+
+- **Missing `docs/releases/vX.md` or `docs/releases/vX/assets.md`**
+ - create both files before triggering release.
+- **`softprops` failures (`fail_on_unmatched_files`)**
+ - ensure all build artifacts + docs copies exist in `release-assets`.
+
+## 6) Release policy and best practices
+
+- Prefer single-purpose PRs for release content vs release infra changes.
+- Keep release notes and manifest content deterministic for reproducibility.
+- Prefer appending bugfixes/notes to the current patch version before bumping major/minor.
+- Keep signing certificates and identity changes centralized in CI secrets.
+- Use the changelog + release notes as a single source for user-visible changes.
+
+## 7) Optional manual triggers
+
+- For staging-only release runs, use `workflow_dispatch` with explicit version and
+ `mac_arm64_only` preference.
+- For normal full desktop releases, set `mac_arm64_only=false` only when x64 macOS,
+ Linux, and Windows artifacts are all required.