Skip to content

feat(slash): /rename, plus tier-contrast and cursor follow-ups#72

Merged
hakula139 merged 25 commits intomainfrom
feat/resume-followups-and-cursor-fix
May 9, 2026
Merged

feat(slash): /rename, plus tier-contrast and cursor follow-ups#72
hakula139 merged 25 commits intomainfrom
feat/resume-followups-and-cursor-fix

Conversation

@hakula139
Copy link
Copy Markdown
Owner

@hakula139 hakula139 commented May 9, 2026

Summary

Bundles the /resume picker follow-ups with a /rename slash command and a chat-input chrome simplification.

/rename lets you set a session title manually — bare opens a single-line modal pre-filled with the current title; /rename <title> applies directly. After a manual rename, the background AI title generator no-ops for the rest of the session so a slow Haiku response can't overwrite the user's pick. Renames before the first prompt defer the on-disk write: the JSONL file isn't created until a real message lands, so a session that's only renamed (then quit) leaves nothing behind, and one that does have a header-only file on disk can still be resumed.

Two follow-ups land alongside it:

  • Tier contrast. The /resume picker's secondary text (project / id / time) was nearly indistinguishable from dim. Brighten the muted slot across all five built-in themes so the muted / dim / text triplet reads as three distinct steps.
  • Simpler input chrome. Drop the empty-buffer placeholder copy entirely (matching Claude Code's bare prompt). The arg-mode [id] ghost text stays but now sits one column after the cursor, mirroring ratatui_textarea's built-in placeholder layout. Net 74 fewer lines in input.rs plus the has_queued / sync_input_queue_hint plumbing the placeholder used to drive.

The slash-command popup descriptions for the five dual-mode commands also converge on a single short verb-first phrase, since the parameter placeholder is already shown by the row's usage() next to the name.

Design decisions

  • Defer the write, don't filter at flush time. A /rename before the first prompt produces a header + Title entry but no message. Rather than writing those out and stripping them on next resume, WriterStatus::Pending { header, deferred_title } holds them in memory and only opens the JSONL on the first non-empty flush. Empty sessions die with the actor; non-empty sessions get a single atomic flush.
  • Single source of truth for manual-title state. manual_title_set lives on SharedState only. The actor reads / writes it through a &SharedState borrow (passed into absorb); the title generator's pre-check goes through the same flag. This closes the race where Haiku finishes between /rename arriving and the actor draining its channel, and avoids the "two correlated bools" smell of a parallel field on SessionState.
  • try_defer_title returns Option<String>, not Result<(), String>. The rejected title is routed back to the caller for normal-path queueing, not surfaced as an error. Option says that directly: Some(t) hands the title back, None parks it in the deferred slot. Avoids both the inverted-Result-reading smell and the clippy::large_enum_variant hit from holding a 272-byte Entry in WriterStatus. Last-wins is preserved (a second Some overwrites the first).
  • Resume bail on pre_sanitize_len > 0 && messages.is_empty(). Header + Title-only sessions sanitize down to zero messages but should still resume — the title is meaningful state. Folding data.title.is_some() into the first_user_prompt_seen check keeps the title alive across the next message.
  • SlashContext::with_title borrow over LiveSessionInfo field. The status-bar title isn't part of the persisted config snapshot — adding it to LiveSessionInfo would mean syncing two sources of truth across SessionRolled / Title events. A read-only Option<&str> parameter keeps the data flow obvious for the one command that needs it.
  • Mutating in both forms. /rename <title> could in principle dispatch mid-turn, but routing it as Mutating matches /clear, /init, /resume — a single deferral rule keeps the dispatcher simple and the user model consistent.
  • Slash-command popup wording. description() and usage() are rendered side by side, so spelling the parameter inside the description duplicates information the row already shows. The five dual-mode commands also split between two prose templates ("Open the X picker or switch directly with /cmd <param>" vs. "Verb the noun — /cmd for X, /cmd <param> to Y"). Land on the same shape claude-code and codex use: a single short verb-first phrase, no parameter mention, no UI-mechanism framing.
  • Brighten muted, don't darken text. Bumping Catppuccin from Overlay0 to Subtext0 (and Material's equivalent) lifts the secondary tier without disturbing existing snapshots' text rendering.
  • Drop placeholder, lean on the textarea's primitive. The previous fix overlaid a Paragraph to work around ratatui_textarea reserving col 0 for its cursor cell. Removing the placeholder altogether eliminates the hand-roll, and shifting the arg-mode hint by one column lets the OS-native cursor lead the dim text — the same layout the textarea's built-in set_placeholder_text produces.

Changes

File Description
agent/event.rs Add UserAction::Rename { title }.
agent.rs Route Rename through the mid-turn warn-log catch-all.
main.rs apply_rename helper: persist via set_manual_title, surface I/O failure, emit SessionTitleUpdated.
session/state.rs WriterStatus::Pending { header, deferred_title } | Active(SessionWriter) | Broken state machine; try_defer_title returns Option<String> (Some(t) routes the title to the normal queue, None parks it); flush_entries reconstructs Entry::Title { source: UserProvided, updated_at: now() } at flush time when deferred_title is set. New // ── try_defer_title ── unit section covers Pending overwrite, Active reject, Broken reject. Resume contract on SessionState::resumed reworded as a precondition.
session/handle.rs SharedState.manual_title_set: AtomicBool is the single source of truth; SessionHandle::set_manual_title latches before dispatch and exposes a manual_title_set accessor. Resume bail loosened to pre_sanitize_len > 0 && data.messages.is_empty() ("(likely corrupt)" parenthetical dropped — sanitize-prunes-everything is also legitimate); data.title.is_some() folds into first_user_prompt_seen. Tests cover the latch contract on healthy and dead-actor dispatch, plus title-only resume with title preservation across the next message.
session/handle/testing.rs acks_then_drops covers the SetManualTitle or-pattern arm.
session/actor.rs absorb now takes &SharedState. The SetManualTitle arm marks the shared flag, then either parks the title via try_defer_title (returns None) or queues a Title { source: UserProvided } entry (returns Some); AppendAiTitle short-circuits when shared.manual_title_set(); Record passes the same flag into queue_message_entries. New tests cover last-wins on two SetManualTitles and positional ordering of header / title / message.
session/title_generator.rs Pre-check manual_title_set after parse_title to skip the actor round-trip when the user has already renamed.
slash.rs Register the new module.
slash/context.rs SlashContext.title: Option<&str> plus with_title constructor; new() delegates with None.
slash/registry.rs Add &RenameCmd to BUILT_INS (alphabetical).
slash/rename.rs New: RenameCmd (Mutating, [<title>] usage) + RenameModal (single-line input, 80-char cap, Enter / Backspace / Esc handling), plus TestBackend render tests for layout, ellipsis truncation, cursor placement, and short-area cursor bail.
slash/{effort,model,theme,rename,resume}.rs Tighten popup descriptions to a single verb-first phrase; the parameter placeholder is already shown by usage().
tui/app.rs Route UserAction::Rename through apply_action_locally; pass the live status-bar title into SlashContext; drop sync_input_queue_hint.
tui/components/status.rs Promote title() from #[cfg(test)] to production-visible.
tui/components/input.rs Drop empty-buffer placeholder + has_queued plumbing; arg-mode [id] ghost text now paints one column past the cursor.
tui/theme.rs, tui/theme/loader.rs, themes/*.toml Brighten the muted slot across the five built-in themes (Catppuccin Subtext0 and Material equivalents).
CLAUDE.md, README.md, docs/guide/slash-commands.md, docs/roadmap.md, docs/design/tui/cancellation.md Surface /rename and trim the slash-commands guide; drop placeholder claims that no longer apply.
Snapshots Catch up popup, app-frame, and input render baselines.

Test plan

  • cargo build compiles cleanly
  • cargo clippy --all-targets -- -D warnings — zero warnings
  • cargo test — 1811 tests pass
  • cargo llvm-cov --ignore-filename-regex 'main\.rs' — 98% line / 98% region coverage
  • pnpm lint — Markdown
  • pnpm spellcheck
  • Manually exercised /rename, /rename foo, modal Enter on empty, Esc cancel, the AI-title race (rename mid-Haiku — manual wins, no flicker), and the empty-session round-trip (rename + quit leaves no file on disk; rename + message + quit + resume keeps the title)

hakula139 added 6 commits May 9, 2026 01:43
`ratatui_textarea`'s built-in placeholder paint offsets one column right
of its cursor anchor, so the OS cursor sat in a gap to the left of `A`
in `Ask anything...`. Hand-roll the placeholder at `textarea.x` so the
cursor lands on top of the first character, matching the modal pickers'
search row.
Five `draw_frame_*` snapshots that include the input panel still showed
the old `❯  Ask` two-space gap; refresh them to match the
placeholder-on-first-character layout.
…ctly

`muted` was Catppuccin's Overlay0 (one step above `dim`'s Surface2),
which left only ~17% luminance separation between secondary text and
metadata. On many terminals the two tiers blurred into the same shade
of grey. Pulling `muted` to Subtext0 (and the equivalent secondary-text
hue in the non-Catppuccin palettes) widens the gap to a clear 4-step
ladder while leaving `dim` where it is. Tool block left bars stay on
the prior Overlay0 grey via their own `tool_border` slot — borders are
chrome and want to stay subtle, so they decouple from the text tier.

The /resume picker is the most visible beneficiary: unselected titles
now read clearly above the metadata row. Status bar titles, tool
labels, and grep file headers also gain readability for the same
underlying reason.
Bare opens a modal pre-filled with the current title; `/rename <title>`
applies directly. After a manual rename, the background AI title
generator no-ops for the rest of the session so a slow Haiku response
can't overwrite the user's pick.

The manual-title flag is mirrored into both `SessionState` (read by the
actor's `AppendAiTitle` gate) and `SharedState` (read by the title-gen
pre-check via `SessionHandle`), so the suppression catches the race
either side of the Haiku call.
Restate-the-code lines and multi-paragraph rationales that the function
or field signature already implies — kept only the one-line summaries
that explain the cross-module race coordination.
The slash-commands user guide gains a "Renaming a Session" section and a
table row; the roadmap moves /rename out of Deferred into the working
slash-command list (also tightening the bullet so the section stays
balanced); the README adds /rename to the working-today bullet; the
CLAUDE.md crate tree gains a `rename.rs` row.
@hakula139 hakula139 added the enhancement New feature or request label May 9, 2026
@hakula139 hakula139 self-assigned this May 9, 2026
@hakula139 hakula139 added the enhancement New feature or request label May 9, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 98.74477% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/oxide-code/src/slash/rename.rs 97.42% 7 Missing ⚠️
crates/oxide-code/src/session/state.rs 97.95% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

hakula139 added 19 commits May 9, 2026 16:40
Drops Autocomplete-Popup-as-its-own-section in favor of a two-line lede,
folds /resume + /rename into one Sessions section, trims implementation
detail (resolution tier walk, JSONL source tag, "load + sanitize path"
parity note) that users don't need to use the commands.
Adds TestBackend render tests for `RenameModal` (layout, ellipsis truncation,
cursor placement, short-area cursor bail, height pin) plus a `Tab`-key arm
test for the catch-all branch. Extends the `acks_then_drops` integration
test to route a `set_manual_title` call through the fixture so the
`SetManualTitle` or-pattern arm in the test helper is exercised; adds two
handle-level tests pinning the latched-flag contract on both healthy
dispatch and dead-actor paths.

Patch coverage on the /rename feature lifts from ~78% to ~97%.
Empty-buffer placeholder text (`Ask anything...`, `Type to queue a
follow-up...`, `Esc edits last queued · Enter adds another`) added more
chrome than signal — Claude Code's bare prompt is the simpler model.
The arg-mode `[id]` ghost text stays, but is now painted one column
right of the cursor so the OS-native cursor visually leads the dim
hint, mirroring `ratatui_textarea`'s built-in placeholder layout.

Drops `placeholder` and `has_queued` fields, the `PLACEHOLDER_*`
constants, `refresh_placeholder` / `set_has_queued` /
`is_buffer_empty`, and the App's `sync_input_queue_hint` plumbing —
nothing else read those signals. Net 74 fewer lines in `input.rs`.
A `/rename` on a fresh session followed by quit used to write a
header+title-only JSONL with no actual messages, which then failed to
resume ("session has no messages"). Two fixes in one shape:

- Generalize `WriterStatus::Pending` to hold a `deferred: Vec<Entry>`
  so a `/rename` before any record cmd queues the title there. The
  queue dies with the actor when no message ever arrives — no file
  on disk. Replaces the prior `ManualTitle` enum (Unset/Pending/
  Persisted) and its invariant correlation with `WriterStatus`.
- Loosen the resume bail to `pre_sanitize_len > 0 && messages.is_empty()`
  so a legitimately empty (header-only or header+title) file resumes
  cleanly. Pre-fill `first_user_prompt_seen` with `data.title.is_some()`
  so the next message doesn't push a duplicate FirstPrompt title.
The first cut of `WriterStatus::Pending { header, deferred: Vec<Entry> }`
plus a `defer_entry`/`is_pending` pair had two soft spots that PR review
flagged: `Vec<Entry>` permits any entry shape (today only titles are
ever deferred), and `defer_entry`'s `debug_assert!(false)` silently
drops the entry in release builds when the precondition fails.

- Narrow to `deferred_title: Option<String>`. Only titles are ever
  queued, multiple `/rename`s collapse to last-wins for free, and the
  `large_enum_variant` clippy lint stays quiet without boxing.
- Replace `is_pending` + `defer_entry` with a single
  `try_defer_title(title) -> Result<(), String>` that returns the
  rejected title for the caller to route into the live batch. Removes
  the silent-drop hazard and the precondition-then-mutate dance.
- Reconstruct the `Entry::Title { source: UserProvided }` at flush
  time (timestamp = flush instant, since the deferred slot only fires
  on first promotion of an otherwise-empty file).
- Add tests for last-wins multi-rename and for the deferred slot
  surviving a `Pending`-create rollback. Pin positional ordering of
  header < title < message in the deferred-then-record path. Tighten
  the unwritten-session assertion to `store.list().is_empty()`.
PR review flagged the dual `manual_title_set` (a `bool` on `SessionState`
and an `AtomicBool` on `SharedState`) as correlated state — two writers
storing the same logical fact. The dual was useful but expressed as if
accidental.

Drop the `SessionState` field. The actor reads / writes the
`SharedState` atomic directly (passed through `absorb`), and
`queue_message_entries` takes `manual_title_set: bool` as a parameter.
The handle still flips the atomic eagerly before dispatch (closes the
title-generator TOCTOU window) and the actor mirrors on absorb (covers
direct-cmd tests + defends against a flip during a batch's
`AppendAiTitle` arm). Single storage, two idempotent writers.
Two stale comments flagged in PR review:

- `slash/rename.rs:289`: claimed empty `String::pop` "would panic in
  some std impls" — `String::pop` is documented to return `None` on
  empty in every impl. The test name already pins the behavior.
- `slash/rename.rs:300`: referenced `MAX_TITLE_LEN`, but the local
  constant is `MAX_TITLE_CHARS`. Fully qualify to
  `session::state::MAX_TITLE_LEN` so the cross-module mirror is
  unambiguous.

Also trim the title-only resume test comment to one line.
The popup help displays `description()` next to the `usage()` placeholder
(`[<level>]`, `[<title>]`, ...), so spelling the parameter inside the
description duplicates information the row already shows. The five
dual-mode commands also split between two prose templates ("Open the X
picker or switch directly with /cmd <param>" vs. "Verb the noun — /cmd
for X, /cmd <param> to Y"), which read as inconsistent.

Drop the parameter mention and the UI-mechanism framing. Land on the
same shape claude-code and codex use: a single short verb-first phrase.
Result<(), String> read like an error channel ("the title couldn't be
deferred") when the rejected title is just routed elsewhere — not an
error. Option<String> says it directly: Some(t) hands the title back for
the caller to queue normally, None means it's parked in the deferred
slot.

Drops the redundant "Pending: defer / Active: route" comment on the call
site — the new doc comment on try_defer_title plus the None / Some
branches read self-evidently. Adds a // ── try_defer_title ── unit
section covering the three branches (Pending overwrite, Active rejects,
Broken rejects) directly so a regression that drops the title on
Active / Broken would fail at the unit layer instead of riding only on
the actor integration tests.
- Drop "(likely corrupt)" from the resume bail. Sanitize also fires when
  every message was an unresolved tool block, which is legitimate
  pruning; the bail comment above already states the real reason
  (chaining onto a dropped last_message_uuid).
- Reword `SessionState::resumed` doc as a precondition the caller must
  meet, not a description of what the one current caller does.
- Drop the inline ` || data.title.is_some()` comment in handle.rs — the
  new resumed contract above carries the same WHY.
- Tighten the AppendAiTitle re-check comment to name the actual race
  (rename flips the latch after the title generator queued the entry).
- Reword the FirstPrompt latch comment so "before the push" is no longer
  ambiguous between the conditional title push and the unconditional
  message push.
The resume picker rendered titles on the muted tier so they could stay
distinct from the metadata's dim tier without colliding with the
cursor-row text+bold. After the recent muted brightening, muted and dim
sit closer than they read on paper — the title still doesn't pop.

Lift every title onto text and rely on bold + the gutter marker to call
out the cursor row, matching how claude-code's session list reads. Add
a trailing blank line per row (ROW_HEIGHT 2 → 3) so adjacent sessions
don't visually run together.
Bold + the gutter `>` weren't a strong enough cue when the title color
itself matched non-cursor rows. Lift the cursor row's title onto the
theme accent so the whole row reads as one bright pop, matching the
gutter marker that's already in accent.
The popup column read uneven: /config and /diff and /status had 90+ char
descriptions that wrapped or stomped neighbors at typical widths, while
/model and /theme were 16-17 chars. The path detail in /config and the
git-command parenthetical in /diff belonged in the modal output, not the
popup hint.

Bring everything into the 15-40 char range. The state column already
shows usage placeholders (`<id>`, `<title>`, ...) so descriptions can
focus on a single verb-first phrase.
- handle.rs: move resume_title_only_session_succeeds_and_keeps_title_on_next_message
  ahead of resume_empty_session_errors so within-section order tracks
  happy → variants → error per CLAUDE.md.
- state.rs: rename `_returns_title_verbatim` → `_rejects_with_title` to
  phrase the scenario instead of the return mechanism.
Seven test names were 60-88 chars long, padded with mechanism details
that the assert lines already cover. Examples: "writes user provided
title in lieu of first prompt" → "replaces first prompt title";
"keeps pending and preserves deferred title" → "preserves deferred
title" (Pending is implied by the deferred slot surviving). Brings
the long ones under 60 chars while keeping the scenario clear.
The trailing fragments were mechanism details the asserts already cover:
"with system message no forward" → "locally", "with cch populated" →
implicit, "within declared order" → implicit, "swaps id replays
transcript and clears pending state" → "replays transcript and clears
pending". Brings these from 75-88 chars down to 47-60.
@hakula139 hakula139 merged commit efc66f9 into main May 9, 2026
4 checks passed
@hakula139 hakula139 deleted the feat/resume-followups-and-cursor-fix branch May 9, 2026 16:16
hakula139 added a commit that referenced this pull request May 9, 2026
<!-- markdownlint-disable-next-line first-line-heading -->
## Summary

A doc-only sweep of `docs/` that fixes stale claims drifted from source,
replaces rotting inline file links with backticks or concept-level
cross-doc pointers, normalizes Sources sections, and reworks the prose
tics that had crept into design and research docs. 19 files touched,
`pnpm spellcheck` and `pnpm lint` clean.

The sweep was deferred during the `/resume` PR after rewriting
`slash/resume.md` exposed several stale claims of the same shape lurking
in `commands.md` and `modals.md`. Folding the cross-doc audit into one
PR reads cleaner than piecemeal edits attached to feature work.

## Design decisions

- **Sources sections keep full `crates/oxide-code/src/...` paths, sorted
alphabetically.** Full paths are clickable in editors and unambiguous
about what level of the tree we're naming. Line numbers stay out so the
entries don't rot under refactors. Annotations stay one short clause.
- **Inline body references switch to backticks or concept-level
cross-doc links.** `[X](../../../crates/.../x.rs)` in prose rotted on
every file split. Backticked names like `` `SearchableList` `` and
`[modals.md](modals.md)` survive renames.
- **Trait-shape claims described semantically rather than as literal
Rust.** The `modals.md` `ModalKey` block was already stale (the
`Preview` variant added in PR #67 was missing). Rewriting it as four
named outcomes with prose stays correct across future variant additions.
- **Research docs keep their implementation depth.** Research docs
document external systems (Claude Code, Codex, opencode, Anthropic API)
and earn their detail. The sweep there is wording consistency only —
antithesis trims, em-dash chain splits, stale counts.
- **"X, not Y" rewritten in the body, sometimes preserved in titles.**
Decision titles where the contrast IS the load-bearing rationale
(`/resume`'s `roll_into` vs. process replacement; the `cch` body field
vs. beta header) keep the antithesis. Body prose loses it: `xxh64, not
SHA-256` becomes `xxh64 for change detection`, etc.
- **Prose connectors over em-dashes and period fragmentation.** Em-dash
is reserved for true parenthetical asides, not for stitching two
independent clauses together. When trimming em-dash / semicolon overuse,
transitions like `since`, `because`, `while`, and `where` carry the
rewrite — defaulting to a period creates staccato fragmentation that
reads worse than the original.

## Changes

| File | Description |
| ---- | ----------- |
| `docs/design/slash/commands.md` | "Nine built-ins" → eleven; `/rename`
and `/resume` added to the inline list and Per-Command notes; antithesis
decision titles softened; Sources alphabetized with full paths. |
| `docs/design/slash/modals.md` | `ModalKey` rewritten semantically
(now-stale 3-of-4 Rust block dropped); Per-Modal notes added for
`/rename` editor and `/resume` picker; Decisions 1, 7, 8 rewritten
without antithesis; em-dash chains turned into transition-word
sentences; Sources alphabetized. |
| `docs/design/slash/resume.md` | `SearchableList` / `SessionRow`
descriptions updated for the multi-line render and current field set
(`message_count`, `git_branch`, `project`); decision titles softened;
full repo paths in body links replaced with concept-level pointers;
Sources alphabetized. |
| `docs/design/session/file-tracking.md` | "xxh64, not SHA-256"
rewritten in positive form; Sources alphabetized. |
| `docs/design/session/persistence.md` | Em-dash chains in
actor-batching and resume-sanitization paragraphs converted to
conjunction-joined sentences; `WriterStatus::Pending` updated to mention
the deferred-title field added in PR #72. |
| `docs/design/tools/truncation.md` | `TRUNCATION_OVERHEAD` constant
`50` → `80` (matches `tool.rs`); "Head-tail, not tail-only" rewritten in
positive form; Sources alphabetized. |
| `docs/design/tui/cancellation.md` | Status hints `Streaming . Esc` /
`Running {tool} . Esc` → middot `·` matching `status.rs`; Decision 6's
antithesis rewritten; em-dash-as-connector cases swapped for `since` /
`because` clauses; Sources alphabetized. |
| `docs/design/tui/overview.md` | Dropped fictional `trait Component`
pseudocode (no such trait exists); "11 named color slots" replaced with
a slot-family description (the actual count is 30+ accessors); the
staccato streaming-markdown paragraph combined into one cohesive
sentence. |
| `docs/design/tui/welcome.md` | "9 entries" → 11 (full registry size);
"8-entry STARTER_POOL / TIP_POOL" → 9-entry; antithesis decision titles
softened; em-dash chain in the live-feeds Out-of-Scope item turned into
a conjunction; Sources alphabetized. |
| `docs/guide/configuration.md` | Default `model` cell `claude-opus-4-7`
→ `claude-opus-4-7[1m]` (matches `DEFAULT_MODEL`) in both the `[client]`
table and the env-var table; "opt-in rather than automatic" rewritten as
"you have to opt in explicitly"; OAuth-paragraph "matches Claude Code"
implementation leak trimmed. |
| `docs/guide/instructions.md` | "More specific locations override
broader ones" softened to "files closer to your working directory appear
later in the prompt and conventionally take precedence", which matches
the actual concatenate-in-walk-order behaviour. |
| `docs/guide/sessions.md` | Mid-session resume description loses the
implementation-internal "load + sanitize pipeline" phrase; `/rename`
interaction with the AI title generator added to the Titles section. |
| `docs/guide/slash-commands.md` | Theme bullet's comma-spliced
enumeration restructured; `/resume` description's em-dash run split;
persistence-stance em-dash dropped. |
| `docs/guide/theming.md` | `Color::Reset` (internal Rust type) →
user-facing `reset`; relative-paths antithesis sentence rephrased; "same
routing applies in both modes" empty restatement dropped. |
| `docs/research/api/anthropic.md` | Per-model beta-set body trimmed of
one staircase-narration sentence; em-dash chain on first-party-vs-3P
fingerprint paragraph rewritten with conjunctions; "billing plumbing,
not a security boundary" reworded; `prompt-caching-scope` paragraph
reflows. |
| `docs/research/api/extended-thinking.md` | `signature_delta` line
restored to a clean parenthetical; credential-rotation em-dash chain
split. |
| `docs/research/api/system-prompt.md` | "absent → default (org-scoped)
ephemeral cache. Universally accepted" tightened; org-default
rationale's three-clause stack rewritten as a colon-introduced list. |
| `docs/research/slash/commands.md` | Comparison table `oxide-code`
`Variants: 9` → `11`. |
| `docs/roadmap.md` | "and bash output" dropped from rich-tool-views
(bash uses the fallback view); silent-merge "Rejects Claude Code's …"
parenthetical inlined; status-bar follow-up sentence flow tightened. |

## Test plan

- [x] `pnpm spellcheck` — clean
- [x] `pnpm lint` — clean
- [x] All Sources entries verified to exist under
`crates/oxide-code/src/`
- [x] All count-bearing claims verified against source (`BUILT_INS =
11`, `STARTER_POOL.len() = 9`, `TIP_POOL.len() = 9`,
`TRUNCATION_OVERHEAD = 80`, `DEFAULT_MODEL = "claude-opus-4-7[1m]"`)
- [x] All `[link](path)` cross-doc references resolve
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant