Compare commits

..

3 Commits

Author SHA1 Message Date
5ee266a4d9 feat(auto_name): propagate first chat name to parent session
When a chat is auto-named, also rename the parent session if it is
still on its default 'New session' label. UPDATE is gated by an
atomic WHERE clause so user renames and prior propagations are not
clobbered. Publishes session_renamed via broker.publishUser; useSidebar
already listens.

Closes the gap where sessions auto-created from the sidebar would
stay 'New session' forever.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:23:11 +00:00
bbf9fac936 docs(roadmap): reconcile post-v1.6.1 + v1.6.2 in-flight
Update version summary: v1.6-mobile-pass and v1.6.1-cleanup are now
merged with SHAs; v1.6.2-mobile-ui-fixes added as in-flight with its
4-commit plan. v1.6.1-cleanup details rewritten to reflect what
actually shipped (B1) vs what was audited-only (secrets, panes,
unused exports, hand-rolled patterns, mount scope, etc.).

Closed two open items: session_renamed has a server publisher since
v1.4; PATCH /api/panes/:id is moot (endpoint never re-introduced).
Dependency graph updated with v1.6.2 node between v1.6.1 and v1.7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 06:37:27 +00:00
6fa6eb7f32 feat(inference): raise MAX_TOOL_LOOP_DEPTH from 5 to 15
Allows assistant turns up to 15 tool calls in a single chain before
the loop-depth guard trips. Real chats commonly need 6-10 tool calls
(grep -> view_file -> view_file -> grep -> view_file -> answer); the
old cap of 5 was firing on legitimate investigation patterns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 06:37:27 +00:00
3 changed files with 74 additions and 27 deletions

View File

@@ -144,4 +144,23 @@ export async function maybeAutoNameChat(
updated_at: updated[0]!.updated_at, updated_at: updated[0]!.updated_at,
}); });
ctx.log.info({ chatId, name }, 'chat auto-named'); ctx.log.info({ chatId, name }, 'chat auto-named');
// Propagate to the parent session if it's still on its default name.
// The WHERE guard makes the check atomic — if the user has already
// renamed (or a prior chat already propagated), this UPDATE matches
// zero rows and we do nothing. First chat wins; manual renames win.
const renamedSession = await ctx.sql<{ id: string; name: string }[]>`
UPDATE sessions
SET name = ${name}
WHERE id = ${sessionId} AND name = 'New session'
RETURNING id, name
`;
if (renamedSession.length > 0) {
ctx.publishUser({
type: 'session_renamed',
session_id: sessionId,
name,
});
ctx.log.info({ sessionId, name }, 'session auto-named from chat');
}
} }

View File

@@ -10,7 +10,7 @@ const BASE_SYSTEM_PROMPT = (projectPath: string) =>
`You are BooCode Chat, a code investigation assistant. The user is working on a project located at ${projectPath}. Use the file-read tools (view_file, list_dir, grep, find_files) to investigate code when needed. Be concise. Cite file paths and line numbers when discussing code. Do not hallucinate file contents — read the file first. Tool results may be truncated; if so, narrow your query rather than guessing.`; `You are BooCode Chat, a code investigation assistant. The user is working on a project located at ${projectPath}. Use the file-read tools (view_file, list_dir, grep, find_files) to investigate code when needed. Be concise. Cite file paths and line numbers when discussing code. Do not hallucinate file contents — read the file first. Tool results may be truncated; if so, narrow your query rather than guessing.`;
const DB_FLUSH_INTERVAL_MS = 500; const DB_FLUSH_INTERVAL_MS = 500;
const MAX_TOOL_LOOP_DEPTH = 5; const MAX_TOOL_LOOP_DEPTH = 15;
export interface InferenceFrame { export interface InferenceFrame {
type: type:

View File

@@ -27,8 +27,9 @@ Live at `https://code.indifferentketchup.com` (Caddy → Authelia → Tailscale
|v1.4 |Fork from message + delete message + header polish + housekeeping |✅ Merged |Was original “Batch 5” | |v1.4 |Fork from message + delete message + header polish + housekeeping |✅ Merged |Was original “Batch 5” |
|v1.5 |Refactor splits, vitest harness (23 tests), error-log surfacing, `/opt:ro` + `BOOTSTRAP_ROOT`, persistent context-window tracker |✅ Merged |— | |v1.5 |Refactor splits, vitest harness (23 tests), error-log surfacing, `/opt:ro` + `BOOTSTRAP_ROOT`, persistent context-window tracker |✅ Merged |— |
|v1.5.1 |Bootstrap hotfix: git in container, SSH keypair, known_hosts, SSH URL rewrite, /opt/projects label |✅ Merged |`4a9f207` | |v1.5.1 |Bootstrap hotfix: git in container, SSH keypair, known_hosts, SSH URL rewrite, /opt/projects label |✅ Merged |`4a9f207` |
|v1.6-mobile-pass|Mobile pass: drawer, pane stacking, long-press, swipe-to-close, pull-to-refresh, IME safety, safe-area, tap targets + H1 path-guard fix|🔄 Hand-back received, uncommitted|Was original “Batch 4” | |v1.6-mobile-pass|Mobile pass: drawer, pane stacking, long-press, swipe-to-close, pull-to-refresh, IME safety, safe-area, tap targets + H1 path-guard fix|✅ Merged |`57c883b..943ae7d` (6 commits) |
|v1.6.1-cleanup |Stale code audit, overengineering audit, secrets hygiene, RightRail mobile fix |Planned (next) |— | |v1.6.1-cleanup |Mostly audit-only; one fix shipped: RightRail `max-md:hidden` wrapper. Audit reports for secrets, stale code, panes, mount scope, hand-rolled patterns deferred to follow-ups |✅ Merged |`6a9fe18` |
|v1.6.2-mobile-ui-fixes|Mobile UI polish from device testing: kill single-pane navigator chrome, header rework, “New chat” in long-press menu, RightRail as mobile drawer (reverts v1.6.1 wrapper) |🔄 Hand-back received, uncommitted|— |
|v1.7 |Drag-drop + paste-as-attachment (chip infra extension) |Planned |Was Batch 6 | |v1.7 |Drag-drop + paste-as-attachment (chip infra extension) |Planned |Was Batch 6 |
|v1.8 |Settings drawer (system prompt per project + session, web search toggle) |Planned |Was Batch 7 | |v1.8 |Settings drawer (system prompt per project + session, web search toggle) |Planned |Was Batch 7 |
|v1.9 |Web search backend: SearXNG `web_search` + `web_fetch` tools |Planned |Was Batch 8 | |v1.9 |Web search backend: SearXNG `web_search` + `web_fetch` tools |Planned |Was Batch 8 |
@@ -139,15 +140,16 @@ Dockerfile (git installed in container), docker-compose.yml, project_bootstrap.t
----- -----
### v1.6-mobile-pass 🔄 ### v1.6-mobile-pass
**Hand-back received, uncommitted on `v1.6-mobile-pass`.** 5-commit sequence proposed: **Merged via 6 commits `57c883b..943ae7d`** (5 functional + 1 docs):
1. `chore: fix resolveProjectPath whitelist-root bypass` (H1 — dropped `real !== whitelistReal` short-circuit; 23/23 pass). 1. `57c883b chore: fix resolveProjectPath whitelist-root bypass` (H1 — dropped `real !== whitelistReal` short-circuit; flipped the v1.5 BEHAVIOR GAP test; 23/23 pass).
1. `feat(mobile): viewport hook + sidebar drawer + hamburger headers` (M1 + M2 + M6-header). 1. `a643b5f feat(mobile): viewport hook + sidebar drawer + hamburger headers` (M1 + M2 + M6-header).
1. `feat(mobile): single-pane stack + long-press tab menu + swipe-to-close` (M3 + M4 + A2). 1. `cd897d6 feat(mobile): single-pane stack + long-press tab menu + swipe-to-close` (M3 + M4 + A2).
1. `feat(mobile): chat input keybinds + safe-area + tap targets + overflow safety` (M5 + M6-bottom + M7 + M8). 1. `273eeac feat(mobile): chat input keybinds + safe-area + tap targets + overflow safety` (M5 + M6-bottom + M7 + M8).
1. `feat(mobile): pull-to-refresh sidebar list` (A1). 1. `4b5b9b2 feat(mobile): pull-to-refresh sidebar list` (A1).
1. `943ae7d docs: add v1.x roadmap snapshot` (this file).
**Decisions:** **Decisions:**
@@ -168,21 +170,41 @@ Dockerfile (git installed in container), docker-compose.yml, project_bootstrap.t
----- -----
### v1.6.1-cleanup — Stale + overengineering audit + secrets hygiene (next) ### v1.6.1-cleanup ✅ (`6a9fe18`)
**Depends on:** v1.6 committed. **Shipped:** RightRail wrapped in `<div className="max-md:hidden contents">` so it's hidden entirely below the md breakpoint on mobile. (Note: v1.6.2 reverses this and replaces with a proper mobile drawer — see below.)
**Scope:** **Audited but not shipped (queued for follow-ups):**
1. RightRail mobile fix (`max-md:hidden` on outer container). - **Secrets hygiene:** `secrets/boocode_gitea` is NOT tracked; never committed to any branch; `.gitignore` already covers `secrets/`. Rotation is a Gitea-side action, no repo change needed.
1. Secrets audit: rotate `secrets/boocode_gitea`, confirm `.gitignore` covers `secrets/`, scan git history (`git log --all -- secrets/`), `git filter-repo` or BFG if exposed in history, force-push if rewriting. - **`.bak` files:** 3 leftover from v1.5.1 (`docker-compose.yml.bak-20260516`, `Dockerfile.bak-20260516`, `apps/web/src/components/CreateProjectModal.tsx.bak-20260516`). Git-invisible via global `~/.gitignore_global` (`*.bak*`). Decide per file.
1. Fix agent SSH key path so future Claude Code dispatches dont fall back to in-repo keys. - **Unused exports:** neither `knip` nor `ts-prune` installed. Proposal pending.
1. Stale code audit: pruning unused exports, dead WS frames (e.g. `session_renamed` server publisher TODO from Batch 1), backup `.bak` files, unused imports. - **Dead WS frames:** `session_renamed` HAS a server publisher (`routes/sessions.ts:140`, added in v1.4) — the roadmap's "no server publisher" open item is **STALE**, crossed off. The `InferenceFrame` union still declares `session_renamed` as a type variant but no code publishes it on the per-session channel; trivial 1-line cleanup deferred.
1. Overengineering audit: places where hand-rolled patterns are more complex than necessary, places where singleton hooks should consolidate (`useSessionStream` refcount). - **Unused imports:** web `tsc --noUnusedLocals --noUnusedParameters` returns 0 warnings.
1. PATCH `/api/panes/:id` session-ownership check tightening. - **`useSessionStream` refcount:** opportunity confirmed (~90 lines diff to apply the `useSidebar`-style module-scope singleton pattern). Risk LOW. Queued for v1.6.2 or later.
1. `/opt:/opt:ro` mount whitelist tightening (precursor to BooCoder). - **PATCH `/api/panes/:id` ownership:** **MOOT** — endpoint does not exist (the pane REST API was never re-introduced after pane state moved to client-side localStorage in v1.2). Crossed off open items.
- **Hand-rolled patterns vs library:** 5 hand-rolled hooks/components total 336 lines. None duplicates anything in existing deps; library swap (`@use-gesture`, `react-pull-to-refresh`) not worth the dep cost yet.
- **`/opt:/opt:ro` mount tightening:** Two-option plan documented for v1.6.2 — Option A (per-project bind-mounts) or Option B (deny `.env` pattern in `pathGuard`). Option B is the simpler short-term fix.
**No new features. No schema changes.** -----
### v1.6.2-mobile-ui-fixes 🔄
**Hand-back received, uncommitted on `v1.6.2-mobile-ui-fixes`.** 4-commit sequence proposed:
1. `fix(mobile): hide Split button + single-pane navigator chrome` (G1 — wrap the Workspace Split row in `!isMobile`).
1. `feat(mobile): rework Session and Project headers for narrow viewports` (G2 — breadcrumb `hidden sm:flex`, session name cap `max-w-[140px] sm:max-w-[280px]`, project page heading `text-base sm:text-lg`, “New session” icon-only on mobile).
1. `feat(mobile): add "New chat" to tab long-press context menu` (G3 — top of menu, separator, then existing items).
1. `feat(mobile): right-rail as drawer on mobile, header toggle button` (G4 option b — new `useRightRailDrawer` Context hook, `RightRail` renders as fixed `w-[85vw] max-w-sm` drawer on mobile, FolderTree button in Session header, **reverts v1.6.1's `max-md:hidden` wrapper**).
**Decisions:**
- G4 option b chosen: mobile file browsing IS useful; drawer pattern mirrors `useSidebarDrawer`.
- G2 single-row session-name+model layout (model picker right-aligned), per spec example.
- G3 "New chat" at top, separator, then Rename.
- G2 "New session" button: icon-only on mobile via `<span className="hidden sm:inline">New session</span>`.
**Adjacent uncommitted change (not part of v1.6.2):** `MAX_TOOL_LOOP_DEPTH 5 → 15` in `apps/server/src/services/inference.ts`. Sam-authored, sitting in working tree on `v1.6.2-mobile-ui-fixes`. **NOT on main as of this update.** Commit separately.
----- -----
@@ -407,14 +429,17 @@ settings
## Known open items ## Known open items
- **`useSessionStream` refcount.** Two ChatPanes = two WS. Apply singleton pattern. Tracked in v1.6.1. - **`useSessionStream` refcount.** Two ChatPanes = two WS. Apply singleton pattern. Audited in v1.6.1, queued.
- **PATCH `/api/panes/:id` lacks session-ownership check.** Single-user fine; tighten in v1.6.1. - **`/opt:/opt:ro` mount exposes all `.env` files.** Whitelist scope before BooCoder. Two-option plan documented in v1.6.1 audit; ship in v1.6.2 or v1.7.
- **`/opt:/opt:ro` mount exposes all `.env` files.** Whitelist scope before BooCoder. Tracked in v1.6.1. - **`secrets/boocode_gitea` in repo working tree.** Never committed (git-invisible via global ignore). Rotate the Gitea-side key when convenient; no repo action required.
- **`session_renamed` no server WS publisher.** Carried from Batch 2. Tracked in v1.6.1.
- **`secrets/boocode_gitea` in repo.** v1.5.1 dispatch fallback. Rotation + history scrub in v1.6.1.
- **Dormant in-boolab BooCode mode.** Reference only. - **Dormant in-boolab BooCode mode.** Reference only.
- **BooCoder container.** Post-v1.x. - **BooCoder container.** Post-v1.x.
**Closed since last update:**
- ~~`session_renamed` no server WS publisher~~ — server publishes via `broker.publishUser` from `routes/sessions.ts:140` (added in v1.4). Confirmed in v1.6.1 audit.
- ~~PATCH `/api/panes/:id` lacks session-ownership check~~ — endpoint does not exist; the pane REST API was never re-introduced after v1.2 moved pane state to localStorage.
----- -----
## Dependency graph ## Dependency graph
@@ -456,7 +481,10 @@ v1.5.1 (bootstrap hotfix) │
v1.6-mobile-pass │ v1.6-mobile-pass │
│ │ │ │
▼ │ ▼ │
v1.6.1-cleanup ◄─────────────┘ v1.6.1-cleanup
│ │
▼ │
v1.6.2-mobile-ui-fixes ◄─────┘
v1.7 (drag-drop) ◄── v1.1-batch3.5 v1.7 (drag-drop) ◄── v1.1-batch3.5