Skip to content

Mobile chat tooling pass: expanded tools, reliable navigation, and token/context optimization - #82

Merged
ddutchie merged 15 commits into
mainfrom
ddutchie/mobiletoolingpass
Jul 11, 2026
Merged

ddutchie merged 15 commits into
mainfrom
ddutchie/mobiletoolingpass

Conversation

@ddutchie

@ddutchie ddutchie commented Jul 11, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

A focused pass on the AI/agent layer plus three mobile chat fixes. It (1) expands the mobile chat tool set to full note/task CRUD, (2) makes chat→note/card navigation reliable and collision-proof, and (3) cuts the tokens spent per chat turn across three fronts — system prompts, tool schemas, and tool responses — each validated by an opt-in, live tool-selection/arg-correctness experiment (no secrets in source). Net effect: the assistant can do more, links land on the right item, and a single query no longer balloons the context window.

Type of change

  • Bug fix
  • New feature
  • Refactor / code quality
  • Docs / changelog
  • Tests

Summary of changes

Mobile chat capability + fixes

  • Expanded mobile chat tools 12 → 19: get_task, update_task (incl. column move), rename_note (with wikilink fixups), bulk_move_notes, list_folders, delete_note, delete_task.
  • Reliable chat → note/card navigation: tool-result chips are tappable (open by id); [[wikilinks]] resolve to note and card ids, baked at preprocess time.
  • Collision-proof links: the agent now links as [[id]] (rendered as the note's title), so a link can't open the wrong item when two share a title; [[Title]] still works and title lookup is now deterministic.
  • Fixed the attachment button drifting off-centre when the keyboard opens / input grows.
  • Fixed the keyboard occasionally getting stuck open (now dismisses via the keyboard-controller API on send + blur).

Token / context optimization (all measured, no capability loss)

  • Trimmed system prompts: desktop buildSystemPrompt ~960 → ~240 tok; mobile systemMessage ~224 → ~146 tok. Per-tool guidance moved to (already lived in) the tool descriptions.
  • Stripped mechanical JSON-Schema noise (pattern/min/max/default/additionalProperties) from the Chat tool payload: ~5,092 → ~4,628 tok/turn. Server-side zod validation unchanged.
  • Shrank tool responses: capped get_project_context_pack (5 pinned notes / 20 tasks, short previews) and gave get_note a TOC/outline mode for long notes + a get_note_range follow-up (busy pack 5,788 → 1,735 tok; long note 2,706 → 315). Pinned notes are now summarised by their heading outline when structured.
  • Audited tool-error self-correction: plumbing sound; a capable model recovers from terse errors, so no rewrite shipped (parked for weaker/on-device models).

Screenshots / recording

N/A for the desktop AI-layer changes. The three mobile fixes (attach-button centring, keyboard dismissal, wikilink navigation) are behavioural and best verified on device — see "Notes for reviewer".

Checklist

  • npm run type-check:all passes
  • npm run lint passes
  • npm test passes — new opt-in live tests (electron/lib/{prompt-optimization,tool-schema-optimization,tool-response-audit,tool-error-audit}.test.ts) skip cleanly when no LLM endpoint is configured (no secrets in source); all other unit tests (shared, mcp-server, chat-executor, payload-baseline, context-audit, toc) pass
  • npm run test:e2e — N/A (no desktop renderer UI change)
  • No hardcoded colours — N/A for changed files (AI layer + mobile use theme/var()); no new desktop UI
  • No text-[Npx] pixel font classes — N/A (no changed desktop UI)
  • New IPC handlers wrapped in handle() and return IpcResult<T> — N/A (no new IPC handlers)
  • New DB migrations appended (not edited) in schema.ts — N/A (no schema changes)
  • New SQL goes in electron/db/queries.ts — N/A on desktop (no new desktop SQL); new mobile queries live in mobile/src/db/queries.ts (mobile's own single source)
  • New dependencies / license map — N/A (no new deps)
  • New MCP tools registered in dispatch + Zod schema — N/A (no new desktop MCP tools; mobile tools live in mobile/src/chat/tools.ts). Desktop get_project_context_pack response shape changed (capped) — covered by updated chat-executor test.
  • --external:<pkg> bundle-guard allowlist — N/A (no compile flag changes; MCP runtime bundle rebuilds clean)

Notes for reviewer

  • Opt-in live tests: the four new AI experiment tests hit an OpenAI-compatible endpoint and are gated on PROMPT_TEST_BASE_URL / TEST_LLM_BASE_URL — they skip fast when unset, so CI is unaffected. Results were captured against a local Rork-as-OpenAI endpoint (claude-sonnet-4-6) and written up in the notes below.
  • Design notes (in the Cairn workspace): "AI Tooling Matrix", "AI Agent Identity & System Prompts", "Tool Schema Token Optimization", "Tool Response Size Optimization", "Tool Error Self-Correction Audit" — each records baseline → theory → measured result.
  • Deliberately held: aggressive tool-schema description-stripping (−25%, but occasionally dipped a multi-turn arg on one model) and the actionable-error rewrite — both parked pending a second/weaker model, with a clear re-entry plan.
  • Device-verify: the two mobile keyboard/attach fixes are native-behaviour and should be confirmed on a device; the wikilink fix is fully unit-covered.
  • Scope note: the tool-schema strip benefits the Chat path (TOOLS); the MCP server passes schema.shape to the MCP SDK (its own conversion), so MCP is unaffected — this is documented in the note.

Summary by CodeRabbit

  • New Features
    • Expanded mobile assistant support for managing notes, folders, and tasks, including renaming, moving, updating, and deleting items.
    • Added outline and line-range previews for long notes to make assistant responses faster and more focused.
    • Improved note and task links to resolve reliably using exact item IDs.
  • Bug Fixes
    • Improved keyboard dismissal and attachment-button alignment in mobile chat.
  • Performance
    • Reduced assistant prompt and tool-response size while preserving core behavior.
  • Documentation
    • Updated Star History display and documented the latest mobile and assistant improvements.

ddutchie added 10 commits July 11, 2026 08:47
…letes

Mobile chat went from 12 -> 19 Cairn tools, closing gaps vs desktop.

New tools (mobile/src/chat/tools.ts):
- get_task — full card detail (backed by existing getCard)
- update_task — edit title/desc/priority/due/assignee AND move column
  (existing updateTask + moveCardToColumn)
- rename_note — rename + rewrite inbound [[wikilinks]] in other notes,
  same-project title-collision guard, transactional (new renameNote query)
- bulk_move_notes — move notes to a folder, returns count (new moveNotesToFolder)
- list_folders — expose existing listFolders
- delete_note / delete_task — soft-delete synced as tombstone
  (existing softDeleteNote + new deleteCard)

New queries: renameNote, moveNotesToFolder, deleteCard.

Chat tool chips now navigate for rename_note/get_task/update_task (open by id);
deletes intentionally not tappable (item is gone).

Desktop PI Agent allowlist left unchanged (destructive tools stay excluded for
safety); its get_task/update_task were already present.

mobile type-check + eslint clean; type-check:all clean; shared vitest 122.
Updated AI Tooling Matrix note + changelog v0.1.3.
…s carry selection)

A live tool-selection experiment (Rork exposed as OpenAI, claude-sonnet-4-6)
showed the per-subsystem prompt guidance largely restated what each tool's
description already tells the model — trimming it kept tool-selection accuracy
equal or better.

- Desktop buildSystemPrompt: ~960 -> ~239 tok (-75%). Kept only cross-cutting
  rules not expressible in a tool description (get_active_context/never-invent-id,
  write-then-confirm, suggest_connections UI directive, markdown/mermaid
  rendering); dropped the Notes/Tasks/Dashboards/Idea Flow/KG tutorials.
- Mobile systemMessage: ~224 -> ~146 tok (-35%). Kept get_cairn_context-first,
  never-invent-id, [[wikilink]] output, write-confirm.

Per-tool nuances (dashboard constants, idea-flow inline note/task creation) were
already in the tool descriptions — no schema edits needed.

New electron/lib/prompt-optimization.test.ts is an opt-in regression guard: it
compares the old verbose prompt vs the current production prompt over realistic
requests and asserts prod selects tools at least as well. No hardcoded secrets —
reads PROMPT_TEST_BASE_URL / TEST_LLM_BASE_URL and skips cleanly (fast) when no
endpoint is reachable. Mobile tool descriptions are parsed from tools.ts so they
stay in sync without importing RN modules.

Live result (claude-sonnet-4-6): Desktop PROD 10/10 vs OLD 9/10; Mobile PROD
9/10 == OLD 9/10.

type-check:all + mobile type-check + eslint clean. Findings recorded in note
"AI Agent Identity & System Prompts". Changelogs: v2.4.8, mobile v0.1.3.
…ness, multi-turn)

Opt-in live test measuring whether the ~5,092-tok tool payload can be shrunk
without hurting the model. A token breakdown showed the weight is in PARAMETER
schemas (~3,308 tok), not descriptions — much of it mechanical JSON-Schema noise
(giant ISO `pattern` regexes, `maximum: 9e15` bounds, `default`,
`additionalProperties:false`) that carries no signal.

Compares two compression levels vs full, scoring BOTH dimensions schemas drive:
tool selection AND argument correctness. 12 single-turn scenarios (multi-field /
enum / date / nested-array / id-array) plus 3 multi-turn sequences where later
args depend on earlier tool results (id-threading). Also checks the transform
against the mobile hand-written tool shape.

Results (claude-sonnet-4-6, 3 runs): safe -10% never regressed and even fixes
the pathological updatedAfter regex; aggressive -25% held selection but
occasionally dipped one multi-turn arg. Mobile schemas are already lean (bloat is
desktop zod->JSON-Schema only). Findings + decision in note "Tool Schema Token
Optimization".

No hardcoded secrets — reads PROMPT_TEST_BASE_URL / TEST_LLM_BASE_URL, skips
cleanly (fast) when unreachable. eslint clean.
…ssion)

Adds stripSchemaNoise() to schemaToParameters (electron/lib/tools.ts): removes
JSON-Schema keywords the model never uses — `pattern` (e.g. the ~230-tok ISO-8601
regex on datetime fields), `minimum`/`maximum` bounds (incl. the 9e15 integer
ceiling), `default`, and `additionalProperties:false`. Field descriptions are
KEPT (they drive argument correctness). Server-side zod validation is unchanged.

Chat tool payload: ~5,092 -> ~4,628 tok/request (-9%, ~464 tok every turn),
with no change to tool selection or argument correctness — validated by the
tool-schema experiment across 12 single-turn + 3 multi-turn (id-threading)
scenarios. Scope is the Chat path (`TOOLS`); the MCP server passes schema.shape
to the MCP SDK which does its own conversion, so MCP is unaffected.

The experiment test now builds the true-full baseline from raw TOOL_SCHEMAS
(since production TOOLS is already stripped) and its assertions gained a ±1
nondeterminism slack so a single flipped scenario per run doesn't false-fail.

type-check:all clean; prompt-optimization + tool-schema regression tests green;
99 chat/tools unit tests pass. Note "Tool Schema Token Optimization" + changelog
v2.4.8 updated.
…100k)

gpt-tokenizer's default export is the o200k_base encoder (gpt-4o), not
cl100k_base as the header claimed — verified empirically. Comment + describe
label only; no behaviour change. Keeps the audit consistent with the o200k
figures used in the prompt/tool-schema optimization notes. The audit's AFTER
values already reflect this session's prompt trim + schema strip (it reads
buildSystemPrompt/TOOLS live), so no numeric changes were needed.
Tool responses (JSON.stringify(result)) accrue in the conversation and are
re-sent every subsequent turn, so one fat result permanently inflates context —
the cause of "a single query jumps context 10-20%" (and on the 4K on-device
window, outright overflow). Audited with electron/lib/tool-response-audit.test.ts.

Offenders + fixes:
- get_project_context_pack embedded up to 1000 chars per pinned note with NO cap
  on count, plus every open task's description → a busy project = ~5,788 tok in
  one call. Now: 5 most-recent pinned notes @ 300-char excerpt (+pinnedNotesTotal),
  open tasks capped to 20 total @ 120-char preview (+openTaskCount on mobile).
  Applied to BOTH mobile (db/queries.ts) and desktop (electron/shared/
  read-tools-pure.ts, used by MCP + desktop chat). Busy pack 5,788 -> 1,735 tok
  (-70%); no longer overflows the on-device window.
- get_note returned full content untruncated (~2,706 tok for a long doc). Mobile
  get_note now returns full content for small notes (<=1500 chars) but a
  line-numbered OUTLINE + intro for long ones (mode:"outline"), and a new
  get_note_range(id, start_line, end_line?) tool reads just the needed slice.
  Long-note read 2,706 -> 315 tok (-88%). full=true forces whole content. The
  getNote query + NoteDetailScreen UI are untouched (still full content).

Shared: shared/notes/toc.ts gains buildNoteOutline() + sliceLines() (+7 tests).

type-check:all + mobile type-check + eslint clean; shared 129; mcp-server +
chat-executor 83; payload-baseline + context-audit green. Note "Tool Response
Size Optimization" + changelogs v2.4.8 / mobile v0.1.3.
Follow-up to the response-size work. A pinned note in get_project_context_pack
is now represented by its heading OUTLINE when it has section headings (≥1 h2) —
a compact, structured semantic summary — falling back to a short excerpt only
for flat/heading-less notes. Applied to both mobile (db/queries.ts) and desktop
(electron/shared/read-tools-pure.ts → MCP + desktop chat).

Rationale: embeddings are one-way/lossy and can't be decoded to text, so they
can't replace an excerpt; but human-authored headings already ARE a summary. For
a structured note the outline conveys the whole note's shape
(Auth Design › Token rotation › Refresh flow) in ~half the tokens. Measured: 5
structured pinned notes 403 → 157 tok (-61%) vs the excerpt shape.

New shared noteDigest() in shared/notes/toc.ts (+tests). Desktop read-tools-pure
imports repo-root shared via relative path (no @cairn/shared alias on desktop);
MCP runtime bundle rebuilds clean. Updated the chat-executor pack test to the new
{outline|excerpt} digest shape (structured→outline, flat→excerpt).

type-check:all + mobile + eslint clean; shared 141; mcp-server + chat-executor 83.
Note "Tool Response Size Optimization" + changelogs v2.4.8 / mobile v0.1.3.
Audits whether tool error responses let the model recover on its own (re-fetch a
valid id via a read/search tool) vs dead-end or blindly retry. Drives a live
conversation with a STALE id injected, then scores whether the model recovers
and whether it does so immediately (no wasted retry turn). Compares the current
terse error text vs an actionable variant that names the bad id + recovery tool.

Finding (claude-sonnet-4-6, stable): both recover 3/3, immediate 2/3 — a capable
model self-corrects from "Not found" alone, so error text quality barely moves
the needle here. No production change shipped; the actionable-error rewrite is
parked as a low-cost win to validate against weaker/on-device models (where
terse errors are more likely to dead-end). Rationale + error inventory in note
"Tool Error Self-Correction Audit".

Plumbing verified sound: {error} results reach the model on all surfaces
(mobile output-available part, desktop MCP isError, desktop chat). Opt-in, no
secrets; skips cleanly with no endpoint. eslint clean.
… keyboard

1. Wikilinks by id (collision-proof): the chat agent now links notes/tasks as
   [[id]] using the exact id, which the MarkdownView resolver renders as the
   note's canonical title — so a link can never open the wrong item when two
   share a title. [[Title]] still works as a fallback, and findNoteIdByTitle/
   findCardIdByTitle are now deterministic (ORDER BY updated_at DESC) instead of
   an arbitrary LIMIT 1. New liveNoteTitleById/liveCardTitleById reverse lookups;
   prompts updated to prefer [[id]].

2. Attach button centring: the image-attachment GlassMenu (a native Host) drifted
   up under the composer row's flex-end alignment when the keyboard opened or the
   input grew. Wrapped it in a fixed 36px RN slot so it stays aligned with the
   send button regardless of keyboard/multiline state.

3. Stuck keyboard: send() now dismisses via KeyboardController.dismiss() (the
   keyboard-controller API — RN's Keyboard.dismiss desyncs with this library and
   left it stuck until an app switch), and the screen also dismisses on blur.

type-check:all + mobile type-check + eslint clean; shared markdown tests pass.
Changelog v0.1.3.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ddutchie, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c785082-fe08-4ee1-9855-996024c71080

📥 Commits

Reviewing files that changed from the base of the PR and between c06d8a9 and 3af4051.

📒 Files selected for processing (13)
  • electron/lib/mobile-tools-fixture.ts
  • electron/lib/prompt-optimization.test.ts
  • electron/lib/tool-error-audit.test.ts
  • electron/lib/tool-schema-optimization.test.ts
  • mobile/app/(tabs)/chat/index.tsx
  • mobile/app/(tabs)/search/index.tsx
  • mobile/changelogs/v0.1.3.md
  • mobile/src/chat/tools.ts
  • mobile/src/components/GlassMenu.tsx
  • mobile/src/db/chat-store.ts
  • mobile/src/db/queries.ts
  • mobile/src/notes/embeddings.ts
  • shared/notes/toc.ts
📝 Walkthrough

Walkthrough

The changes compact AI prompts, tool schemas, and project context responses; add outline-based note reads and mobile note/task tools; improve id-based link resolution and chat behavior; and add live and deterministic audits for selection, recovery, and token usage.

Changes

AI context and mobile chat

Layer / File(s) Summary
Note outline and digest primitives
shared/notes/toc.ts, shared/notes/toc.test.ts
Adds heading outlines, inclusive line slicing, compact note digests, and unit tests.
Bounded context and note reads
electron/shared/read-tools-pure.ts, mobile/src/db/queries.ts, electron/ipc/chat-executor.test.ts
Caps pinned notes and task previews, adds total counters, and supports outline or range-based note reads.
Assistant prompt and schema compaction
electron/lib/tools.ts, mobile/src/chat/agent.ts, mobile/src/chat/providers/apple.ts, changelogs/v2.4.8.md
Shortens assistant prompts and removes selected nonessential schema fields.
Mobile agent tools and entity links
mobile/src/chat/tools.ts, mobile/src/db/queries.ts, mobile/src/components/MarkdownView.tsx, mobile/app/(tabs)/chat/index.tsx, mobile/changelogs/v0.1.3.md
Adds note/task operations, id-first link resolution, and navigation mappings for affected entities.
Prompt, schema, response, and recovery audits
electron/lib/*audit.test.ts, electron/lib/prompt-optimization.test.ts, electron/lib/tool-schema-optimization.test.ts, electron/lib/context-audit.test.ts
Adds token, prompt-selection, schema, and tool-error recovery audits.
Chat interaction and repository artifacts
mobile/app/(tabs)/chat/index.tsx, README.md, mobile/changelogs/v0.1.3.md
Updates keyboard and attachment behavior, release notes, and the Star History embed.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Chat
  participant MobileTools
  participant Database
  participant MarkdownView
  Chat->>MobileTools: invoke note or task tool
  MobileTools->>Database: read or mutate entity
  Database-->>MobileTools: return result and id
  MobileTools-->>Chat: return tool response
  Chat->>MarkdownView: render entity reference
  MarkdownView->>Database: resolve id to live title
  Database-->>MarkdownView: return title
Loading

Possibly related PRs

  • ddutchie/cairn#55: Both changes compact get_project_context_pack payloads and pinned-note previews.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: expanded mobile tools, navigation improvements, and prompt/context token optimization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ddutchie/mobiletoolingpass

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
shared/notes/toc.ts (1)

27-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting shared fence-detection and heading-match logic.

extractHeadings (L27-44) and buildNoteOutline (L64-79) duplicate the same inFence toggle, line.startsWith("```") check, and ^(#{1,3})\s+(.+) regex. If one is fixed for an edge case (e.g., tilde fences, indented fences), the other can silently diverge. The agreement test at toc.test.ts:32-36 guards heading texts but not structural details like line numbers or ids.

Consider extracting a shared matchHeadingLine(line, inFence) helper or a scanHeadings(markdown) that both functions build on.

Also applies to: 64-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/notes/toc.ts` around lines 27 - 44, Extract the duplicated fence
detection and heading matching from extractHeadings and buildNoteOutline into a
shared helper such as matchHeadingLine or scanHeadings. Update both functions to
use the shared logic so fence handling and the ^(#{1,3}) heading pattern remain
consistent, while preserving their existing heading text, line number, and id
behavior.
electron/lib/prompt-optimization.test.ts (1)

202-219: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

mobileTools() is duplicated across two test files with an additionalProperties inconsistency. Both files define the same regex-based mobileTools() function to parse tool names/descriptions from mobile/src/chat/tools.ts, but prompt-optimization.test.ts uses additionalProperties: true while tool-schema-optimization.test.ts uses additionalProperties: false. The false value matches the mobile obj() helper. Extract this to a shared helper module (e.g., electron/lib/test-helpers.ts) to eliminate duplication, align additionalProperties to false, and make the regex easier to maintain in one place.

  • electron/lib/prompt-optimization.test.ts#L202-L219: extract mobileTools() to a shared module; change additionalProperties: true → false.
  • electron/lib/tool-schema-optimization.test.ts#L221-L237: replace inline mobileTools() with import from the shared module.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/lib/prompt-optimization.test.ts` around lines 202 - 219, Extract the
duplicated mobileTools() parser from
electron/lib/prompt-optimization.test.ts#L202-L219 into a shared helper module,
set its generated schema to additionalProperties: false, and export it. Replace
the inline mobileTools() implementation in
electron/lib/tool-schema-optimization.test.ts#L221-L237 with an import from that
helper; both sites should use the shared regex and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/lib/prompt-optimization.test.ts`:
- Around line 108-122: Add a 30-second AbortSignal timeout to the LLM fetch
calls in taskToolCall (electron/lib/prompt-optimization.test.ts:108-122),
runWithInjectedError (electron/lib/tool-error-audit.test.ts:69-73), taskToolCall
(electron/lib/tool-schema-optimization.test.ts:153-157), and runConversation
(electron/lib/tool-schema-optimization.test.ts:194-198).
- Around line 202-219: Update mobileTools() to use the same robust tool-schema
extraction approach as tool-schema-optimization.test.ts, supporting the mobile
source’s current description formats without silently omitting tools when
formatting changes. Align the generated parameters schema with the mobile obj()
helper by setting additionalProperties to false, and reuse the established
implementation rather than maintaining divergent parsing logic.
- Around line 190-196: Update the fourth instruction in PROD_MOBILE to match the
exact note-title linking wording used by mobile/src/chat/agent.ts
systemMessage(), replacing the stale [[double brackets]] text. Keep the
remaining prompt instructions unchanged and ensure the test fixture stays
synchronized with the production mobile prompt.

In `@electron/lib/tool-schema-optimization.test.ts`:
- Around line 221-237: Extract the shared mobile tool-schema parsing logic from
mobileTools() into a reusable helper, preserving additionalProperties: false to
match the mobile obj() helper. Update both tool optimization tests to use the
helper and remove the duplicated implementations, while keeping the differing
schema behavior of non-mobile tools unchanged.

In `@mobile/app/`(tabs)/chat/index.tsx:
- Around line 410-425: Update the attachSlot style used by the wrapper around
GlassMenu to set alignSelf: "center", matching sendBtn’s alignment so the attach
control remains vertically aligned as the multiline input grows; keep its fixed
height and existing layout behavior unchanged.

In `@mobile/src/db/queries.ts`:
- Around line 1021-1034: Update moveNotesToFolder so its UPDATE only matches
live note records by adding the existing LIVE/type='note' predicates alongside
the id filter. Preserve the current folder, timestamp, version, notification,
and affected-row behavior for matching notes.

---

Nitpick comments:
In `@electron/lib/prompt-optimization.test.ts`:
- Around line 202-219: Extract the duplicated mobileTools() parser from
electron/lib/prompt-optimization.test.ts#L202-L219 into a shared helper module,
set its generated schema to additionalProperties: false, and export it. Replace
the inline mobileTools() implementation in
electron/lib/tool-schema-optimization.test.ts#L221-L237 with an import from that
helper; both sites should use the shared regex and behavior.

In `@shared/notes/toc.ts`:
- Around line 27-44: Extract the duplicated fence detection and heading matching
from extractHeadings and buildNoteOutline into a shared helper such as
matchHeadingLine or scanHeadings. Update both functions to use the shared logic
so fence handling and the ^(#{1,3}) heading pattern remain consistent, while
preserving their existing heading text, line number, and id behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3c852ff-9d5e-4e70-a7cb-b3954896c545

📥 Commits

Reviewing files that changed from the base of the PR and between 48e4278 and c06d8a9.

📒 Files selected for processing (19)
  • README.md
  • changelogs/v2.4.8.md
  • electron/ipc/chat-executor.test.ts
  • electron/lib/context-audit.test.ts
  • electron/lib/prompt-optimization.test.ts
  • electron/lib/tool-error-audit.test.ts
  • electron/lib/tool-response-audit.test.ts
  • electron/lib/tool-schema-optimization.test.ts
  • electron/lib/tools.ts
  • electron/shared/read-tools-pure.ts
  • mobile/app/(tabs)/chat/index.tsx
  • mobile/changelogs/v0.1.3.md
  • mobile/src/chat/agent.ts
  • mobile/src/chat/providers/apple.ts
  • mobile/src/chat/tools.ts
  • mobile/src/components/MarkdownView.tsx
  • mobile/src/db/queries.ts
  • shared/notes/toc.test.ts
  • shared/notes/toc.ts

Comment on lines +108 to +122
const res = await fetch(`${BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}),
},
body: JSON.stringify({
model: MODEL,
messages,
tools,
tool_choice: "auto",
temperature: 0.2,
stream: false,
}),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

LLM fetch calls lack timeouts across three audit test files. All three live test files check endpoint reachability with AbortSignal.timeout(2500) in endpointUp(), but the actual /chat/completions fetch calls have no timeout — if the endpoint accepts the connection but hangs during generation, the test blocks until the 180–240s vitest timeout. Add signal: AbortSignal.timeout(30_000) to each LLM fetch call.

  • electron/lib/prompt-optimization.test.ts#L108-L122: add signal: AbortSignal.timeout(30_000) to the fetch in taskToolCall.
  • electron/lib/tool-error-audit.test.ts#L69-L73: add signal: AbortSignal.timeout(30_000) to the fetch in runWithInjectedError.
  • electron/lib/tool-schema-optimization.test.ts#L153-L157: add signal: AbortSignal.timeout(30_000) to the fetch in taskToolCall.
  • electron/lib/tool-schema-optimization.test.ts#L194-L198: add signal: AbortSignal.timeout(30_000) to the fetch in runConversation.
📍 Affects 3 files
  • electron/lib/prompt-optimization.test.ts#L108-L122 (this comment)
  • electron/lib/tool-error-audit.test.ts#L69-L73
  • electron/lib/tool-schema-optimization.test.ts#L153-L157
  • electron/lib/tool-schema-optimization.test.ts#L194-L198
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/lib/prompt-optimization.test.ts` around lines 108 - 122, Add a
30-second AbortSignal timeout to the LLM fetch calls in taskToolCall
(electron/lib/prompt-optimization.test.ts:108-122), runWithInjectedError
(electron/lib/tool-error-audit.test.ts:69-73), taskToolCall
(electron/lib/tool-schema-optimization.test.ts:153-157), and runConversation
(electron/lib/tool-schema-optimization.test.ts:194-198).

Comment thread electron/lib/prompt-optimization.test.ts
Comment thread electron/lib/prompt-optimization.test.ts
Comment thread electron/lib/tool-schema-optimization.test.ts
Comment thread mobile/app/(tabs)/chat/index.tsx
Comment thread mobile/src/db/queries.ts
ddutchie added 5 commits July 11, 2026 11:54
Valid findings fixed:
- Add AbortSignal.timeout(30_000) to all 4 live LLM fetch calls
  (prompt-optimization taskToolCall, tool-error-audit runWithInjectedError,
  tool-schema-optimization taskToolCall + runConversation) so a hung endpoint
  can't hang the run.
- Extract the duplicated mobile-tools parser into a shared electron/lib/
  mobile-tools-fixture.ts (parseMobileTools): tolerant regex ([gs], relaxed
  whitespace) that handles single- and multi-line description formats and throws
  if it parses nothing (guards against silent omission on a format change);
  emits additionalProperties:false to match mobile's obj() helper. Both AI tests
  now import it instead of maintaining divergent parsers.
- Sync PROD_MOBILE fixture's note-linking instruction to the current
  mobile/src/chat/agent.ts systemMessage() ([[id]] wording; the stale
  [[double brackets]] text was left behind).
- attachSlot: add alignSelf:"center" to match sendBtn so the attach control
  stays vertically aligned as the multiline input grows.
- moveNotesToFolder: scope the UPDATE to live notes (LIVE + type='note')
  alongside the id filter, so a tombstoned/non-note row can't be moved.
- shared/notes/toc.ts: extract shared scanHeadings() so extractHeadings and
  buildNoteOutline share one fence-tracking + heading-match implementation.

Skipped: none — all findings were still valid against current code.

type-check:all + mobile type-check + eslint clean; shared toc/markdown 25,
chat-executor/mcp-server/shared/context-audit 88, AI experiment tests skip
cleanly (token asserts pass). Fixture parses all 19 mobile tools.
The image-attachment button was mis-centred vertically until a tab change forced
a re-layout. Cause: the @expo/ui SwiftUI Host uses matchContents, which measures
its content asynchronously after first mount, so on initial paint the trigger
wasn't centred in its slot.

Fix: pin the Host to an explicit 32x32 frame (attachContainer) and, in GlassMenu,
skip matchContents when the container has a fixed width+height — a concrete
frame lays out correctly on first paint with no async remeasure. Behaviour is
unchanged for other GlassMenu callers (they pass no fixed size, so matchContents
stays on).

mobile type-check + eslint clean. Changelog v0.1.3.
…t mount

Context ring: the context-window usage ring was session-only state, lost when
the Chat tab unmounted. Persist the latest usage in app_settings (local-only,
no capture trigger) — saveLastChatUsage on each final-usage event, seed the
state from loadLastChatUsage() on mount, and clear it in clearChatHistory. The
ring now restores on reopen instead of vanishing until the next turn.

Attach icon: the @expo/ui SwiftUI Host measures its content asynchronously after
mount, so the trigger rendered mis-centred until a later re-layout (keyboard/tab
change) forced a remeasure. GlassMenu now bumps state once on the Host's
onLayoutContent (re-keying the Menu) to force a single re-layout so it settles
correctly on first paint; the attach container keeps a 32x32 size hint.

mobile type-check + eslint + type-check:all clean. Changelog v0.1.3.
…lock

The ScrollView's onContentSizeChange unconditionally called scrollToEnd, so any
content-height change — including expanding a previous message's Reasoning block
while scrolled up — yanked the transcript to the bottom.

Track whether the user is near the bottom (onScroll, <80px from end) and only
auto-follow on content growth when true, so streaming still pins to the bottom
but manual expansions don't. Sending a message resets nearBottom=true so the
reply is followed.

mobile type-check + eslint clean. Changelog v0.1.3.
…n scroll

Semantic search parity + no buried notes:
- The chat semantic_search_notes tool now calls catchUpIndex() first (like the
  Search tab), so it indexes new/edited notes and searches a fresh index instead
  of a stale one — previously recent notes were missed and it never downloaded
  assets on a device that hadn't opened Search.
- semanticSearch(ws, q, k?) now makes the k slice optional and returns the FULL
  hybrid-ranked candidate list when k is omitted. Both callers (tool + Search
  tab) now gather the full lists across workspaces, merge, re-rank by the hybrid
  `rank`, and slice ONCE — so a note ranked low within its workspace but high
  globally isn't dropped before the merge (the "correct note buried at #36 →
  promoted to #1" case). The tool also dropped a redundant re-round of the
  already-rounded display score.

Keyboard-open scroll regression: the near-bottom auto-follow guard (added to
stop reasoning-expand from jumping) also blocked scrolling to the latest when
the keyboard opened. TextInput onFocus now sets nearBottom=true and scrolls to
end, so focusing the composer follows to the newest message again.

mobile type-check + eslint clean. Changelog v0.1.3.
@ddutchie
ddutchie merged commit 55bb7d3 into main Jul 11, 2026
6 checks passed
@ddutchie
ddutchie deleted the ddutchie/mobiletoolingpass branch July 11, 2026 16:26
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